#ifndef __HH_REACTION_H #define __HH_REACTION_H // // hhReactionTarget // class hhReactionTarget { public: hhReactionTarget() { weight = 1.0f; ent = NULL; } hhReactionTarget(idEntity *e, float w = 1.0f) { ent = e; weight = w; } idEntityPtr ent; // The entity that is effected float weight; // The degree ent is effected }; // // hhReactionVolume // class hhReactionVolume : public idEntity { public: CLASS_PROTOTYPE( hhReactionVolume ); enum { flagPlayers = (1<<0), flagMonsters = (1<<1), flagActors = (flagPlayers|flagMonsters), }; void Spawn(void); // Gets all of the entities (filtered by validFlags) that are inside this volume void GetValidEntitiesInside(idList &outValidEnts); bool IsValidEntity( idEntity *ent ); void Save(idSaveGame *savefile) const; void Restore(idRestoreGame *savefile); protected: int flags; // Flags for this volume }; // // hhReactionDesc // // Holds the unchanging properties of a reaction // class hhReactionDesc { public: friend class hhReactionHandler; hhReactionDesc() { name = idStr(""); effect = Effect_Invalid; cause = Cause_Invalid; flags = 0; effectRadius = -1.0f; effectMinRadius = -1.0f; effectVolumes.SetGranularity(1); key = idStr(""); keyVal = idStr(""); anim = idStr(""); listenerRadius = -1.0f; listenerMinRadius = -1.0f; listenerVolumes.SetGranularity(1); } enum Cause { Cause_Invalid = -1, Cause_Damage, // AI can damage this to get reaction Cause_Touch, // AI can touch this to get reaction Cause_Use, // AI can 'use' this to get reaction Cause_None, // AI does nothing, and the effect is always felt Cause_PlayAnim, // AI must play an anim to get the desired effect Cause_Telepathic_Trigger, // AI must use their telepathy to trigger this entity Cause_Telepathic_Throw, // AI must use their telepathy to physically throw this entity Cause_Total }; enum Effect { Effect_Invalid = -1, Effect_Damage, // Causes damage (reduces health) Effect_DamageEnemy, // Causes damage (reduces health) Effect_VehicleDock, // Causes damage (reduces health) Effect_Vehicle, // Causes damage (reduces health) Effect_Heal, // Heals entity (increases health) Effect_HaveFun, // Something 'fun' for this entity to do Effect_ProvideCover, // Indicates this entity can possibily provide 'cover' for a monster Effect_BroadcastTargetMsgs, // This entity will have its target's msg's sent Effect_Climb, // This entity will let the monster climb it. Effect_Passageway, // This entity is a harvester passageway Effect_CallBackup, // Calls for help Effect_Total }; enum { flag_EffectAllPlayers = (1<<0), // This entity automatically targets ALL entities of type hhPlayer flag_EffectAllMonsters = (1<<1), // This entity automatically targets ALL entities of typehhAI flag_Exclusive = (1<<2), // This msg is exclusive, and once a monster has committed to it - NO one else should listen to it flag_EffectListener = (1<<3), // The effect of this reaction is applied to the listener flag_AnimFaceCauseDir = (1<<4), // When playing an anim for this reaction, face the direction of our cause flag_AnimTriggerCause = (1<<5), // When playing an anim for this reaction, trigger the cause entity flag_SnapToPoint = (1<<6), // This msg is only for monsters that are telepathic flagReq_MeleeAttack = (1<<8), // This msg is for monsters that can melee attack flagReq_RangeAttack = (1<<9), // This msg is for monsters that can range attack flagReq_Anim = (1<<10), // This msg is for monsters that have a given anim (anim) flagReq_KeyValue = (1<<11), // This msg is for monsters that have a given key set (key, keyVal) flagReq_NoVehicle = (1<<12), // This msg is for monsters that are NOT in vehicles flagReq_CanSee = (1<<13), // This msg is only for monsters that can see the cause entity flagReq_Telepathic = (1<<14), // This msg is only for monsters that are telepathic }; static idStr CauseToStr(Cause c); // Converts activate enum into readable form static idStr EffectToStr(Effect e); // Converts reaction enum to readable form static Cause StrToCause(const char* str); // Converts string to caused by enum or Caused_Invalid if not found static Effect StrToEffect(const char* str); // Converts string to effect enum or Effect_Invalid if not found void Set(const idDict &keys); // Sets this description to the given key values bool CauseRequiresPathfinding(void) const {return cause == Cause_Touch || cause == Cause_Use || cause == Cause_PlayAnim;} void Save( idSaveGame *savefile ) const; void Restore( idRestoreGame *savefile ); idStr name; // unique name for each reaction desc Effect effect; // pdm: for reaction system Cause cause; // pdm: for reaction system unsigned int flags; // jrm: reaction flags for reaction system float effectRadius; // jrm: If > 0.0f then ONLY target entities that are at most this far away float effectMinRadius; // jrm: if > 0.0f then ONLY target entities that are at least this far away idList effectVolumes; // jrm: if valid, then only entities that are inside one of these volumes are valid 'effect' entities idStr key; // jrm: the key required for a monster to be eligable for this reaction msg (ReactionFlag_KeyReq must be set) idStr keyVal; // jrm: the value of the reaction key required idStr anim; // jrm: the value of the reaction key required float listenerRadius; // jrm: listeners farther away than this distance will not receive this reaction float listenerMinRadius; // jrm: listeners closer than this dist will not receive this reaction idList listenerVolumes; // jrm: only listeners that are inside one of these volumes will receive this reaction idDict touchOffsets; // Example: Use "monster_hunter" as key to get touchOffset values idStr touchDir; // Indicates how a monster should approach the cause entity idStr finish_key; // mdc: used to set a key on monster after finishing a reaction idStr finish_val; // mdc: used to set a key on monster after finishing a reaction }; // // hhReaction // // Describes a reaction ready to be processed by the AI. Holds a description of the reaction, as well as the cause/effect entities. // Do the cause describe by to to get the effect in applied to // class hhReaction { public: hhReaction() { desc = NULL; causeEntity = NULL; active = TRUE; exclusiveOwner = NULL; exclusiveUntil = -1; effectEntity.Clear(); }; hhReaction(const hhReactionDesc *reactDesc, idEntity *cause, const hhReactionTarget &effect) { active = TRUE; desc = reactDesc; causeEntity = cause; exclusiveUntil = -1; exclusiveOwner = NULL; effectEntity.Append(effect); } bool IsActive() const {return active;} bool IsActiveForListener(idEntity *listener) const { if(!active) { return FALSE; } // If someone has claimed this, and their claim is still valid... Only active for that particular AI if(exclusiveOwner.GetEntity() && gameLocal.GetTime() < exclusiveUntil) { return exclusiveOwner.GetEntity() == listener; } // No one has claimed us, or time has expired, so anyone can claim us now return TRUE; }; bool ClaimExclusivity(idEntity *claimer, int ms=1000) { // Cannot claim, someone else already has it if(exclusiveOwner.GetEntity() && gameLocal.GetTime() < exclusiveUntil) { return FALSE; } // They claimed it now.... exclusiveOwner = claimer; exclusiveUntil = gameLocal.GetTime() + ms; return TRUE; } void Save(idSaveGame *savefile) const; void Restore(idRestoreGame *savefile); bool active; // TRUE to be considered for sending idEntityPtr exclusiveOwner; // The AI that has 'claimed' this reaction int exclusiveUntil; // Until gameLocal.time has reached this value, this reaction does not send msgs (except for exclusive reactions) const hhReactionDesc* desc; // Info about the kind of reaction this is idEntityPtr causeEntity; // The entity to apply the cause to idList effectEntity; // The effect of doing the cause is felt by these entities }; // // hhReactionHandler // // Manages all of the reaction descs currently loaded // class hhReactionHandler { public: hhReactionHandler(); virtual ~hhReactionHandler(); void Save( idSaveGame *savefile ) const; void Restore( idRestoreGame *savefile ); // Loads the given reaction def name. If uniqueDescKeys are specified, a unique hhReactionDesc* is guarenteed to be returned. // Otherwise, if no override keys are specified and a reaction of the given name is found, it is returned without creating a new one // The returned desc is ONLY a desc, it is not meant to be modified in any way const hhReactionDesc* LoadReactionDesc(const char* defName, const idDict &uniqueDescKeys) { if(uniqueDescKeys.GetNumKeyVals() <= 0) { return LoadReactionDesc(defName, NULL); } else { return LoadReactionDesc(defName, &uniqueDescKeys); } } const hhReactionDesc* LoadReactionDesc(const char* defName) {return LoadReactionDesc(defName, NULL);} protected: friend class hhReaction; // Creates a new reaction desc with the given name and keys to describe the reaction hhReactionDesc* CreateReactionDesc(const char *name, const idDict &keys); idStr GetUniqueReactionDescName(const char *name); const hhReactionDesc* FindReactionDesc(const char *name); void StripReactionPrefix(idDict &dict); // Removes any "reaction_" or "reaction1_" prefix from the given dict const hhReactionDesc* LoadReactionDesc(const char* defName, const idDict *uniqueDescKeys); idList reactionDescs; }; #endif