//----------------------------------------------------------------------------- // // $Logfile:: /Code/DLLs/game/entity.cpp $ // $Revision:: 271 $ // $Author:: Steven $ // $Date:: 10/13/03 9:43a $ // // Copyright (C) 2002 by Ritual Entertainment, Inc. // All rights reserved. // // This source is may not be distributed and/or modified without // expressly written permission by Ritual Entertainment, Inc. // // // DESCRIPTION: // Base class for all enities that are controlled by the game. If you have any // object that should be called on a periodic basis and it is not an entity, // then you have to have an dummy entity that calls it. // // An entity in the game is any object that is not part of the world. Any non-world // object that is visible in the game is an entity, although it is not required that // all entities be visible to the player. Some objects are basically just virtual // constructs that act as an instigator of certain actions, for example, some // triggers are invisible and cannot be touched, but when activated by other // objects can cause things to happen. // // All entities are capable of receiving messages from the game or from other entities. // Messages received by an entity may be ignored, passed on to their superclass, // or acted upon by the entity itself. The programmer must decide on the proper // action for the entity to take to any message. There will be many messages // that are completely irrelevant to an entity and should be ignored. Some messages // may require certain states to exist and if they are received by an entity when // it these states don't exist may indicate a logic error on the part of the // programmer or map designer and should be reported as warnings (if the problem is // not severe enough for the game to be halted) or as errors (if the problem should // not be ignored at any cost). // #include "_pch_cpp.h" //#include "g_local.h" #include "entity.h" #include "worldspawn.h" #include "scriptmaster.h" #include "sentient.h" #include "specialfx.h" #include "misc.h" #include "object.h" #include "player.h" #include "weaputils.h" #include "soundman.h" #include "earthquake.h" #include #include "mp_manager.hpp" #include // Player events Event EV_ClientMove ( "client_move", EV_DEFAULT, NULL, NULL, "The movement packet from the client is processed by this event." ); Event EV_ClientEndFrame ( "client_endframe", EV_DEFAULT, NULL, NULL, "Called at the end of each frame for each client." ); // Generic entity events Event EV_Classname ( "classname" , EV_TIKIONLY, "s", "nameOfClass", "Determines what class to use for this entity,\n" "this is pre-processed from the BSP at the start\n" "of the level." ); Event EV_SpawnFlags ( "spawnflags", EV_CODEONLY, "i", "flags", "spawnflags from the BSP, these are set inside the editor" ); Event EV_SetTeam ( "team", EV_DEFAULT, "s", "moveTeam", "used to make multiple entities move together." ); Event EV_Trigger ( "trigger", EV_SCRIPTONLY, "s", "name", "Trigger the specified target or entity." ); Event EV_Activate ( "doActivate", EV_DEFAULT, "e", "activatingEntity", "General trigger event for all entities" ); Event EV_Use ( "doUse", EV_DEFAULT, "e", "activatingEntity", "sent to entity when it is used by another entity" ); Event EV_FadeNoRemove ( "fade", EV_DEFAULT, "F[0,]F[0,1]", "fadetime target_alpha", "Fade the entity's alpha, reducing it by 0.03\n" "every FRAMETIME, until it has faded out, does not remove the entity" ); Event EV_FadeOut ( "_fadeout", EV_CODEONLY, NULL, NULL, "Fade the entity's alpha and scale out, reducing it by 0.03\n" "every FRAMETIME, until it has faded out, removes the entity\n" "Once the entity has been completely faded, the entity is removed." ); Event EV_Fade ( "fadeout", EV_DEFAULT, "F[0,]F[0,1]", "fadetime target_alpha", "Fade the entity's alpha and scale out, reducing it by 0.03\n" "every FRAMETIME, until it has faded out. If fadetime or\n" "target_alpha are defined, they will override the defaults.\n" "Once the entity has been completely faded, the entity is removed." ); Event EV_FadeIn ( "fadein", EV_DEFAULT, "F[0,]F[0,1]", "fadetime target_alpha", "Fade the entity's alpha and scale in, increasing it by 0.03\n" "every FRAMETIME, until it has faded completely in to 1.0.\n" "If fadetime or target_alpha are defined, they will override\n" "the default values." ); Event EV_Killed ( "killed", EV_DEFAULT, "efei", "attacker damage inflictor meansofdeath", "event which is sent to an entity once it as been killed" ); Event EV_GotKill ( "gotkill" , EV_DEFAULT, "eieib", "victim damage inflictor meansofdeath gib", "event sent to attacker when an entity dies" ); Event EV_Pain ( "pain", EV_DEFAULT, "iei", "damage attacker meansofdeath", "used to inflict pain to an entity" ); Event EV_Damage ( "_damage", EV_CODEONLY, "feevvviii", "damage inflictor attacker position direction normal knockback damageflags meansofdeath", "general damage event used by all entities" ); Event EV_Stun ( "_stun", EV_CODEONLY, "f", "time", "Stun this entity for the specified time" ); Event EV_Kill ( "kill", EV_CONSOLE, NULL, NULL, "console based command to kill yourself if stuck." ); Event EV_Gib ( "gib", EV_DEFAULT, "iIFS", "number power scale gibmodel", "causes entity to spawn a number of gibs" ); Event EV_Hurt ( "hurt", EV_DEFAULT, "fSV", "damage means_of_death direction", "Inflicts damage if the entity is damageable. If the number of damage\n" "points specified in the command argument is greater or equal than the\n" "entity's current health, it will be killed or destroyed." ); Event EV_TakeDamage ( "takedamage", EV_DEFAULT, NULL, NULL, "makes entity take damage." ); Event EV_NoDamage ( "nodamage", EV_DEFAULT, NULL, NULL, "entity does not take damage." ); Event EV_Stationary ( "stationary", EV_DEFAULT, NULL, NULL, "entity does not move, causes no physics to be run on it." ); // Physics events Event EV_MoveDone ( "movedone", EV_DEFAULT, "e", "finishedEntity", "Sent to commanding thread when done with move ." ); Event EV_Touch ( "doTouch", EV_DEFAULT, "e", "touchingEntity", "sent to entity when touched for the first time." ); Event EV_Contact ( "inContactWithEntity", EV_DEFAULT, "e", "entityWrapper", "sent to an entity that is in contact with specified entity." ); Event EV_LostContact ( "lostContactWithEntity", EV_DEFAULT, "e", "entityNoLongerInContact", "sent to an entity no longer in contact with specified entity." ); Event EV_Blocked ( "doBlocked", EV_DEFAULT, "e", "obstacle", "sent to entity when blocked." ); Event EV_UseBoundingBox ( "usebbox", EV_DEFAULT, NULL, NULL, "do not perform perfect collision, use bounding box instead." ); Event EV_Gravity ( "gravity", EV_DEFAULT, "f", "gravityValue", "Change the gravity on this entity" ); Event EV_Stop ( "stopped", EV_DEFAULT, NULL, NULL, "sent when entity has stopped bouncing for MOVETYPE_TOSS." ); Event EV_SetFullTrace ( "fulltrace", EV_DEFAULT, "b", "on_or_off", "Turns fulltrace physics movement on or off for this entity." ); Event EV_ProcessInitCommands ( "processinit", EV_CODEONLY, "i", "modelIndex", "process the init section of this entity, this is an internal command,\n" "it is not meant to be called from script." ); Event EV_Attach ( "attach", EV_DEFAULT, "esIVV", "parent tagname use_angles offset angles_offset", "attach this entity to the parent's legs tag called tagname" ); Event EV_AttachModel ( "attachmodel", EV_DEFAULT, "ssFSBFFFFVV", "modelname tagname scale targetname detach_at_death removetime fadeintime fadeoutdelay fadetime offset angles_offset", "attach a entity with modelname to this entity to tag called tagname.\n" "scale - scale of attached entities\n" "targetname - targetname for attached entities\n" "detach_at_death - when entity dies, should this model be detached.\n" "removetime - when the entity should be removed, if not specified, never.\n" "fadeintime - time to fade the model in over.\n" "fadeoutdelay - time to wait until we fade the attached model out\n" "fadeoutspeed - time the model fades out over\n" "offset - vector offset for the model from the specified tag\n" "angles_offset - angles offset for the model from the specified tag" ); Event EV_RemoveAttachedModel ( "removeattachedmodel", EV_DEFAULT, "sFS", "tagname fadetime modelname", "Removes the model attached to this entity at the specified tag." ); Event EV_RemoveAttachedModelByTargetname ( "removeAttachedModelByTargetname", EV_DEFAULT, "s", "targetname", "Removes all models that are attached to this entity with the specified targetname." ); Event EV_Detach ( "detach", EV_SCRIPTONLY, NULL, NULL, "detach this entity from its parent." ); Event EV_IncreaseShotCount ( "increaseshotcount", EV_DEFAULT, NULL, NULL, "boosts the shotsFired on this entity's parent if it is an actor" ); // script stuff Event EV_Model ( "model", EV_DEFAULT, "s", "modelName", "set the model to modelName." ); Event EV_Hide ( "hide", EV_DEFAULT, NULL, NULL, "hide the entity, opposite of show." ); Event EV_Show ( "show", EV_DEFAULT, NULL, NULL, "show the entity, opposite of hide." ); Event EV_BecomeSolid ( "solid", EV_DEFAULT, NULL, NULL, "make solid." ); Event EV_BecomeNonSolid ( "notsolid", EV_DEFAULT, NULL, NULL, "make non-solid." ); Event EV_Ghost ( "ghost", EV_DEFAULT, NULL, NULL, "make non-solid but still send to client regardless of hide status." ); Event EV_TouchTriggers ( "touchtriggers", EV_DEFAULT, "B", "touch_triggers_bool", "Sets whether the entity should touch triggers or not" ); Event EV_Sound ( "playsound", EV_DEFAULT, "sIFS", "soundName channel volume min_distance", "play a sound coming from this entity.\n" "default channel, CHAN_BODY." ); Event EV_StopSound ( "stopsound", EV_DEFAULT, "I", "channel", "stop the current sound on the specified channel.\n" "default channel, CHAN_BODY." ); Event EV_Bind ( "bind", EV_DEFAULT, "e", "parent", "bind this entity to the specified entity." ); Event EV_Unbind ( "unbind", EV_DEFAULT, NULL, NULL, "unbind this entity." ); Event EV_JoinTeam ( "joinTeam", EV_DEFAULT, "e", "teamMember", "join a bind team." ); Event EV_QuitTeam ( "quitTeam", EV_DEFAULT, NULL, NULL, "quit the current bind team" ); Event EV_SetHealth ( "health", EV_CHEAT | EV_TIKIONLY, "i", "newHealth", "set the health of the entity to newHealth" ); Event EV_GetHealth ( "gethealth", EV_CHEAT | EV_SCRIPTONLY, "@f", "currentHealth", "Gets the health of the entity" ); Event EV_SetMaxHealth ( "maxhealth", EV_CHEAT, "i", "newMaxHealth", "set the max_health of the entity to newMaxHealth" ); Event EV_SetScale ( "scale", EV_DEFAULT, "f", "newScale", "set the scale of the entity" ); Event EV_SetRandomScale ( "randomScale", EV_DEFAULT, "fd", "minScale maxScale", "Set the scale of the entity randomly between minScale and maxScale" ); Event EV_SetSize ( "setsize", EV_DEFAULT, "vv", "mins maxs", "Set the bounding box of the entity to mins and maxs." ); Event EV_GetMins ( "getmins", EV_SCRIPTONLY, "@v", "returnval", "Returns the minimum extent of the bounding box" ); Event EV_GetMaxs ( "getmaxs", EV_SCRIPTONLY, "@v", "returnval", "Returns the maximum extent of the bounding box" ); Event EV_SetMins ( "_setmins", EV_CODEONLY, "v", "mins", "Set the mins of the bounding box of the entity to mins." ); Event EV_SetMaxs ( "_setmaxs", EV_CODEONLY, "v", "maxs", "Set the maxs of the bounding box of the entity to maxs." ); Event EV_SetAlpha ( "alpha", EV_DEFAULT, "f", "newAlpha", "Set the alpha of the entity to alpha." ); Event EV_SetOrigin ( "origin", EV_SCRIPTONLY, "v", "newOrigin", "Set the origin of the entity to newOrigin." ); Event EV_SetOriginEveryFrame ( "setorigineveryframe", EV_DEFAULT, NULL, NULL, "Sets the origin every frame ( for bound objects )" ); Event EV_GetOrigin ( "getorigin", EV_CHEAT | EV_SCRIPTONLY, "@v", "returnval", "Gets the origin of the entity." ); Event EV_SetTargetName ( "targetname", EV_SCRIPTONLY, "s", "targetName", "set the targetname of the entity to targetName." ); Event EV_GetTargetName ( "getTargetName", EV_SCRIPTONLY, "@s", "targetName", "Gets the targetname of the entity with the leading $." ); Event EV_GetRawTargetName ( "getRawTargetName", EV_SCRIPTONLY, "@s", "rawTargetName", "Gets the targetname of the entity without the leading $." ); Event EV_SetTarget ( "target", EV_DEFAULT, "s", "targetname_to_target", "target another entity with targetname_to_target." ); Event EV_GetTarget ( "getTarget", EV_SCRIPTONLY, "@sB", "name_of_current_target wantsPrefix", "Returns the name of the current target." ); Event EV_GetTargetEntity ( "gettargetentity", EV_SCRIPTONLY, "@e", "NULL", "Returns the current target entity" ); Event EV_SetKillTarget ( "killtarget", EV_DEFAULT, "s", "targetName", "when dying kill entities with this targetName." ); Event EV_GetModelName ( "getmodelname", EV_SCRIPTONLY, "@s", "modelname", "Retrieves the model name of the entity" ); Event EV_SetAngles ( "angles", EV_SCRIPTONLY, "v[0,360][0,360][0,360]", "newAngles", "set the angles of the entity to newAngles." ); Event EV_GetAngles ( "getangles", EV_SCRIPTONLY, "@v", "angle_vector", "Retrieves the entity's angles" ); Event EV_SetAngle ( "angle", EV_DEFAULT, "f", "newAngle", "set the angles of the entity using just one value.\n" "Sets the yaw of the entity or an up and down\n" "direction if newAngle is [0-359] or -1 or -2" ); Event EV_RegisterAlias ( "alias", EV_CACHE, "ssSSSSSS", "alias realname parameter1 parameter2 parameter3 parameter4 parameter5 paramater6", "create an alias for the given realname\n" "Valid parameters are as follows:\n" "global - all instances act as one\n" "stop - once used, don't use again\n" "timeout [seconds] - once used wait [seconds] until using again\n" "maxuse [times] - can only be used [times] times.\n" "weight [weight] - random weighting" ); Event EV_RegisterAliasAndCache ( "aliascache", EV_CACHE, "ssSSSSSS", "alias realname parameter1 parameter2 parameter3 parameter4 parameter5 paramater6", "create an alias for the given realname and cache the resource\n" "Valid parameters are as follows:\n" "global - all instances act as one\n" "stop - once used, don't use again\n" "timeout [seconds] - once used wait [seconds] until using again\n" "maxuse [times] - can only be used [times] times.\n" "weight [weight] - random weighting" ); Event EV_Cache ( "cache", EV_CACHE, "s", "resourceName", "pre-cache the given resource." ); Event EV_AutoCache ( "autocache", EV_CACHE | EV_TIKIONLY, "s", "resourceName", "pre-cache the given resource." ); Event EV_SetMass ( "mass", EV_TIKIONLY, "i", "massAmount", "set the mass of this entity." ); Event EV_LoopSound ( "loopsound", EV_DEFAULT, "sFS", "soundName volume minimum_distance", "play a looped-sound with a certain volume and minimum_distance\n" "which is attached to the current entity." ); Event EV_StopLoopSound ( "stoploopsound", EV_DEFAULT, NULL, NULL, "Stop the looped-sound on this entity." ); Event EV_SurfaceModelEvent ( "surface", EV_DEFAULT, "sSSSSSS", "surfaceName parameter1 parameter2 parameter3 parameter4 parameter5 parameter6", "change a legs surface parameter for the given surface.\n" "+ sets the flag, - clears the flag\n" "Valid surface commands are:\n" "skin1 - set the skin1 offset bit\n" "skin2 - set the skin2 offset bit\n" "nodraw - don't draw this surface" ); Event EV_SetAnimOnAttachedModel ( "attachanim", EV_DEFAULT, "ss", "anim_name tag_name", "sets the anim on an attached entity at the specified tag" ); Event EV_SetCinematicAnim ( "setcinematicanim", EV_DEFAULT, "s", "anim_name", "sets a cinematic anim on an entity" ); Event EV_CinematicAnimDone ( "cinematicanimdone", EV_CODEONLY, NULL, NULL, "Called when an entity's cinematic animation is done, " "it is not meant to be called from script." ); Event EV_SetEntityExplosionModel ( "setentityexplosionmodel", EV_DEFAULT, "s", "model_name", "sets this entitys explosion model which is used in selfDetonate" ); // AI sound events Event EV_BroadcastSound ( "soundevent", EV_DEFAULT, "FS", "soundRadius soundtype", "Let the AI know that this entity made a sound,\n" "radius determines how far the sound reaches." ); Event EV_HeardSound ( "heardsound", EV_DEFAULT, "evi", "noisyEntity noisyLocation soundtype", "sent to entities when another makes a sound, not for script use\n" ); // Conditionals Event EV_IfSkill ( "ifskill", EV_DEFAULT, "isSSSSS", "skillLevel command argument1 argument2 argument3 argument4 argument5", "if the current skill level is skillLevel than execute command" ); // Lighting Event EV_SetLight ( "light", EV_DEFAULT, "ffff", "color_red color_green color_blue radius", "Create a dynmaic light on this entity." ); Event EV_LightOn ( "lightOn", EV_DEFAULT, NULL, NULL, "Turn the configured dynmaic light on this entity on." ); Event EV_LightOff ( "lightOff", EV_DEFAULT, NULL, NULL, "Turn the configured dynamic light on this entity off." ); Event EV_LightStyle ( "lightStyle", EV_DEFAULT, "i", "lightStyleIndex", "What light style to use for this dynamic light on this entity." ); Event EV_LightRed ( "lightRed", EV_DEFAULT, "f", "red", "Set the red component of the dynmaic light on this entity." ); Event EV_LightGreen ( "lightGreen", EV_DEFAULT, "f", "green", "Set the green component of the dynmaic light on this entity." ); Event EV_LightBlue ( "lightBlue", EV_DEFAULT, "f", "blue", "Set the blue component of the dynmaic light on this entity." ); Event EV_LightRadius ( "lightRadius", EV_DEFAULT, "f", "radius", "Set the radius of the dynmaic light on this entity." ); // Entity flag specific Event EV_EntityFlags ( "flags", EV_DEFAULT, "SSSSSS", "parameter1 parameter2 parameter3 parameter4 parameter5 parameter6", "Change the current entity flags.\n" "Valid flags are as follows:\n" "+ sets a flag, - clears a flag\n" "blood - should it bleed\n" "explode - should it explode when dead\n" "die_gibs - should it spawn gibs when dead\n" "god - makes the entity invincible\n" "notarget - makes the entity notarget\n" ); Event EV_EntityRenderEffects ( "rendereffects", EV_DEFAULT, "SSSSSS", "parameter1 parameter2 parameter3 parameter4 parameter5 parameter6", "Change the current render effects flags.\n" "Valid flags are as follows:\n" "+ sets a flag, - clears a flag\n" "dontdraw - send the entity to the client, but don't draw\n" "betterlighting - do sphere based vertex lighting on the entity\n" "lensflare - add a lens glow to the entity at its origin\n" "viewlensflare - add a view dependent lens glow to the entity at its origin\n" "lightoffset - use the dynamic color values as a light offset to the model\n" "skyorigin - this entity is the portal sky origin\n" "minlight - this entity always has some lighting on it\n" "fullbright - this entity is always fully lit\n" "additivedynamiclight - the dynamic light should have an additive effect\n" "lightstyledynamiclight - the dynamic light uses a light style, use the\n" "depthhack - this entity is drawn with depth hack on\n" "'lightstyle' command to set the index of the light style to be used" ); Event EV_EntityEffects ( "effects", EV_DEFAULT, "SSSSSS", "parameter1 parameter2 parameter3 parameter4 parameter5 parameter6", "Change the current entity effects flags.\n" "Valid flags are as follows:\n" "+ sets a flag, - clears a flag\n" "everyframe - process commands every time entity is rendered" ); Event EV_EntitySVFlags ( "svflags", EV_DEFAULT, "SSSSSS", "parameter1 parameter2 parameter3 parameter4 parameter5 parameter6", "Change the current server flags.\n" "Valid flags are as follows:\n" "+ sets a flag, - clears a flag\n" "broadcast - always send this entity to the client" ); // Special Effects Event EV_Censor ( "censor", EV_TIKIONLY, NULL, NULL, "used to ban certain contact when in parentmode\n" ); Event EV_Explosion ( "explosionattack", EV_DEFAULT, "sS", "explosionModel tagName", "Spawn an explosion optionally from a specific tag" ); Event EV_DoRadiusDamage ( "doradiusdamage", EV_DEFAULT, "fsffBF", "damage meansofdeath radius knockback constant_damage repeat_time", "calls RadiusDamage() using the entity information and owner" ); Event EV_SelfDetonate ( "selfdetonate", EV_DEFAULT, NULL, NULL, "spawns and explosion and removes this entity" ); Event EV_ShaderEvent ( "shader", EV_DEFAULT, "sfF", "shaderCommand argument1 argument2", "change a specific shader parameter for the entity.\n" "Valid shader commands are:\n" "translation [trans_x] [trans_y] - change the texture translation\n" "offset [offset_x] [offset_y] - change the texture offset\n" "rotation [rot_speed] - change the texture rotation speed\n" "frame [frame_num] - change the animated texture frame\n" "wavebase [base] - change the base parameter of the wave function\n" "waveamp [amp] - change the amp parameter of the wave function\n" "wavebase [phase] - change the phase parameter of the wave function\n" "wavefreq [freq] - change the frequency parameter of the wave function\n" ); Event EV_ScriptShaderEvent ( "scriptshader", EV_DEFAULT, "sfF", "shaderCommand argument1 argument2", "alias for shader command, change a specific shader parameter for the entity.\n" "Valid shader commands are:\n" "translation [trans_x] [trans_y] - change the texture translation\n" "offset [offset_x] [offset_y] - change the texture offset\n" "rotation [rot_speed] - change the texture rotation speed\n" "frame [frame_num] - change the animated texture frame\n" "wavebase [base] - change the base parameter of the wave function\n" "waveamp [amp] - change the amp parameter of the wave function\n" "wavebase [phase] - change the phase parameter of the wave function\n" "wavefreq [freq] - change the frequency parameter of the wave function\n" ); Event EV_KillAttach ( "killattach", EV_DEFAULT, NULL, NULL, "kill all the attached entities." ); Event EV_DropToFloor ( "droptofloor", EV_CODEONLY, "F", "maxRange", "drops the entity to the ground, if maxRange is not specified 8192 is used." ); Event EV_AddToSoundManager ( "_addtosoundmanager", EV_CODEONLY, NULL, NULL, "adds the current entity to the sound manager." ); Event EV_SetControllerAngles ( "setcontrollerangles", EV_CODEONLY, "iv", "num angles", "Sets the control angles for the specified bone." ); Event EV_DeathSinkStart ( "deathsinkstart", EV_CODEONLY, NULL, NULL, "Makes the entity sink into the ground and then get removed (this starts it)." ); Event EV_DeathSink ( "deathsinkeachframe", EV_CODEONLY, NULL, NULL, "Makes the entity sink into the ground and then get removed (this gets called each frame)." ); Event EV_DamageType ( "damage_type", EV_DEFAULT, "s", "meansofdeathstring", "Set the type of damage that this entity can take" ); Event EV_LookAtMe ( "lookatme", EV_DEFAULT, NULL, NULL, "Makes the player look at this object if close." ); Event EV_ProjectilesCanStickToMe ( "projectilecansticktome", EV_DEFAULT, "b", "can_stick", "Bool that determines whether projectiles stick in this entity." ); Event EV_DetachAllChildren ( "detachallchildren", EV_SCRIPTONLY, NULL, NULL, "Detach all the children from the entity." ); Event EV_Morph ( "morph", EV_DEFAULT, "sFFB", "morph_target_name final_percent morph_time return_to_zero", "Morphs to the specified morph target" ); Event EV_Unmorph ( "unmorph", EV_DEFAULT, "s", "morph_target_name", "Unmorphs the specified morph target" ); Event EV_MorphControl ( "morphcontrol", EV_CODEONLY, NULL, NULL, "Does all of the morph work each frame" ); Event EV_SetObjectProgram ( "setobjectprogram", EV_DEFAULT, "s", "program", "sets this objects program" ); Event EV_ExecuteObjectProgram ( "executeobjectprogram", EV_DEFAULT, "f", "time", "executes this objects program at the specified time" ); Event EV_ProjectileAtk ( "projectileattack", EV_DEFAULT, "sS", "projectile_name tag_name", "Launches a projectile" ); Event EV_ProjectileAttackPoint ( "projectileattackpoint", EV_DEFAULT, "svFF", "projectileName targetPosition trajectoryAngle lifespan", "Launches a projectile at the named entity" ); Event EV_ProjectileAttackEntity ( "projectileattackentity", EV_DEFAULT, "ssFF", "projectileName entityName trajectoryAngle lifespan", "Launches a projectile at the named entity" ); Event EV_ProjectileAttackFromTag ( "projectileattackfromtag", EV_DEFAULT, "ssFF", "projectileName tagName speed lifespan", "Launches a projectile from the named tag" ); Event EV_ProjectileAttackFromPoint ( "projectileattackfrompoint", EV_DEFAULT, "svvFF", "projectileName position direction speed lifespan", "Launches a projectile from the desired location" ); Event EV_TraceAtk ( "traceattack", EV_DEFAULT, "ffSFS", "damage range means_of_death knockback tag_name", "Does a trace attack" ); Event EV_Contents ( "contents", EV_DEFAULT, "sSSSSS", "ct1 ct2 ct3 ct4 ct5 ct6", "The content type of the entity" ); Event EV_Mask ( "mask", EV_DEFAULT, "sSSSSS", "mask1 mask2 mask3 mask4 mask5 mask6", "Sets the mask of the entity. Masks can either be real masks or contents. A +before the" "mask adds it, a - removes it, and nothing sets it" ); Event EV_DisplayEffect ( "displayeffect", EV_DEFAULT, "sS", "effect_type effect_name", "Displays the named effect." ); Event EV_ForceAlpha ( "forcealpha", EV_DEFAULT, "B", "bool", "Sets whether or not alpha is forced" ); Event EV_SpawnEffect ( "spawneffect", EV_DEFAULT, "ssF", "modelName tagName removeTime", "Spawns the effect at the specified tag" ); Event EV_CreateEarthquake ( "earthquake", EV_DEFAULT, "ffF", "magnitude duration distance", "Creates an earthquake" ); Event EV_SetFloatVar ( "setfloatvar", EV_DEFAULT, "sf", "variable_name float_value", "Sets a level, game, or entity variable." ); Event EV_SetVectorVar ( "setvectorvar", EV_DEFAULT, "sv", "variable_name vector_value", "Sets a level, game, or entity variable." ); Event EV_SetStringVar ( "setstringvar", EV_DEFAULT, "ss", "variable_name string_value", "Sets a level, game, or entity variable." ); Event EV_DoesVarExist ( "doesVarExist", EV_SCRIPTONLY, "@fs", "float_value variable_name", "Returns whether or not a variable exists." ); Event EV_GetFloatVar ( "getfloatvar", EV_SCRIPTONLY, "@fs", "float_value variable_name", "Gets a level, game, or entity variable." ); Event EV_RemoveVariable ( "removevar", EV_SCRIPTONLY, "s", "variable_name", "Removes a level, game, or entity variable and frees any memory used by it" ); Event EV_GetVectorVar ( "getvectorvar", EV_SCRIPTONLY, "@vs", "vector_value variable_name", "Gets a level, game, or entity variable." ); Event EV_GetStringVar ( "getstringvar", EV_SCRIPTONLY, "@ss", "string_value variable_name", "Gets a level, game, or entity variable." ); Event EV_SetUserVar1 ( "uservar1", EV_DEFAULT, "s", "string_value", "Sets an entity variable." ); Event EV_SetUserVar2 ( "uservar2", EV_DEFAULT, "s", "string_value", "Sets an entity variable." ); Event EV_SetUserVar3 ( "uservar3", EV_DEFAULT, "s", "string_value", "Sets an entity variable." ); Event EV_SetUserVar4 ( "uservar4", EV_DEFAULT, "s", "string_value", "Sets an entity variable." ); Event EV_AffectingViewMode ( "viewmode", EV_DEFAULT, "s", "viewModeName", "Specifies that this entity uses the specified view mode." ); // These are events that may come from tiki files Event EV_TikiNote ( "note", EV_DEFAULT, "s", "note", "This is a comment" ); Event EV_TikiTodo ( "todo", EV_DEFAULT, "s", "todo", "This is an item that needs to be done" ); Event EV_SetGroupID ( "setgroupid", EV_DEFAULT, "i", "groupID", "Sets the groupID of this entity" ); Event EV_Multiplayer ( "multiplayer", EV_DEFAULT, "sSSSSS", "realEventName parm1 parm2 parm3 parm4 parm5", "Sends off a real event only if this is a multiplayer game" ); Event EV_DamageModifier ( "damagemodifier", EV_DEFAULT, "ssfFFF", "modifiertype value multiplier chance minpain maxpain", "Add a damage modifier to this entities list\n" " modifiertypes are:\n" " tikiname, name, group, actortype, targetname, damagetype" ); Event EV_SetMoveType ( "setmovetype", EV_DEFAULT, "s", "movetype", "Sets the move type of this entity\n" " Valid types are: \n" " none , stationary , noclip , push , stop\n" " walk , step , fly , toss , flymissile\n" " bounce, slider, rope , gib , vehicle" ); Event EV_HelperNodeCommand ( "helpernodecommand", EV_CODEONLY, "s", "commandtype", "Command from a helper node" ); Event EV_UseDataAnim ( "useanim", EV_DEFAULT, "s", "animname", "Animation for the player to play when this entity is used." ); Event EV_UseDataType ( "usetype", EV_DEFAULT, "s", "usetype", "Use type (widget name) for the use icon" ); Event EV_UseDataThread ( "usethread", EV_DEFAULT, "s", "threadname", "Thread to call when this entity is used." ); Event EV_UseData ( "usedata", EV_DEFAULT, "sss", "animname usetype threadname", "Sets data for this usuable entity." ); Event EV_UseMaxDist ( "usemaxdist", EV_DEFAULT, "f", "maxdist", "Sets maximum distance this entity can be used." ); Event EV_UseCount ( "usecount", EV_DEFAULT, "i", "count", "Sets the number of times this entity can be used." ); Event EV_SetArchetype ( "archetype", EV_DEFAULT, "s", "archetype", "Sets the archetype name for this entity" ); Event EV_SetMissionObjective ( "missionobjective", EV_DEFAULT, "b", "missionobjective", "Sets the mission objective flag" ); Event EV_SetGameplayHealth ( "gdb_sethealth", EV_DEFAULT, "s", "healthstr", "Sets the gameplay version of health with keywords." ); Event EV_SetGameplayDamage ( "gdb_setdamage", EV_DEFAULT, "s", "damagestr", "Sets the gameplay version of damage with keywords." ); Event EV_ProcessGameplayData ( "processgameplaydata", EV_TIKIONLY, NULL, NULL, "Causes any subclass of entity to process any specific gameplay related data." ); Event EV_GetVelocity ( "getvelocity", EV_SCRIPTONLY, "@v", "NULL", "Returns the Velocity" ); Event EV_SetVelocity ( "setvelocity", EV_SCRIPTONLY, "v", "velocity", "Sets the Velocity" ); Event EV_WatchOffset ( "watchoffset", EV_DEFAULT, "v", "offset", "Sets the entity's watch offset." ); Event EV_StartStasis ( "startStasis", EV_DEFAULT, NULL, NULL, "Makes the entity go into stasis mode" ); Event EV_StopStasis ( "stopStasis", EV_DEFAULT, NULL, NULL, "Makes the entity stop its stasis mode" ); Event EV_SetTargetPos ( "settargetposition", EV_DEFAULT, "s", "targetbone", "Sets the Target Position Bone" ); Event EV_AddHealthOverTime ( "addHealthOverTime", EV_DEFAULT, "ff", "healthToAdd timeToAdd", "Specifies how much and howlong to add health." ); Event EV_SimplePlayDialog ( "simplePlayDialog", EV_DEFAULT, "sFF", "sound_file volume min_dist", "Plays a dialog without all the special features the actors' have." ); Event EV_Warp ( "warp", EV_CHEAT, "v", "position", "Warps the entity to the specified position." ); Event EV_TraceHitsEntity ( "traceHitsEntity", EV_SCRIPTONLY, "sfe", "tagName length entityToCheck", "Does a trace to check to see if it hits this entity\n." "Use this very rarely or a programmer will kill you!" ); Event EV_SetCustomShader ( "setcustomshader", EV_DEFAULT, "s", "shader_name", "Sets Custom Shader FX on Entity" ); Event EV_ClearCustomShader ( "clearcustomshader", EV_DEFAULT, "s", "shader_name", "Clears Custom Shader FX on Entity" ); Event EV_SetCustomEmitter ( "setCustomEmitter", EV_DEFAULT, "s", "emitterName", "Sets up a custom emitter for this entity." ); Event EV_ClearCustomEmitter ( "clearCustomEmitter", EV_DEFAULT, "s", "emitterName", "Clears the custom emitter for this Entity" ); Event EV_IsWithinDistanceOf ( "iswithindistanceof", EV_DEFAULT, "@fef", "returnvalue targetEntity distance", "returns 1.0 if this entity is within the specified distance of the target entity return 0.0 if it is not" ); Event EV_NetworkDetail ( "networkDetail", EV_TIKIONLY, NULL, NULL, "Sets this entity as detail that doesn't get sent across the network of set as low bandwidth by the client" ); CLASS_DECLARATION( Listener, Entity, NULL ) { { &EV_DamageModifier, &Entity::AddDamageModifier }, { &EV_TikiNote, &Entity::TikiNote }, { &EV_TikiTodo, &Entity::TikiTodo }, { &EV_Damage, &Entity::DamageEvent }, { &EV_DamageType, &Entity::DamageType }, { &EV_Kill, &Entity::Kill }, { &EV_FadeNoRemove, &Entity::FadeNoRemove }, { &EV_FadeOut, &Entity::FadeOut }, { &EV_FadeIn, &Entity::FadeIn }, { &EV_Fade, &Entity::Fade }, { &EV_Hide, &Entity::EventHideModel }, { &EV_Show, &Entity::EventShowModel }, { &EV_BecomeSolid, &Entity::BecomeSolid }, { &EV_BecomeNonSolid, &Entity::BecomeNonSolid }, { &EV_Ghost, &Entity::Ghost }, { &EV_TouchTriggers, &Entity::TouchTriggersEvent }, { &EV_Sound, &Entity::Sound }, { &EV_StopSound, &Entity::StopSound }, { &EV_SetHealth, &Entity::SetHealth }, { &EV_GetHealth, &Entity::GetHealth }, { &EV_SetMaxHealth, &Entity::SetMaxHealth }, { &EV_SetSize, &Entity::SetSize }, { &EV_SetMins, &Entity::SetMins }, { &EV_SetMaxs, &Entity::SetMaxs }, { &EV_GetMins, &Entity::GetMins }, { &EV_GetMaxs, &Entity::GetMaxs }, { &EV_SetScale, &Entity::SetScale }, { &EV_SetRandomScale, &Entity::setRandomScale }, { &EV_SetAlpha, &Entity::SetAlpha }, { &EV_SetOrigin, &Entity::SetOrigin }, { &EV_GetOrigin, &Entity::GetOrigin }, { &EV_SetTargetName, &Entity::SetTargetName }, { &EV_GetTargetName, &Entity::GetTargetName }, { &EV_GetRawTargetName, &Entity::GetRawTargetName }, { &EV_SetTarget, &Entity::SetTarget }, { &EV_GetTarget, &Entity::getTarget }, { &EV_GetTargetEntity, &Entity::GetTargetEntity }, { &EV_SetKillTarget, &Entity::SetKillTarget }, { &EV_GetModelName, &Entity::GetModelName }, { &EV_SetAngles, &Entity::SetAngles }, {&EV_GetAngles, &Entity::GetAngles }, { &EV_SetAngle, &Entity::SetAngleEvent }, { &EV_SetMass, &Entity::SetMassEvent }, { &EV_SetFullTrace, &Entity::SetFullTraceEvent }, { &EV_RegisterAlias, &Entity::RegisterAlias }, { &EV_RegisterAliasAndCache, &Entity::RegisterAliasAndCache }, { &EV_Cache, &Entity::Cache }, { &EV_AutoCache, &Entity::Cache }, { &EV_LoopSound, &Entity::LoopSound }, { &EV_StopLoopSound, &Entity::StopLoopSound }, { &EV_Model, &Entity::SetModelEvent }, { &EV_SetLight, &Entity::SetLight }, { &EV_LightOn, &Entity::LightOn }, { &EV_LightOff, &Entity::LightOff }, { &EV_LightRed, &Entity::LightRed }, { &EV_LightGreen, &Entity::LightGreen }, { &EV_LightBlue, &Entity::LightBlue }, { &EV_LightRadius, &Entity::LightRadius }, { &EV_LightStyle, &Entity::LightStyle }, { &EV_EntityFlags, &Entity::Flags }, { &EV_EntityEffects, &Entity::Effects }, { &EV_EntitySVFlags, &Entity::SVFlags }, { &EV_EntityRenderEffects, &Entity::RenderEffects }, { &EV_BroadcastSound, &Entity::BroadcastSound }, { &EV_SurfaceModelEvent, &Entity::SurfaceModelEvent }, { &EV_ProcessInitCommands, &Entity::ProcessInitCommandsEvent }, { &EV_Attach, &Entity::AttachEvent }, { &EV_AttachModel, &Entity::AttachModelEvent }, { &EV_RemoveAttachedModel, &Entity::RemoveAttachedModelEvent }, { &EV_RemoveAttachedModelByTargetname, &Entity::removeAttachedModelByTargetname }, { &EV_Detach, &Entity::DetachEvent }, { &EV_IncreaseShotCount, &Entity::IncreaseShotCount }, { &EV_TakeDamage, &Entity::TakeDamageEvent }, { &EV_NoDamage, &Entity::NoDamageEvent }, { &EV_Gravity, &Entity::Gravity }, { &EV_UseBoundingBox, &Entity::UseBoundingBoxEvent }, { &EV_Hurt, &Entity::HurtEvent }, { &EV_IfSkill, &Entity::IfSkillEvent }, { &EV_Classname, &Entity::ClassnameEvent }, { &EV_SpawnFlags, &Entity::SpawnFlagsEvent }, { &EV_SetTeam, &Entity::SetTeamEvent }, { &EV_Trigger, &Entity::TriggerEvent }, { &EV_Censor, &Entity::Censor }, { &EV_Stationary, &Entity::StationaryEvent }, { &EV_Explosion, &Entity::Explosion }, { &EV_ShaderEvent, &Entity::Shader }, { &EV_ScriptShaderEvent, &Entity::Shader }, { &EV_KillAttach, &Entity::KillAttach }, { &EV_DropToFloor, &Entity::DropToFloorEvent }, { &EV_Bind, &Entity::BindEvent }, { &EV_Unbind, &Entity::EventUnbind }, { &EV_JoinTeam, &Entity::JoinTeam }, { &EV_QuitTeam, &Entity::EventQuitTeam }, { &EV_AddToSoundManager, &Entity::AddToSoundManager }, { &EV_SetControllerAngles, &Entity::SetControllerAngles }, { &EV_DeathSinkStart, &Entity::DeathSinkStart }, { &EV_DeathSink, &Entity::DeathSink }, { &EV_LookAtMe, &Entity::LookAtMe }, { &EV_ProjectilesCanStickToMe, &Entity::ProjectilesCanStickToMe }, { &EV_DetachAllChildren, &Entity::DetachAllChildren }, { &EV_Morph, &Entity::MorphEvent }, { &EV_Unmorph, &Entity::UnmorphEvent }, { &EV_MorphControl, &Entity::MorphControl }, { &EV_SetCinematicAnim, &Entity::SetCinematicAnim }, { &EV_CinematicAnimDone, &Entity::CinematicAnimDone }, { &EV_SetAnimOnAttachedModel, &Entity::SetAnimOnAttachedModel }, { &EV_SetEntityExplosionModel, &Entity::SetEntityExplosionModel }, { &EV_SetObjectProgram, &Entity::SetObjectProgram }, { &EV_ExecuteObjectProgram, &Entity::ExecuteProgram }, { &EV_DoRadiusDamage, &Entity::DoRadiusDamage }, { &EV_SelfDetonate, &Entity::SelfDetonate }, { &EV_Anim, &Entity::PassToAnimate }, { &EV_SetFrame, &Entity::PassToAnimate }, { &EV_StopAnimating, &Entity::PassToAnimate }, { &EV_Torso_StopAnimating, &Entity::PassToAnimate }, { &EV_NewAnim, &Entity::PassToAnimate }, { &EV_ProjectileAtk, &Entity::ProjectileAtk }, { &EV_ProjectileAttackPoint, &Entity::ProjectileAttackPoint }, { &EV_ProjectileAttackEntity, &Entity::ProjectileAttackEntity }, { &EV_ProjectileAttackFromTag, &Entity::ProjectileAttackFromTag }, { &EV_ProjectileAttackFromPoint, &Entity::ProjectileAttackFromPoint }, { &EV_TraceAtk, &Entity::TraceAtk }, { &EV_Contents, &Entity::Contents }, { &EV_Mask, &Entity::setMask }, { &EV_DisplayEffect, &Entity::DisplayEffect }, { &EV_ForceAlpha, &Entity::ForceAlpha }, { &EV_SpawnEffect, &Entity::SpawnEffect }, { &EV_CreateEarthquake, &Entity::CreateEarthquake }, { &EV_SetFloatVar, &Entity::SetFloatVar }, { &EV_SetVectorVar, &Entity::SetVectorVar }, { &EV_SetStringVar, &Entity::SetStringVar }, { &EV_DoesVarExist, &Entity::doesVarExist }, { &EV_RemoveVariable, &Entity::RemoveVariable }, { &EV_GetFloatVar, &Entity::GetFloatVar }, { &EV_GetVectorVar, &Entity::GetVectorVar }, { &EV_GetStringVar, &Entity::GetStringVar }, { &EV_SetUserVar1, &Entity::SetUserVar1 }, { &EV_SetUserVar2, &Entity::SetUserVar2 }, { &EV_SetUserVar3, &Entity::SetUserVar3 }, { &EV_SetUserVar4, &Entity::SetUserVar4 }, { &EV_AffectingViewMode, &Entity::affectingViewMode }, { &EV_SetGroupID, &Entity::SetGroupID }, { &EV_Multiplayer, &Entity::MultiplayerEvent }, { &EV_SetMoveType, &Entity::setMoveType }, { &EV_UseDataAnim, &Entity::useDataAnim }, { &EV_UseDataType, &Entity::useDataType }, { &EV_UseDataThread, &Entity::useDataThread }, { &EV_UseData, &Entity::useDataEvent }, { &EV_UseMaxDist, &Entity::useDataMaxDist }, { &EV_UseCount, &Entity::useDataCount }, { &EV_SetArchetype, &Entity::setArchetype }, { &EV_SetMissionObjective, &Entity::setMissionObjective }, { &EV_SetGameplayHealth, &Entity::setGameplayHealth }, { &EV_GetVelocity, &Entity::GetVelocity }, { &EV_SetVelocity, &Entity::SetVelocity }, { &EV_WatchOffset, &Entity::SetWatchOffset }, { &EV_StartStasis, &Entity::startStasis }, { &EV_StopStasis, &Entity::stopStasis }, { &EV_SetTargetPos, &Entity::setTargetPos }, { &EV_AddHealthOverTime, &Entity::addHealthOverTime }, { &EV_SimplePlayDialog, &Entity::simplePlayDialog }, { &EV_Warp, &Entity::warp }, { &EV_TraceHitsEntity, &Entity::traceHitsEntity }, { &EV_SetOriginEveryFrame, &Entity::setOriginEveryFrame }, { &EV_SetCustomShader, &Entity::setCustomShader }, { &EV_ClearCustomShader, &Entity::clearCustomShader }, { &EV_SetCustomEmitter, &Entity::setCustomEmitter }, { &EV_ClearCustomEmitter, &Entity::clearCustomEmitter }, { &EV_IsWithinDistanceOf, &Entity::isWithinDistanceOf }, { &EV_NetworkDetail, &Entity::setNetworkDetail }, { NULL, NULL } }; Entity::Entity() { Setup(); } Entity::Entity( int create_flag ) { Setup(); if ( create_flag & ENTITY_CREATE_FLAG_ANIMATE ) animate = new Animate( this ); if ( create_flag & ENTITY_CREATE_FLAG_MOVER ) mover = new Mover( this ); } void Entity::Setup() { // Pluggable modules animate = NULL; mover = NULL; bind_info = NULL; morph_info = NULL; edict = level.AllocEdict( this ); client = edict->client; entnum = edict->s.number; edict->s.clientNum = ENTITYNUM_NONE ; // spawning variables spawnflags = level.spawnflags; level.spawnflags = 0; // rendering variables setAlpha( 1.0f ); setScale( 1.0f ); // physics variables total_delta = Vector(0, 0, 0); mass = 0; gravity = 1.0; groundentity = NULL; groundcontents = 0; velocity = vec_zero; avelocity = vec_zero; edict->clipmask = MASK_SOLID; // bind variables edict->s.bindparent = ENTITYNUM_NONE; // this is an generic entity edict->s.eType = ET_GENERAL; setContents( 0 ); edict->s.parent = ENTITYNUM_NONE; edict->s.pos.trType = TR_LERP; edict->ownerNum = ENTITYNUM_NONE; setOrigin( vec_zero ); origin.copyTo( edict->s.origin2 ); setAngles( vec_zero ); setMoveType( MOVETYPE_NONE ); setSolidType( SOLID_NOT ); // Character state health = 0; max_health = 0; deadflag = DEAD_NO; flags = 0; // underwater variables watertype = 0; waterlevel = 0; // Pain and damage variables takedamage = DAMAGE_NO; damage_type = -1; // Surface variables numsurfaces = 0; // Light variables lightRadius = 0; look_at_me = false; projectilesCanStickToMe = true; explosionModel = ""; _affectingViewModes = 0; edict->s.infoIcon = 0; addAffectingViewModes( gi.GetViewModeClassMask( "entity" ) ); _groupID = 0; damageModSystem = 0; _fulltrace = false ; useData = 0; setTargetPos( "" ); ObjectProgram = NULL; _missionObjective = false; _networkDetail = false; } Entity::~Entity() { Container bindlist; Entity *ent; int num; int i; if ( bind_info ) { // unbind any entities that are bound to me // can't unbind within this loop, so make an array // and unbind them outside of it. num = 0; for( ent = bind_info->teamchain; ent; ent = ent->bind_info->teamchain ) { if ( ent->bind_info->bindmaster == this ) { bindlist.AddObject( ent ); } } num = bindlist.NumObjects(); for( i = 1; i <= num; i++ ) { bindlist.ObjectAt( i )->unbind(); } bindlist.FreeObjectList(); unbind(); quitTeam(); detach(); // // go through and set our children // num = bind_info->numchildren; for( i = 0; ( i < MAX_MODEL_CHILDREN ) && num; i++ ) { if ( bind_info->children[ i ] == ENTITYNUM_NONE ) { continue; } ent = G_GetEntity( bind_info->children[ i ] ); if ( ent ) { ent->PostEvent( EV_Remove, 0.0f ); } num--; } } if ( targetname.length() && world ) { world->RemoveTargetEntity( targetname, this ); } level.FreeEdict( edict ); // Pluggable modules if ( animate ) delete animate; if ( mover ) delete mover; if ( bind_info ) delete bind_info; if ( morph_info ) delete morph_info; entityVars.ClearList(); if ( damageModSystem ) delete damageModSystem; if ( useData ) { delete useData; useData = NULL; } // Note, the ObjectProgram deletion is handled elsewhere // Make sure to remove our selves from the extra list if we are a mission objective if ( edict->s.missionObjective ) { G_RemoveEntityFromExtraList(entnum); } } Entity * Entity::FindEntityByName( const str &entityName ) { for ( int i = 0; i < MAX_GENTITIES; i++ ) { if ( g_entities[i].inuse && g_entities[i].entity && ( entityName == g_entities[ i ].entname ) ) { return g_entities[i].entity; } } return NULL; } void Entity::SetEntNum ( int num ) { if ( edict ) { level.FreeEdict( edict ); } level.spawn_entnum = num; level.AllocEdict( this ); client = edict->client; entnum = edict->s.number; } void Entity::ClassnameEvent ( Event *ev ) { strncpy( edict->entname, ev->GetString( 1 ), sizeof( edict->entname ) - 1 ); } void Entity::SpawnFlagsEvent ( Event *ev ) { // spawning variables spawnflags = ev->GetInteger( 1 ); if ( spawnflags & SPAWNFLAG_DETAIL ) { edict->s.renderfx |= RF_DETAIL; } } void Entity::SetTarget ( const char *text ) { if ( text ) { target = text; } else { target = ""; } } void Entity::SetTargetName ( const char *text ) { if ( targetname.length() && world ) { world->RemoveTargetEntity( targetname, this ); } if ( text ) { if ( text[ 0 ] == '$' ) text++; targetname = text; } else { targetname = ""; } if ( targetname.length() && world ) { // // make sure we don't re-targetname the world entity // if ( ( this != world ) || ( targetname == str( "world" ) ) ) { world->AddTargetEntity( targetname, this ); } else { error( "SetTargetName", "World was re-targeted with targetname %s\n", targetname.c_str() ); // this is bad //assert( 0 ); //gi.WDPrintf( "world was re-targeted with targetname %s\n", targetname.c_str() ); //targetname = "world"; } } } void Entity::SetKillTarget ( const char *text ) { if ( text ) { killtarget = text; } else { killtarget = ""; } } void Entity::setModel( const char *mdl ) { str temp; if ( LoadingSavegame && ( this == world ) ) { // don't set model on the world return; } if ( !mdl ) { mdl = ""; } // Prepend 'models/' to make things easier temp = ""; if ( ( strlen( mdl ) > 0 ) && !strchr( mdl, '*' ) && strnicmp( "models/", mdl, 7 ) && !strstr( mdl, ".spr" ) ) { temp = "models/"; } temp += mdl; // we use a temp string so that if model was passed into here, we don't // accidentally free up the string that we're using in the process. model = temp; gi.setmodel( edict, model.c_str() ); if ( gi.IsModel( edict->s.modelindex ) ) { Event *ev; numsurfaces = gi.NumSurfaces( edict->s.modelindex ); if ( !LoadingSavegame ) { CancelEventsOfType( EV_ProcessInitCommands ); ev = new Event( EV_ProcessInitCommands ); ev->AddInteger( edict->s.modelindex ); PostEvent( ev, EV_PROCESS_INIT ); } else { ProcessInitCommands( edict->s.modelindex, true ); } } else if ( strstr( mdl, ".spr" ) ) { edict->s.eType = ET_SPRITE; } // Sanity check to see if we're expecting a B-Model assert( !( ( edict->solid == SOLID_BSP ) && !edict->s.modelindex ) ); if ( ( edict->solid == SOLID_BSP ) && !edict->s.modelindex ) { const char *name; name = getClassID(); if ( !name ) { name = getClassname(); } gi.WDPrintf( "%s with SOLID_BSP and no model - '%s'(%d)\n", name, targetname.c_str(), entnum ); // Make it non-solid so that the collision code doesn't kick us out. setSolidType( SOLID_NOT ); } mins = edict->mins; maxs = edict->maxs; size = maxs - mins; edict->radius = size.length() * 0.5f; edict->radius2 = edict->radius * edict->radius; // // see if we have a mins and maxs set for this model // if ( gi.IsModel( edict->s.modelindex ) && !mins.length() && !maxs.length() ) { vec3_t tempmins, tempmaxs; int animNum; animNum = gi.Anim_NumForName( edict->s.modelindex, "idle" ); if ( animNum >= 0 ) { gi.Frame_Bounds( edict->s.modelindex, animNum, 0, edict->s.scale, tempmins, tempmaxs ); setSize( tempmins, tempmaxs ); } //vec3_t tempmins, tempmaxs; //gi.CalculateBounds( edict->s.modelindex, edict->s.scale, tempmins, tempmaxs ); //setSize( tempmins, tempmaxs ); } if ( this->isSubclassOf(Player) ) { //If we're a player, we need to reset the statemachine Player* player = (Player*)this; if ( player ) { player->SetAnim( "stand_idle", legs, true ); player->SetAnim( "stand_idle", torso, true ); player->LoadStateTable(); } } } // Added to set the weapon view model void Entity::setViewModel ( const char *mdl ) { str temp; if ( LoadingSavegame && ( this == world ) ) { // don't set model on the world return; } if ( !mdl ) { mdl = ""; } // Prepend 'models/' to make things easier temp = ""; if ( ( strlen( mdl ) > 0 ) && !strchr( mdl, '*' ) && strnicmp( "models/", mdl, 7 ) && !strstr( mdl, ".spr" ) ) { temp = "models/"; } temp += mdl; // we use a temp string so that if model was passed into here, we don't // accidentally free up the string that we're using in the process. model = temp; gi.setviewmodel( edict, model.c_str() ); if ( gi.IsModel( edict->s.viewmodelindex ) ) { Event *ev; numsurfaces = gi.NumSurfaces( edict->s.viewmodelindex ); if ( !LoadingSavegame ) { CancelEventsOfType( EV_ProcessInitCommands ); ev = new Event( EV_ProcessInitCommands ); ev->AddInteger( edict->s.viewmodelindex ); PostEvent( ev, EV_PROCESS_INIT ); } else { ProcessInitCommands( edict->s.viewmodelindex, true ); } } else if ( strstr( mdl, ".spr" ) ) { edict->s.eType = ET_SPRITE; } // Sanity check to see if we're expecting a B-Model assert( !( ( edict->solid == SOLID_BSP ) && !edict->s.viewmodelindex ) ); if ( ( edict->solid == SOLID_BSP ) && !edict->s.viewmodelindex ) { const char *name; name = getClassID(); if ( !name ) { name = getClassname(); } gi.WDPrintf( "%s with SOLID_BSP and no model - '%s'(%d)\n", name, targetname.c_str(), entnum ); // Make it non-solid so that the collision code doesn't kick us out. setSolidType( SOLID_NOT ); } mins = edict->mins; maxs = edict->maxs; size = maxs - mins; edict->radius = size.length() * 0.5f; edict->radius2 = edict->radius * edict->radius; // // see if we have a mins and maxs set for this model // //FIXME //We only did this on startup, but with the spawnargs as events it would have to //be here. Do we still need this? It may cause strange effects. /* if ( gi.IsModel( edict->s.viewmodelindex ) && !mins.length() && !maxs.length() ) { vec3_t tempmins, tempmaxs; gi.CalculateBounds( edict->s.viewmodelindex, edict->s.scale, tempmins, tempmaxs ); setSize( tempmins, tempmaxs ); } */ } void Entity::ProcessInitCommands ( int index, qboolean cache ) { tiki_cmd_t cmds; if ( LoadingSavegame && !cache ) { // Don't process init commands when loading a savegame since // it will cause items to be added to inventories unnecessarily. // All variables affected by the init commands will be set // by the unarchive functions. // // we do want to process the cache commands though regardless return; } if ( gi.InitCommands( index, &cmds ) ) { int i, j, savedindex; Event *event; // because the model has not necessarily been spawned yet, we need to set // this entity to have this index so that precaches go where they are supposed // to, this should have no bad effects, since we are only doing it in the // cache phase of spawning if ( index != edict->s.modelindex ) { savedindex = edict->s.modelindex; edict->s.modelindex = index; } else { savedindex = -1; } for( i = 0; i < cmds.num_cmds; i++ ) { event = new Event( cmds.cmds[ i ].args[ 0 ] ); if ( !cache || ( event->GetFlags() & EV_CACHE ) ) { for( j = 1; j < cmds.cmds[ i ].num_args; j++ ) { event->AddToken( cmds.cmds[ i ].args[ j ] ); } ProcessEvent( event ); } else { delete event; } } // restore the modelindex, see above if ( savedindex != -1 ) { edict->s.modelindex = savedindex; } } } void Entity::ProcessInitCommandsEvent ( Event *ev ) { int index; index = ev->GetInteger( 1 ); ProcessInitCommands( index, false ); } void Entity::EventHideModel ( Event *ev ) { hideModel(); } void Entity::EventShowModel ( Event *ev ) { showModel(); } void Entity::SetTeamEvent ( Event *ev ) { if ( !bind_info ) bind_info = CreateBindInfo(); bind_info->moveteam = ev->GetString( 1 ); } void Entity::TriggerEvent ( Event *ev ) { const char *name; Event *event; Entity *ent; TargetList *tlist; int i; int num; name = ev->GetString( 1 ); if ( !name ) return; // Check for object commands if ( name[ 0 ] == '$' ) { tlist = world->GetTargetList( str( name + 1 ) ); num = tlist->list.NumObjects(); for ( i = 1; i <= num; i++ ) { ent = tlist->list.ObjectAt( i ); assert( ent ); event = new Event( EV_Activate ); event->SetSource( ev->GetSource() ); event->SetThread( ev->GetThread() ); event->SetLineNumber( ev->GetLineNumber() ); event->AddEntity( this ); ent->ProcessEvent( event ); } } else if ( name[ 0 ] == '*' ) // Check for entnum commands { if ( !IsNumeric( &name[ 1 ] ) ) { ev->Error( "Expecting numeric value for * command, but found '%s'\n", &name[ 1 ] ); } else { ent = G_GetEntity( atoi( &name[ 1 ] ) ); if ( ent ) { event = new Event( EV_Activate ); event->SetSource( ev->GetSource() ); event->SetThread( ev->GetThread() ); event->SetLineNumber( ev->GetLineNumber() ); event->AddEntity( this ); ent->ProcessEvent( event ); } else { ev->Error( "Entity not found for * command\n" ); } } return; } else { ev->Error( "Invalid entity reference '%s'.\n", name ); } } void Entity::setAlpha ( float alpha ) { if ( alpha > 1.0f ) { alpha = 1.0f; } if ( alpha < 0.0f ) { alpha = 0.0f; } edict->s.alpha = alpha; } void Entity::setScale ( float scale ) { edict->s.scale = scale; } void Entity::setSolidType ( solid_t type ) { if ( ( !LoadingSavegame ) && ( type == SOLID_BSP ) && ( this != world ) && ( !model.length() || ( ( model[ 0 ] != '*' ) && ( !strstr( model.c_str(), ".bsp" ) ) ) ) ) { error( "setSolidType", "SOLID_BSP entity at x%.2f y%.2f z%.2f with no BSP model", origin[ 0 ], origin[ 1 ], origin[ 2 ] ); } edict->solid = type; // // set the appropriate contents type if ( edict->solid == SOLID_BBOX ) { if ( !getContents() ) setContents( CONTENTS_SOLID ); } else if ( edict->solid == SOLID_NOT ) { if ( getContents() == CONTENTS_SOLID ) setContents( 0 ); } else if ( edict->solid == SOLID_BSP ) { if ( !getContents() ) setContents( CONTENTS_SOLID ); } link(); edict->svflags &= ~SVF_NOCLIENT; if ( hidden() ) { edict->svflags |= SVF_NOCLIENT; } } void Entity::setSize ( Vector min, Vector max ) { Vector delta; if ( flags & FL_ROTATEDBOUNDS ) { vec3_t tempmins, tempmaxs; // // rotate the mins and maxs for the model // min.copyTo( tempmins ); max.copyTo( tempmaxs ); CalculateRotatedBounds2( edict->s.mat, tempmins, tempmaxs ); mins = Vector( tempmins ); maxs = Vector( tempmaxs ); size = max - min; mins.copyTo( edict->mins ); maxs.copyTo( edict->maxs ); edict->radius = size.length() * 0.5; edict->radius2 = edict->radius * edict->radius; } else { if ( ( min == edict->mins ) && ( max == edict->maxs ) ) { return; } mins = min; maxs = max; size = max - min; mins.copyTo( edict->mins ); maxs.copyTo( edict->maxs ); // // get the full mins and maxs for this model // /* if ( gi.IsModel( edict->s.modelindex ) ) { vec3_t fullmins, fullmaxs; Vector delta; gi.CalculateBounds( edict->s.modelindex, edict->s.scale, fullmins, fullmaxs ); delta = Vector( fullmaxs ) - Vector( fullmins ); edict->radius = delta.length() * 0.5f; edict->radius2 = edict->radius * edict->radius; } else */ { edict->radius = size.length() * 0.5; edict->radius2 = edict->radius * edict->radius; } } link(); } Vector Entity::getLocalVector ( const Vector &vec ) { Vector pos; pos[ 0 ] = vec * orientation[ 0 ]; pos[ 1 ] = vec * orientation[ 1 ]; pos[ 2 ] = vec * orientation[ 2 ]; return pos; } void Entity::link ( void ) { if ( !level._cleanup ) gi.linkentity( edict ); absmin = edict->absmin; absmax = edict->absmax; centroid = ( absmin + absmax ) * 0.5f; centroid.copyTo( edict->centroid ); // If this has a parent, then set the areanum the same // as the parent's if ( edict->s.parent != ENTITYNUM_NONE ) { edict->areanum = g_entities[ edict->s.parent ].areanum; } } void Entity::addOrigin ( const Vector &add ) { setOrigin( GetLocalOrigin() + add ); } void Entity::setOrigin ( void ) { setOrigin( GetLocalOrigin() ); } void Entity::setOrigin ( const Vector &org ) { Entity * ent; int i,num; if ( bind_info && bind_info->bindmaster ) { SetLocalOrigin( org ); if ( bind_info->bind_use_my_angles ) MatrixTransformVector( GetLocalOrigin(), orientation, origin ); else MatrixTransformVector( GetLocalOrigin(), bind_info->bindmaster->orientation, origin ); origin += bind_info->bindmaster->origin; origin.copyTo( edict->s.netorigin ); } // If entity has a parent, then set the origin as the // centroid of the parent, and set edict->s.netorigin // as the local origin of the entity which will be used // to position this entity on the client. else if ( edict->s.parent != ENTITYNUM_NONE ) { orientation_t orient; VectorClear( edict->s.netorigin ); ent = ( Entity * )G_GetEntity( edict->s.parent ); //ent->GetTag(( edict->s.tag_num & TAG_MASK, &origin ); ent->GetTag( edict->s.tag_num & TAG_MASK, &orient ); MatrixTransformVector( edict->s.attach_offset, orient.axis, origin ); //origin += edict->s.attach_offset; origin += orient.origin; SetLocalOrigin( vec_zero ); } else { origin = org; SetLocalOrigin( org ); origin.copyTo( edict->s.netorigin ); } origin.copyTo( edict->s.origin ); origin.copyTo( edict->currentOrigin ); link(); #if 0 if ( this->isClient() ) { i = CurrentAnim(); j = CurrentFrame(); G_DrawCoordSystem( origin, orientation[0], orientation[1], orientation[2], 30 ); gi.Printf( "%s:legs anim:%s frame %i\n", this->getClassname(), gi.Anim_NameForNum( edict->s.modelindex, i ), j ); } #endif if ( bind_info ) { // Go through and set our children num = bind_info->numchildren; for( i = 0; ( i < MAX_MODEL_CHILDREN ) && num; i++ ) { if ( bind_info->children[ i ] == ENTITYNUM_NONE ) { continue; } ent = ( Entity * )G_GetEntity( bind_info->children[ i ] ); if ( ent ) { ent->setOrigin(); } num--; } /* for( ent = bind_info->teamchain; ent != NULL; ent = ent->bind_info->teamchain ) { if ( ent->bind_info->teammaster == this ) ent->setOrigin(); } */ } } void Entity::GetRawTag ( int tagnum, orientation_t * orient, bodypart_t part ) { int anim; int frame; anim = CurrentAnim(part); frame = CurrentFrame(part); //If we don't have a valid animation, we can't get a tag if ( anim < 0 ) return; *orient = gi.Tag_OrientationEx( edict->s.modelindex, CurrentAnim( legs ), CurrentFrame( legs ), tagnum & TAG_MASK, edict->s.scale, edict->s.bone_tag, edict->s.bone_quat, 0, 0, 1.0f, ( edict->s.anim & ANIM_BLEND ) != 0, ( edict->s.torso_anim & ANIM_BLEND ) != 0, CurrentAnim( torso ), CurrentFrame( torso ), 0, 0, 1.0f ); } qboolean Entity::GetRawTag ( const char *name, orientation_t * orient, bodypart_t part ) { int tagnum; tagnum = gi.Tag_NumForName( edict->s.modelindex, name ); if ( tagnum < 0 ) return false; GetRawTag( tagnum, orient, part ); return true; } void Entity::GetTag ( int tagnum, orientation_t * orient ) { orientation_t orn; int i; GetRawTag( tagnum, &orn ); VectorCopy( origin, orient->origin ); for ( i = 0 ; i < 3 ; i++ ) { VectorMA( orient->origin, orn.origin[i], orientation[i], orient->origin ); } MatrixMultiply( orn.axis, orientation, orient->axis ); } qboolean Entity::GetTag ( const char *name, orientation_t * orient ) { int tagnum; tagnum = gi.Tag_NumForName( edict->s.modelindex, name ); if ( tagnum < 0 ) return false; GetTag( tagnum, orient ); return true; } void Entity::GetTag ( int tagnum, Vector *pos, Vector *forward, Vector *left, Vector *up ) { orientation_t orn; GetTag( tagnum, &orn); if ( pos ) { *pos = Vector( orn.origin ); } if ( forward ) { *forward = Vector( orn.axis[ 0 ] ); } if ( left ) { *left = Vector( orn.axis[ 1 ] ); } if ( up ) { *up = Vector( orn.axis[ 2 ] ); } } qboolean Entity::GetTag ( const char *name, Vector *pos, Vector *forward, Vector *left, Vector *up ) { int tagnum; tagnum = gi.Tag_NumForName( edict->s.modelindex, name ); if ( tagnum < 0 ) return false; GetTag( tagnum, pos, forward, left, up ); return true; } void Entity::addAngles ( const Vector &add ) { if ( bind_info && bind_info->bindmaster ) { setAngles( localangles + add ); } else { setAngles( angles + add ); } } void Entity::setAngles ( void ) { if ( bind_info && bind_info->bindmaster ) { setAngles( localangles ); } else { setAngles( angles ); } } void Entity::setAngles ( const Vector &ang ) { Entity * ent; int num,i; angles[ 0 ] = AngleMod( ang[ 0 ] ); angles[ 1 ] = AngleMod( ang[ 1 ] ); angles[ 2 ] = AngleMod( ang[ 2 ] ); localangles = angles; if ( bind_info && bind_info->bindmaster ) { float mat[3][3]; AnglesToAxis( localangles, mat ); R_ConcatRotations( mat, bind_info->bindmaster->orientation, orientation ); MatrixToEulerAngles( orientation, angles ); } else if ( edict->s.parent != ENTITYNUM_NONE ) { Entity *parent; Vector tagPos; Vector tagForward; Vector forwardAngles; vec3_t tempAxis[3]; vec3_t tempAxis2[3]; vec3_t finalAxis[3]; vec3_t tempForward; vec3_t finalForward; parent = ( Entity * )G_GetEntity( edict->s.parent ); parent->GetTag( edict->s.tag_num , &tagPos , &tagForward ); forwardAngles = tagForward.toAngles(); forwardAngles.copyTo( tempForward ); AnglesToAxis( tempForward, tempAxis ); AnglesToAxis( edict->s.attach_angles_offset , tempAxis2 ); MatrixMultiply( tempAxis2, tempAxis, finalAxis ); AxisToAngles( finalAxis, finalForward ); angles = finalForward; AnglesToAxis( angles, orientation ); } else { AnglesToAxis( angles, orientation ); } angles.copyTo( edict->s.netangles ); angles.copyTo( edict->s.angles ); angles.copyTo( edict->currentAngles ); // Fill the edicts matrix VectorCopy( orientation[ 0 ], edict->s.mat[ 0 ] ); VectorCopy( orientation[ 1 ], edict->s.mat[ 1 ] ); VectorCopy( orientation[ 2 ], edict->s.mat[ 2 ] ); if (this->isSubclassOf( Player ) ) { Player *player = (Player*)this ; player->GetVAngles().copyTo( edict->s.viewangles ); } else { edict->s.viewangles[0] = 0.0f ; edict->s.viewangles[1] = 0.0f ; edict->s.viewangles[2] = 0.0f ; } if ( bind_info ) { // Go through and set our children num = bind_info->numchildren; for( i = 0 ; (i < MAX_MODEL_CHILDREN) && num ; i++ ) { if ( bind_info->children[i] == ENTITYNUM_NONE ) continue; ent = ( Entity * )G_GetEntity( bind_info->children[i] ); if ( ent ) { ent->setAngles(); VectorClear( ent->edict->s.netangles ); } num--; } /* for( ent = bind_info->teamchain; ent != NULL; ent = ent->bind_info->teamchain ) { if ( ent->bind_info->teammaster == this ) ent->setAngles(); } */ } } qboolean Entity::droptofloor ( float maxfall ) { trace_t trace; Vector end; Vector start; //start = origin + Vector( "0 0 1" ); start = origin; end = origin; end[ 2 ]-= maxfall; trace = G_Trace( start, mins, maxs, end, this, edict->clipmask, false, "Entity::droptofloor" ); if ( ( trace.fraction == 1.0f ) || trace.startsolid || trace.allsolid || !trace.ent ) { groundentity = world->edict; return false; } setOrigin( trace.endpos ); groundentity = trace.ent; return true; } void Entity::DamageType ( Event *ev ) { str damage; damage = ev->GetString( 1 ); if ( damage == "all" ) { damage_type = -1; } else { damage_type = MOD_NameToNum( damage ); } } void Entity::Damage ( Entity *inflictor, Entity *attacker, float damage, const Vector &position, const Vector &direction, const Vector &normal, int knockback, int dflags, int meansofdeath, int surface_number, int bone_number, Entity *weapon ) { Event *ev; // if our damage types do not match, return if ( !MOD_matches( meansofdeath, damage_type ) ) { return; } if ( !attacker ) { attacker = world; } if ( !inflictor ) { inflictor = world; } ev = new Event( EV_Damage ); ev->AddFloat( damage ); ev->AddEntity ( inflictor ); ev->AddEntity ( attacker ); ev->AddVector ( position ); ev->AddVector ( direction ); ev->AddVector ( normal ); ev->AddInteger( knockback ); ev->AddInteger( dflags ); ev->AddInteger( meansofdeath ); ev->AddInteger( surface_number ); ev->AddInteger( bone_number ); ev->AddEntity( weapon ); ProcessEvent ( ev ); } void Entity::DamageEvent ( Event *ev ) { Entity *inflictor; Entity *attacker; float damage; Vector dir; Vector momentum; Event *event; float m; if ( ( takedamage == DAMAGE_NO ) || ( movetype == MOVETYPE_NOCLIP ) ) { return; } damage = ev->GetFloat( 1 ); inflictor = ev->GetEntity( 2 ); attacker = ev->GetEntity( 3 ); if ( !inflictor || !attacker ) return; // figure momentum add if ( ( inflictor != world ) && ( movetype != MOVETYPE_NONE ) && ( movetype != MOVETYPE_STATIONARY ) && ( movetype != MOVETYPE_BOUNCE ) && ( movetype != MOVETYPE_PUSH ) && ( movetype != MOVETYPE_STOP ) ) { dir = origin - ( inflictor->origin + ( inflictor->mins + inflictor->maxs ) * 0.5f ); dir.normalize(); if ( mass < 50 ) { m = 50; } else { m = mass; } momentum = dir * damage * ( 1700.0f / (float)m ); velocity += momentum; } // check for godmode or invincibility if ( flags & FL_GODMODE ) { return; } // team play damage avoidance //if ( ( global->teamplay == 1 ) && ( edict->team > 0 ) && ( edict->team == attacker->edict->team ) ) // { // return; // } if ( !multiplayerManager.inMultiplayer() && isSubclassOf( Player ) ) { damage *= 0.15f; } if ( deadflag ) { // Check for gib. if ( inflictor->isSubclassOf( Projectile ) ) { Event *gibEv; health -= damage; gibEv = new Event( EV_Gib ); gibEv->AddEntity( this ); gibEv->AddFloat( health ); ProcessEvent( gibEv ); } return; } // do the damage health -= damage; if ( health <= 0.0f ) { if ( attacker ) { event = new Event( EV_GotKill ); event->AddEntity( this ); event->AddFloat( damage ); event->AddEntity( inflictor ); event->AddInteger( ev->GetInteger( 9 ) ); event->AddInteger( 0 ); attacker->ProcessEvent( event ); } event = new Event( EV_Killed ); event->AddEntity( attacker ); event->AddFloat( damage ); event->AddEntity( inflictor ); ProcessEvent( event ); return; } event = new Event( EV_Pain ); event->AddFloat( damage ); event->AddEntity( attacker ); ProcessEvent( event ); } void Entity::Stun ( float time ) { Event *ev = new Event( EV_Stun ); ev->AddFloat( time ); ProcessEvent( ev ); } /* ============ CanDamage Returns true if the inflictor can directly damage the target. Used for explosions and melee attacks. ============ */ qboolean Entity::CanDamage ( const Entity *target, const Entity *skip_ent ) { trace_t trace; Vector pos; const Entity *skip_entity; //int maskToUse = MASK_SHOT; int maskToUse = CONTENTS_SOLID; if ( skip_ent ) skip_entity = skip_ent; else skip_entity = this; trace = G_Trace( origin, vec_origin, vec_origin, target->centroid, skip_entity, maskToUse, false, "Entity::CanDamage 1" ); if ( ( trace.fraction == 1.0f ) || ( trace.ent == target->edict ) ) { return true; } pos = target->centroid + Vector( 15.0f, 15.0f, 0.0f ); trace = G_Trace( origin, vec_origin, vec_origin, pos, skip_entity, maskToUse, false, "Entity::CanDamage 3" ); if ( ( trace.fraction == 1.0f ) || ( trace.ent == target->edict ) ) { return true; } pos = target->centroid + Vector( -15.0f, 15.0f, 0.0f ); trace = G_Trace( origin, vec_zero, vec_zero, pos, skip_entity, maskToUse, false, "Entity::CanDamage 4" ); if ( ( trace.fraction == 1.0f ) || ( trace.ent == target->edict ) ) { return true; } pos = target->centroid + Vector( 15.0f, -15.0f, 0.0f ); trace = G_Trace( origin, vec_zero, vec_zero, pos, skip_entity, maskToUse, false, "Entity::CanDamage 5" ); if ( ( trace.fraction == 1.0f ) || ( trace.ent == target->edict ) ) { return true; } pos = target->centroid + Vector( -15.0f, -15.0f, 0.0f ); trace = G_Trace( origin, vec_zero, vec_zero, pos, skip_entity, maskToUse, false, "Entity::CanDamage 6" ); if ( ( trace.fraction == 1.0f ) || ( trace.ent == target->edict ) ) { return true; } return false; } qboolean Entity::IsTouching ( const Entity *e1 ) { if ( e1->absmin.x > absmax.x ) { return false; } if ( e1->absmin.y > absmax.y ) { return false; } if ( e1->absmin.z > absmax.z ) { return false; } if ( e1->absmax.x < absmin.x ) { return false; } if ( e1->absmax.y < absmin.y ) { return false; } if ( e1->absmax.z < absmin.z ) { return false; } return true; } void Entity::FadeNoRemove ( Event *ev ) { float rate; float target; float myalpha; if ( ev->NumArgs() > 1 ) { target = ev->GetFloat( 2 ); } else { target = 0; } if ( ev->NumArgs() > 0 ) { rate = ev->GetFloat( 1 ); assert( rate ); if ( rate > 0.0f ) rate = FRAMETIME / rate; } else { rate = 0.03f; } myalpha = edict->s.alpha; myalpha -= rate; if ( myalpha < target ) myalpha = target; setAlpha( myalpha ); if ( myalpha > target ) { PostEvent( *ev, FRAMETIME ); } G_SetConstantLight( &edict->s.constantLight, &myalpha, &myalpha, &myalpha, 0 ); } void Entity::FadeOut ( Event *ev ) { float myscale; float myalpha; myscale = edict->s.scale; myscale -= 0.03f; myalpha = edict->s.alpha; myalpha -= 0.03f; if ( myscale < 0.0f ) myscale = 0.0f; if ( myalpha < 0.0f ) myalpha = 0.0f; if ( ( myscale <= 0.0f ) && ( myalpha <= 0.0f ) ) { PostEvent( EV_Remove, 0.0f ); } else { PostEvent( *ev, FRAMETIME ); } setScale( myscale ); setAlpha( myalpha ); } void Entity::FadeIn ( Event *ev ) { float rate; float target; float myalpha; if ( ev->NumArgs() > 1 ) { target = ev->GetFloat( 2 ); } else { target = 1; } if ( ev->NumArgs() > 0 ) { rate = ev->GetFloat( 1 ); assert( rate ); if ( rate > 0.0f ) rate = FRAMETIME / rate; } else { rate = 0.03f; } myalpha = edict->s.alpha; myalpha += rate; if ( myalpha > target ) myalpha = target; if ( myalpha < target ) { PostEvent( *ev, FRAMETIME ); } setAlpha( myalpha ); } void Entity::Fade ( Event *ev ) { float rate; float target; float myalpha; if ( ev->NumArgs() > 1 ) { target = ev->GetFloat( 2 ); } else { target = 0; } if ( ev->NumArgs() > 0 ) { rate = ev->GetFloat( 1 ); assert( rate ); if ( rate > 0.0f ) rate = FRAMETIME / rate; } else { rate = 0.03f; } myalpha = edict->s.alpha; myalpha -= rate; if ( myalpha <= 0.0f ) { PostEvent( EV_Remove, 0.0f ); return; } if ( myalpha < target ) myalpha = target; if ( myalpha > target ) { PostEvent( *ev, FRAMETIME ); } setAlpha( myalpha ); G_SetConstantLight( &edict->s.constantLight, &myalpha, &myalpha, &myalpha, 0 ); } void Entity::SetMassEvent ( Event *ev ) { mass = ev->GetInteger( 1 ); } void Entity::CheckGround ( void ) { Vector point; trace_t trace; if ( flags & ( FL_SWIM | FL_FLY ) ) { return; } if ( velocity.z > 100.0f ) { groundentity = NULL; return; } // if the hull point one-quarter unit down is solid the entity is on ground point = origin; point.z -= sv_groundtracelength->value; trace = G_Trace( origin, mins, maxs, point, this, edict->clipmask, false, "Entity::CheckGround" ); if (!(trace.surfaceFlags & SURF_TERRAIN)) { point.z = origin.z - 0.25f; // put groundtrace dist back to 0.25f for non-terrain check trace = G_Trace( origin, mins, maxs, point, this, edict->clipmask, false, "Entity::CheckGround" ); } // check steepness if ( ( trace.plane.normal[ 2 ] <= 0.7f ) && !trace.startsolid ) { groundentity = NULL; return; } groundentity = trace.ent; groundplane = trace.plane; groundcontents = trace.contents; if ( !trace.startsolid && !trace.allsolid ) { setOrigin( trace.endpos ); velocity.z = 0; } } void Entity::BecomeSolid ( Event *ev ) { if ( ( model.length() ) && ( ( model[ 0 ] == '*' ) || ( strstr( model.c_str(), ".bsp" ) ) ) ) { setSolidType( SOLID_BSP ); } else { setSolidType( SOLID_BBOX ); } } void Entity::BecomeNonSolid ( Event *ev ) { setSolidType( SOLID_NOT ); } void Entity::Ghost ( Event *ev ) { // Make not solid, but send still send over whether it is hidden or not setSolidType( SOLID_NOT ); edict->svflags &= ~SVF_NOCLIENT; } void Entity::LoopSound ( Event *ev ) { str sound_name; float volume = DEFAULT_VOL; float min_dist = DEFAULT_MIN_DIST; str min_dist_string; if (ev->NumArgs() < 1) return; // Get parameters sound_name = ev->GetString( 1 ); if ( ev->NumArgs() > 1 ) volume = ev->GetFloat( 2 ); if ( ev->NumArgs() > 2 ) { min_dist_string = ev->GetString( 3 ); if ( min_dist_string == LEVEL_WIDE_STRING ) min_dist = LEVEL_WIDE_MIN_DIST; else min_dist = ev->GetFloat( 3 ); if ( min_dist >= LEVEL_WIDE_MIN_DIST_CUTOFF ) min_dist = LEVEL_WIDE_MIN_DIST; } // Add this sound to loop LoopSound( sound_name, volume, min_dist ); } void Entity::LoopSound( const str &sound_name, float volume, float min_dist ) { const char *name = NULL; str random_alias; // Get the real sound to be played if ( sound_name.length() > 0 ) { // Get the real sound to play name = gi.GlobalAlias_FindRandom( sound_name.c_str() ); if ( !name ) { random_alias = GetRandomAlias( sound_name ).c_str(); if ( random_alias.length() > 0 ) name = random_alias.c_str(); } if ( !name ) name = sound_name.c_str(); // Add the looping sound to the entity edict->s.loopSound = gi.soundindex( name ); edict->s.loopSoundVolume = volume; edict->s.loopSoundMinDist = min_dist; } } void Entity::StopLoopSound( Event *ev ) { StopLoopSound(); } void Entity::StopLoopSound( void ) { edict->s.loopSound = 0; } void Entity::Sound( Event *ev ) { str sound_name; float volume; int channel; float min_dist; int i; str min_dist_string; // Set defaults volume = DEFAULT_VOL; min_dist = DEFAULT_MIN_DIST; channel = CHAN_BODY; // Get sound parameters for ( i = 1 ; i <= ev->NumArgs() ; i++ ) { switch (i-1) { case 0: sound_name = ev->GetString( i ); break; case 1: channel = ev->GetInteger( i ); break; case 2: volume = ev->GetFloat( i ); break; case 3: min_dist_string = ev->GetString( i ); if ( min_dist_string == LEVEL_WIDE_STRING ) min_dist = LEVEL_WIDE_MIN_DIST; else min_dist = ev->GetFloat( i ); if ( min_dist >= LEVEL_WIDE_MIN_DIST_CUTOFF ) min_dist = LEVEL_WIDE_MIN_DIST; break; default: break; } } Sound( sound_name, channel, volume, min_dist, NULL ); } void Entity::StopSound ( Event *ev ) { if (ev->NumArgs() < 1) StopSound( CHAN_BODY ); else StopSound( ev->GetInteger( 1 ) ); } void Entity::StopSound ( int channel ) { gi.StopSound( entnum, channel ); } void Entity::SetLight ( Event *ev ) { float r, g, b; if ( ev->NumArgs() == 1 ) { Vector tmp; tmp = ev->GetVector( 1 ); r = tmp.x; g = tmp.y; b = tmp.z; } else { r = ev->GetFloat( 1 ); g = ev->GetFloat( 2 ); b = ev->GetFloat( 3 ); lightRadius = ev->GetFloat( 4 ); } G_SetConstantLight( &edict->s.constantLight, &r, &g, &b, &lightRadius ); } void Entity::LightOn ( Event *ev ) { G_SetConstantLight( &edict->s.constantLight, NULL, NULL, NULL, &lightRadius ); } void Entity::LightOff ( Event *ev ) { float radius = 0; G_SetConstantLight( &edict->s.constantLight, NULL, NULL, NULL, &radius ); } void Entity::LightRed ( Event *ev ) { float r; r = ev->GetFloat( 1 ); G_SetConstantLight( &edict->s.constantLight, &r, NULL, NULL, NULL ); } void Entity::LightGreen ( Event *ev ) { float g; g = ev->GetFloat( 1 ); G_SetConstantLight( &edict->s.constantLight, NULL, &g, NULL, NULL ); } void Entity::LightBlue ( Event *ev ) { float b; b = ev->GetFloat( 1 ); G_SetConstantLight( &edict->s.constantLight, NULL, NULL, &b, NULL ); } void Entity::LightRadius ( Event *ev ) { lightRadius = ev->GetFloat( 1 ); G_SetConstantLight( &edict->s.constantLight, NULL, NULL, NULL, &lightRadius ); } void Entity::LightStyle ( Event *ev ) { int style; style = ev->GetInteger( 1 ); G_SetConstantLight( &edict->s.constantLight, NULL, NULL, NULL, NULL, &style ); } void Entity::SetHealth ( Event *ev ) { health = ev->GetFloat( 1 ); if( max_health < health ) { max_health = health; } } //-------------------------------------------------------------- // // Name: setGameplayHealth // Class: Entity // // Description: This function acts as a filter to the real function. // It gets data from the database, and then passes it // along to the original event. This is here as an attempt // to sway people into using the database standard instead of // hardcoded numbers. // // Parameters: Event *ev // str -- The value keyword from the database (low, medium, high, etc). // // Returns: None // //-------------------------------------------------------------- void Entity::setGameplayHealth( Event *ev ) { if ( ev->NumArgs() < 1 ) return; GameplayManager *gpm = GameplayManager::getTheGameplayManager(); if ( !gpm->hasFormula("Health") ) return; str healthstr = ev->GetString( 1 ); float healthmod = 1.0f; if ( gpm->getDefine(healthstr) != "" ) healthmod = (float)atof(gpm->getDefine(healthstr)); GameplayFormulaData fd(this); float finalhealth = gpm->calculate("Health", fd, healthmod); Event *newev = new Event(EV_SetHealth); newev->AddFloat(finalhealth); ProcessEvent(newev); } //---------------------------------------------------------------- // Name: addHealth // Class: Entity // // Description: Adds health to the entity up to the max health of the entity or the // max health specified (if its not 0) // // Parameters: float healthToAdd - health we are going to add to the entity // float maxHealth - if not 0, the max health the entity can have // // Returns: none //---------------------------------------------------------------- void Entity::addHealth( float healthToAdd, float maxHealth ) { float tempMaxHealth; float newHealth; // Get the max health if ( maxHealth ) tempMaxHealth = maxHealth; else tempMaxHealth = max_health; // Calculate the new health newHealth = health + healthToAdd; // Set the new health if ( newHealth > tempMaxHealth ) setHealth( tempMaxHealth ); else setHealth( newHealth ); } void Entity::GetHealth ( Event *ev ) { ev->ReturnFloat( health ); } void Entity::SetMaxHealth ( Event *ev ) { max_health = ev->GetFloat( 1 ); } void Entity::SetSize ( Event *ev ) { Vector min, max; min = ev->GetVector( 1 ); max = ev->GetVector( 2 ); setSize( min, max ); } void Entity::SetMins ( Event *ev ) { Vector min; min = ev->GetVector( 1 ); setSize( min, maxs ); } void Entity::SetMaxs ( Event *ev ) { Vector max; max = ev->GetVector( 1 ); setSize( mins, max ); } void Entity::GetMins ( Event *ev ) { ev->ReturnVector( mins ); } void Entity::GetMaxs ( Event *ev ) { ev->ReturnVector( maxs ); } void Entity::SetScale ( Event *ev ) { setScale( ev->GetFloat( 1 ) ); } void Entity::setRandomScale( Event *ev ) { float minScale; float maxScale; minScale = ev->GetFloat( 1 ); maxScale = ev->GetFloat( 2 ); setScale( G_Random( maxScale - minScale ) + minScale ); } void Entity::SetAlpha ( Event *ev ) { setAlpha( ev->GetFloat( 1 ) ); } void Entity::SetOrigin ( Event *ev ) { setOrigin( ev->GetVector( 1 ) ); } void Entity::GetOrigin ( Event *ev ) { ev->ReturnVector( origin ); } void Entity::SetTargetName ( Event *ev ) { SetTargetName( ev->GetString( 1 ) ); } //---------------------------------------------------------------- // Name: GetRawTargetName // Class: Entity // // Description: Gets the targetname of the entity without the leading $ // // Parameters: None // // Returns: str targetname (through ev) - the targetname of the entity //---------------------------------------------------------------- void Entity::GetRawTargetName ( Event *ev ) { ev->ReturnString( targetname ); } //---------------------------------------------------------------- // Name: GetTargetName // Class: Entity // // Description: Gets the targetname of the entity with the leading $ // // Parameters: None // // Returns: str targetname (through ev) - the targetname of the entity //---------------------------------------------------------------- void Entity::GetTargetName ( Event *ev ) { str nameToReturn; nameToReturn = "$"; nameToReturn += targetname; ev->ReturnString( nameToReturn ); } void Entity::SetTarget ( Event *ev ) { SetTarget( ev->GetString( 1 ) ); } void Entity::getTarget( Event *ev ) { str nameToReturn; bool wantsPrefix; if ( ev->NumArgs() > 0 ) wantsPrefix = ev->GetBoolean( 1 ); else wantsPrefix = false; if ( wantsPrefix ) { nameToReturn = "$"; } nameToReturn += target; ev->ReturnString( nameToReturn ); } //-------------------------------------------------------------- // Name: GetTargetEntity() // Class: Entity // // Description: Returns the Entity of the target // // Parameters: Event *ev // // Returns: None ( Entity, though, through the event ) //-------------------------------------------------------------- void Entity::GetTargetEntity( Event *ev ) { TargetList *tlist = world->GetTargetList( target, false ); if ( tlist ) { ev->ReturnEntity( tlist->GetNextEntity( NULL ) ); } else { ev->ReturnEntity( NULL ); } } void Entity::SetKillTarget ( Event *ev ) { SetKillTarget( ev->GetString( 1 ) ); } //----------------------------------------------------- // // Name: GetModelName // Class: Entity // // Description: Retrieves the model name // // Parameters: event - the event to request the model name // // Returns: None //----------------------------------------------------- void Entity::GetModelName(Event* ev) { ev->ReturnString(model.c_str()); } void Entity::SetAngles ( Event *ev ) { setAngles( ev->GetVector( 1 ) ); } //----------------------------------------------------- // // Name: GetAngles // Class: Entity // // Description: Retrieves the entity's angles // // Parameters: event - event to request the entity's angles // // Returns: None //----------------------------------------------------- void Entity::GetAngles(Event* ev) { ev->ReturnVector(angles); } Vector Entity::GetControllerAngles ( int num ) { Vector controller_angles; assert( ( num >= 0 ) && ( num < NUM_BONE_CONTROLLERS ) ); if ( ( num < 0 ) || ( num >= NUM_BONE_CONTROLLERS ) ) { error( "GetControllerAngles", "Bone controller index out of range (%d)\n", num ); return vec_zero; } controller_angles = edict->s.bone_angles[ num ]; return controller_angles; } void Entity::SetControllerAngles ( int num, vec3_t angles ) { assert( ( num >= 0 ) && ( num < NUM_BONE_CONTROLLERS ) ); if ( ( num < 0 ) || ( num >= NUM_BONE_CONTROLLERS ) ) { error( "SetControllerAngles", "Bone controller index out of range (%d)\n", num ); return; } VectorCopy( angles, edict->s.bone_angles[ num ] ); EulerToQuat( edict->s.bone_angles[ num ], edict->s.bone_quat[ num ] ); } void Entity::SetControllerAngles ( Event *ev ) { int num; Vector angles; if ( ev->NumArgs() < 2 ) return; num = ev->GetInteger( 1 ); angles = ev->GetVector( 2 ); SetControllerAngles( num, angles ); } void Entity::SetControllerTag ( int num, int tag_num ) { assert( ( num >= 0 ) && ( num < NUM_BONE_CONTROLLERS ) ); if ( ( num < 0 ) || ( num >= NUM_BONE_CONTROLLERS ) ) { error( "SetControllerTag", "Bone controller index out of range (%d)\n", num ); return; } edict->s.bone_tag[ num ] = tag_num; } //=============================================================== // Name: SetFullTraceEvent // Class: Entity // // Description: Sets the fulltrace flag. This flag is used by // G_PushMove to determine if the object needs a // fulltrace or not. Default for flag is false. // // Parameters: Event* -- first argument is boolean // // Returns: None // //=============================================================== inline void Entity::SetFullTraceEvent ( Event *ev ) { _fulltrace = ev->GetBoolean( 1 ); } void Entity::RegisterAlias ( Event *ev ) { char parameters[100]; int i; // Get the parameters for this alias command parameters[0] = 0; for( i = 3 ; i <= ev->NumArgs() ; i++ ) { strcat( parameters, ev->GetString( i ) ); strcat( parameters, " "); } gi.Alias_Add( edict->s.modelindex, ev->GetString( 1 ), ev->GetString( 2 ), parameters ); } void Entity::Cache ( Event *ev ) { CacheResource( ev->GetString( 1 ), this ); } void Entity::RegisterAliasAndCache ( Event *ev ) { RegisterAlias(ev); CacheResource( ev->GetString( 2 ), this ); } void Entity::Sound ( const str &sound_name, int channel, float volume, float min_dist, Vector *sound_origin, float pitch_modifier, qboolean onlySendToThisEntity ) { const char *name = NULL; vec3_t org; str random_alias; if ( sound_name.length() > 0 ) { // Get the real sound to play name = gi.GlobalAlias_FindRandom( sound_name.c_str() ); if ( !name ) { random_alias = GetRandomAlias( sound_name ).c_str(); if ( random_alias.length() > 0 ) name = random_alias.c_str(); } if ( !name ) name = sound_name.c_str(); // Play the sound if ( name != NULL ) { if ( sound_origin != NULL) { sound_origin->copyTo( org ); entnum = ENTITYNUM_NONE; } else { VectorCopy( edict->s.origin, org ); } gi.Sound( &org, entnum, channel, name, volume, min_dist, pitch_modifier, onlySendToThisEntity ); } } else { warning( "Sound", "Null sample pointer" ); } } qboolean Entity::attach ( int parent_entity_num, int tag_num, qboolean use_angles, Vector offset, Vector angles_offset ) { int i; Entity * parent; if ( entnum == parent_entity_num ) { warning("attach","Trying to attach to oneself." ); return false; } if ( edict->s.parent != ENTITYNUM_NONE ) detach(); // // make sure this is a modelanim entity so that the attach works properly // if ( edict->s.eType == ET_GENERAL ) { edict->s.eType = ET_MODELANIM; } // // get the parent // parent = ( Entity * )G_GetEntity( parent_entity_num ); if ( !parent->bind_info ) parent->bind_info = CreateBindInfo(); if (parent->bind_info->numchildren < MAX_MODEL_CHILDREN) { // // find a free spot in the parent // for ( i=0; i < MAX_MODEL_CHILDREN; i++ ) if ( parent->bind_info->children[i] == ENTITYNUM_NONE ) { break; } edict->s.parent = parent_entity_num; setSolidType( SOLID_NOT ); parent->bind_info->children[i] = entnum; parent->bind_info->numchildren++; if ( tag_num >= 0 ) { edict->s.tag_num = tag_num; } edict->s.attach_use_angles = use_angles; offset.copyTo( edict->s.attach_offset ); angles_offset.copyTo( edict->s.attach_angles_offset ); setOrigin(); return true; } return false; } void Entity::KillAttach ( Event *ev ) { int i; Entity *child = NULL; if ( bind_info ) { // Kill all of this entities children for ( i = 0 ; i < MAX_MODEL_CHILDREN; i++ ) { if ( bind_info->children[i] != ENTITYNUM_NONE ) { // Remove child child = ( Entity * )G_GetEntity( bind_info->children[i] ); if ( child ) child->ProcessEvent( EV_Remove ); // Remove child from this entity bind_info->children[i] = ENTITYNUM_NONE; } } bind_info->numchildren = 0; } } void Entity::detach ( void ) { int i; int num; Entity * parent; if ( edict->s.parent == ENTITYNUM_NONE ) return; parent = ( Entity * )G_GetEntity( edict->s.parent ); if (!parent) return; if ( parent->bind_info) { for ( i=0,num = parent->bind_info->numchildren; i < MAX_MODEL_CHILDREN; i++ ) { if ( parent->bind_info->children[i] == ENTITYNUM_NONE ) { continue; } if (parent->bind_info->children[i] == entnum) { parent->bind_info->children[i] = ENTITYNUM_NONE; parent->bind_info->numchildren--; break; } num--; if (!num) break; } } edict->s.parent = ENTITYNUM_NONE; setOrigin( origin ); } void Entity::Flags( Event *ev ) { const char *flag; int mask; int action; int i; for ( i = 1; i <= ev->NumArgs(); i++ ) { action = FLAG_IGNORE; flag = ev->GetString( i ); switch( flag[0] ) { case '+': action = FLAG_ADD; flag++; break; case '-': action = FLAG_CLEAR; flag++; break; default: ev->Error( "Entity::Flags", "First character is not '+' or '-', assuming '+'\n" ); action = FLAG_ADD; break; } // // WARNING: please change the Event decleration, // to match this function, if flags are added or // deleted the event must be updated. // if ( !stricmp( flag, "blood" ) ) mask = FL_BLOOD; else if ( !stricmp( flag, "explode" ) ) mask = FL_DIE_EXPLODE; else if ( !stricmp( flag, "die_gibs" ) ) mask = FL_DIE_GIBS; else if ( !stricmp( flag, "autoaim" ) ) mask = FL_AUTOAIM; else if ( !stricmp( flag, "god" ) ) mask = FL_GODMODE; else if ( !stricmp( flag, "notarget" ) ) mask = FL_NOTARGET; else { mask = 0; action = FLAG_IGNORE; ev->Error( "Unknown flag '%s'", flag ); } switch (action) { case FLAG_ADD: flags |= mask; break; case FLAG_CLEAR: flags &= ~mask; break; case FLAG_IGNORE: break; } } if ( !com_blood->integer ) { if ( flags & (FL_BLOOD|FL_DIE_GIBS) ) { flags &= ~FL_BLOOD; flags &= ~FL_DIE_GIBS; } } } void Entity::Effects( Event *ev ) { const char *flag; int mask=0; int action; int i; for ( i = 1; i <= ev->NumArgs(); i++ ) { action = 0; flag = ev->GetString( i ); switch( flag[0] ) { case '+': action = FLAG_ADD; flag++; break; case '-': action = FLAG_CLEAR; flag++; break; default: ev->Error( "Entity::Effects", "First character is not '+' or '-', assuming '+'\n" ); action = FLAG_ADD; break; } // // WARNING: please change the Event decleration, // to match this function, if flags are added or // deleted the event must be updated. // if ( !stricmp( flag, "everyframe" ) ) mask = EF_EVERYFRAME; else { action = FLAG_IGNORE; ev->Error( "Unknown token %s.", flag ); } switch (action) { case FLAG_ADD: edict->s.eFlags |= mask; break; case FLAG_CLEAR: edict->s.eFlags &= ~mask; break; case FLAG_IGNORE: break; } } } void Entity::RenderEffects( Event *ev ) { const char *flag; int mask=0; int action; int i; for ( i = 1; i <= ev->NumArgs(); i++ ) { action = 0; flag = ev->GetString( i ); switch( flag[0] ) { case '+': action = FLAG_ADD; flag++; break; case '-': action = FLAG_CLEAR; flag++; break; default: ev->Error( "Entity::RenderEffects", "First character is not '+' or '-', assuming '+'\n" ); action = FLAG_ADD; break; } // // WARNING: please change the Event decleration, // to match this function, if flags are added or // deleted the event must be updated. // if ( !stricmp( flag, "dontdraw" ) ) mask = RF_DONTDRAW; else if ( !stricmp( flag, "betterlighting" ) ) mask = RF_EXTRALIGHT; else if ( !stricmp ( flag, "lensflare" ) ) mask = RF_LENSFLARE; else if ( !stricmp ( flag, "viewlensflare" ) ) mask = RF_VIEWLENSFLARE; else if ( !stricmp ( flag, "lightoffset" ) ) mask = RF_LIGHTOFFSET; else if ( !stricmp( flag, "skyorigin" ) ) mask = RF_SKYORIGIN; else if ( !stricmp( flag, "fullbright" ) ) mask = RF_FULLBRIGHT; else if ( !stricmp( flag, "minlight" ) ) mask = RF_MINLIGHT; else if ( !stricmp( flag, "additivedynamiclight" ) ) mask = RF_ADDITIVE_DLIGHT; else if ( !stricmp( flag, "lightstyledynamiclight" ) ) mask = RF_LIGHTSTYLE_DLIGHT; else if ( !stricmp( flag, "shadow" ) ) mask = RF_SHADOW; else if ( !stricmp( flag, "shadowFromBip01" ) ) mask = RF_SHADOW_FROM_BIP01; else if ( !stricmp( flag, "preciseshadow" ) ) mask = RF_SHADOW_PRECISE; else if ( !stricmp( flag, "dontInheritAlpha" ) ) mask = RF_CHILDREN_DONT_INHERIT_ALPHA; else if ( !stricmp( flag, "invisible" ) ) mask = RF_INVISIBLE; else if ( !stricmp( flag, "depthhack" ) ) mask = RF_DEPTHHACK; else { action = FLAG_IGNORE; ev->Error( "Unknown token %s.", flag ); } switch (action) { case FLAG_ADD: edict->s.renderfx |= mask; break; case FLAG_CLEAR: edict->s.renderfx &= ~mask; break; case FLAG_IGNORE: break; } } } void Entity::SVFlags ( Event *ev ) { const char *flag; int mask=0; int action; int i; for ( i = 1; i <= ev->NumArgs(); i++ ) { action = 0; flag = ev->GetString( i ); switch( flag[0] ) { case '+': action = FLAG_ADD; flag++; break; case '-': action = FLAG_CLEAR; flag++; break; default: ev->Error( "Entity::SVFlags", "First character is not '+' or '-', assuming '+'\n" ); action = FLAG_ADD; break; } // // WARNING: please change the Event decleration, // to match this function, if flags are added or // deleted the event must be updated. // if ( !stricmp( flag, "broadcast" ) ) mask = SVF_BROADCAST; else if ( !stricmp( flag, "sendonce" ) ) mask = SVF_SENDONCE; else { action = FLAG_IGNORE; ev->Error( "Unknown token %s.", flag ); } switch (action) { case FLAG_ADD: edict->svflags |= mask; break; case FLAG_CLEAR: edict->svflags &= ~mask; break; case FLAG_IGNORE: break; } } if ( edict->svflags & SVF_SENDONCE ) { // Turn this entity into an event if the SENDONCE flag is sent edict->s.eType = ET_EVENTS; edict->svflags &= ~SVF_SENT; } } void Entity::BroadcastSound ( float rad, int soundType //Defaults to SOUNDTYPE_GENERAL ) { if ( !( this->flags & FL_NOTARGET ) ) { G_BroadcastSound( this, centroid, rad, soundType ); } } void Entity::BroadcastSound ( Event *ev ) { float rad = SOUND_RADIUS; int soundTypeIdx = SOUNDTYPE_GENERAL; str soundTypeStr = ""; if ( !( this->flags & FL_NOTARGET ) ) { if (ev->NumArgs() > 0 ) //<-- At least 1 Parameter { rad = ev->GetFloat( 1 ); if(ev->NumArgs() > 1 ) //<-- At least 2 Parameters { soundTypeStr = ev->GetString( 2 ); soundTypeIdx = Soundtype_string_to_int( soundTypeStr ); } } if (soundTypeIdx == SOUNDTYPE_FOOTSTEPS_RUN || soundTypeIdx == SOUNDTYPE_FOOTSTEPS_WALK ) rad = ModifyFootstepSoundRadius( rad , soundTypeIdx ); BroadcastSound( rad, soundTypeIdx ); } } float Entity::ModifyFootstepSoundRadius ( float radius, int soundTypeIdx ) { trace_t trace; Vector end; Vector start; start = origin; end = origin; end[2]-= 1000.0f; int surftype; trace = G_Trace( start, mins, maxs, end, this, edict->clipmask, false, "Entity::ModifyFootstepsRadius" ); surftype = trace.surfaceFlags & MASK_SURF_TYPE; switch ( surftype ) { case SURF_TYPE_DIRT: radius *= .5f; break; case SURF_TYPE_ROCK: break; case SURF_TYPE_METAL: radius *= 1.5; break; case SURF_TYPE_GRILL: radius *= 1.25f; break; case SURF_TYPE_ORGANIC: radius *= .5f; break; case SURF_TYPE_SQUISHY: radius *= .5f; break; case SURF_TYPE_SAND: radius *= .5f; break; case SURF_TYPE_SNOW: radius *= .5f; break; case SURF_TYPE_METAL_DUCT: radius *= 1.25f; break; case SURF_TYPE_METAL_HOLLOW: radius *= 1.25f; break; case SURF_TYPE_CARPET: radius *= .5f; break; } //Tone Radius Down for Walking if (soundTypeIdx == SOUNDTYPE_FOOTSTEPS_WALK ) radius *= .75f; return radius; } void Entity::Think ( void ) { } void Entity::SetWaterType ( void ) { qboolean isinwater; watertype = gi.pointcontents( origin, 0 ); isinwater = watertype & MASK_WATER; if ( isinwater ) { waterlevel = 1; } else { waterlevel = 0; } } void Entity::DamageSkin ( trace_t * trace, float damage ) { /* FIXME : Do we need damage skins? int surface; // FIXME handle different bodyparts surface = trace->intersect.surface; if ( !edict->s.surfaces[ surface ] ) { edict->s.surfaces[ surface ]++; } */ } void Entity::Kill ( Event *ev ) { health = 0.0f; Damage( this, this, 10.0f, origin, vec_zero, vec_zero, 0, 0, MOD_SUICIDE ); } void Entity::SurfaceCommand ( const char * surf_name, const char * token ) { const char * current_surface_name; int surface_num; int mask; int action; qboolean do_all = false; qboolean mult = false; if ( surf_name[ strlen( surf_name ) - 1 ] == '*' ) { mult = true; surface_num = 0; } else if ( str( surf_name ) != str( "all" ) ) { surface_num = gi.Surface_NameToNum( edict->s.modelindex, surf_name ); if (surface_num < 0) { warning( "SurfaceCommand", "group %s not found for entity %s (%d), model %s.\n", surf_name, targetname.c_str(), entnum, model.c_str() ); return; } } else { surface_num = 0; do_all = true; } action = 0; switch( token[0] ) { case '+': action = FLAG_ADD; token++; break; case '-': action = FLAG_CLEAR; token++; break; default: warning( "Entity::SurfaceModelEvent", "First character is not '+' or '-', assuming '+' for entity %s (%d), model %s\n", targetname.c_str(), entnum, model.c_str() ); action = FLAG_ADD; break; } // // WARNING: please change the Event decleration, // to match this function, if flags are added or // deleted the event must be updated. // if (!stricmp( token, "skin1")) { mask = MDL_SURFACE_SKINOFFSET_BIT0; } else if (!strcmpi (token, "skin2")) { mask = MDL_SURFACE_SKINOFFSET_BIT1; } else if (!strcmpi (token, "nodraw")) { mask = MDL_SURFACE_NODRAW; } else if (!strcmpi (token, "crossfade")) { mask = MDL_SURFACE_CROSSFADE_SKINS; } else { mask = 0; warning( "SurfaceCommand", "Unknown token %s. for entity %s (%d), model %s", token, targetname.c_str(), entnum, model.c_str() ); action = FLAG_IGNORE; } for( ; surface_num < numsurfaces ; surface_num++ ) { if ( mult ) { current_surface_name = gi.Surface_NumToName( edict->s.modelindex, surface_num ); if ( Q_stricmpn( current_surface_name, surf_name, strlen( surf_name ) - 1) != 0 ) continue; } switch (action) { case FLAG_ADD: edict->s.surfaces[ surface_num ] |= mask; break; case FLAG_CLEAR: edict->s.surfaces[ surface_num ] &= ~mask; break; case FLAG_IGNORE: break; } if ( !do_all && !mult ) break; } } void Entity::SurfaceModelEvent ( Event *ev ) { const char * surf_name; const char * token; int i; surf_name = ev->GetString( 1 ); for ( i = 2; i <= ev->NumArgs() ; i++ ) { token = ev->GetString( i ); SurfaceCommand( surf_name, token ); } } void Entity::AttachEvent ( Event * ev ) { Entity * parent; const char * bone; int tagnum; qboolean use_angles = true; Vector offset; Vector angles_offset; parent = ev->GetEntity( 1 ); bone = ev->GetString( 2 ); if ( ev->NumArgs() > 2 ) use_angles = ev->GetInteger( 3 ); if ( ev->NumArgs() > 3 ) offset = ev->GetVector( 4 ); if ( ev->NumArgs() > 4 ) angles_offset = ev->GetVector( 5 ); if ( !parent ) return; tagnum = gi.Tag_NumForName( parent->edict->s.modelindex, bone ); if ( tagnum >= 0 ) { attach( parent->entnum, tagnum, use_angles, offset, angles_offset ); } else { warning( "AttachEvent", "Tag %s not found", bone ); } } void Entity::AttachModelEvent ( Event * ev ) { Entity * obj; const char * bone; str modelname; int tagnum; float remove_time,fade_time,fade_delay; Vector offset; Vector angles_offset; qboolean use_angles = false; obj = new Entity( ENTITY_CREATE_FLAG_ANIMATE ); obj->bind_info = CreateBindInfo(); modelname = ev->GetString( 1 ); bone = ev->GetString( 2 ); if ( ev->NumArgs() > 2 ) { obj->setScale( ev->GetFloat( 3 ) ); } if ( ev->NumArgs() > 3 ) { obj->SetTargetName( ev->GetString( 4 ) ); } if ( ev->NumArgs() > 4 ) obj->bind_info->detach_at_death = ev->GetInteger( 5 ); if ( ev->NumArgs() > 5 ) { remove_time = ev->GetFloat( 6 ); if ( remove_time > 0.0f ) { Event *remove_event = new Event( EV_Remove ); obj->PostEvent( remove_event, remove_time ); } } if ( ev->NumArgs() > 6 ) { Event *fade_event; fade_time = ev->GetFloat( 7 ); if ( fade_time > 0.0f ) { obj->setAlpha( 0.0f ); fade_event = new Event( EV_FadeIn ); fade_event->AddFloat( fade_time ); obj->PostEvent( fade_event, 0.0f ); } } if ( ev->NumArgs() > 7 ) { Event *fade_event; fade_delay = ev->GetFloat( 8 ); if ( fade_delay != -1.0f ) { if ( ev->NumArgs() > 8 ) fade_time = ev->GetFloat( 9 ); else fade_time = 0.0f; fade_event = new Event( EV_Fade ); if ( fade_time > 0.0f ) fade_event->AddFloat( fade_time ); obj->PostEvent( fade_event, fade_delay ); } } if ( ev->NumArgs() > 9 ) offset = ev->GetVector( 10 ); if ( ev->NumArgs() > 10 ) { angles_offset = ev->GetVector( 11 ); use_angles = false; } obj->setModel( modelname ); if ( !obj->animate ) { Animate *newAnimate = 0; newAnimate = new Animate; if ( newAnimate ) obj->animate = newAnimate; } int anim_num = gi.Anim_Random ( obj->edict->s.modelindex, "idle" ); if ( anim_num != -1 && obj->animate ) { obj->animate->NewAnim( anim_num ); } tagnum = gi.Tag_NumForName( edict->s.modelindex, bone ); if ( tagnum >= 0 ) { if ( !obj->attach( this->entnum, tagnum, use_angles, offset, angles_offset ) ) { //warning( "AttachModelEvent", "Could not attach model %s", modelname.c_str() ); delete obj; return; } } else { warning( "AttachModelEvent", "Tag %s not found", bone ); } } void Entity::RemoveAttachedModelEvent ( Event *ev ) { const char *tag_name; int tag_num; int num; int i; Entity *ent; float fade_rate = 0; Event *fade_event; str model_name; if ( bind_info ) { tag_name = ev->GetString( 1 ); tag_num = gi.Tag_NumForName( edict->s.modelindex, tag_name ); if ( ev->NumArgs() > 1 ) fade_rate = ev->GetFloat( 2 ); if ( ev->NumArgs() > 2 ) model_name = ev->GetString( 3 ); if ( tag_num >= 0 ) { num = bind_info->numchildren; for ( i = 0 ; (i < MAX_MODEL_CHILDREN) && num ; i++ ) { if ( bind_info->children[i] == ENTITYNUM_NONE ) continue; ent = ( Entity * )G_GetEntity( bind_info->children[i] ); if ( ent && ent->edict->s.tag_num == tag_num ) { if ( !model_name.length() || ( model_name == ent->model ) ) { if ( fade_rate ) { fade_event = new Event( EV_Fade ); fade_event->AddFloat( fade_rate ); fade_event->AddFloat( 0.0f ); ent->PostEvent( fade_event, 0.0f ); } ent->PostEvent( EV_Remove, fade_rate ); } } num--; } } } } void Entity::removeAttachedModelByTargetname( Event *ev ) { str targetNameToRemove; targetNameToRemove = ev->GetString( 1 ); removeAttachedModelByTargetname( targetNameToRemove ); } void Entity::removeAttachedModelByTargetname( const str &targetNameToRemove ) { int num; int i; Entity *ent; if ( bind_info ) { num = bind_info->numchildren; for ( i = 0 ; (i < MAX_MODEL_CHILDREN) && num ; i++ ) { if ( bind_info->children[i] == ENTITYNUM_NONE ) continue; ent = ( Entity * )G_GetEntity( bind_info->children[i] ); if ( ent && stricmp( ent->targetname, targetNameToRemove.c_str() ) == 0 ) { ent->PostEvent( EV_Remove, 0.0f ); } num--; } } } void Entity::DetachEvent ( Event * ev ) { if ( edict->s.parent == ENTITYNUM_NONE ) { return; } detach(); } void Entity::TakeDamageEvent ( Event * ev ) { takedamage = DAMAGE_YES; } void Entity::NoDamageEvent ( Event * ev ) { takedamage = DAMAGE_NO; } void Entity::Gravity ( Event *ev ) { gravity = ev->GetFloat( 1 ); } void Entity::UseBoundingBoxEvent ( Event *ev ) { edict->svflags |= SVF_USEBBOX; } void Entity::HurtEvent ( Event *ev ) { Vector normal; float dmg; int means_of_death; Vector direction; if ( ev->NumArgs() < 1 ) { dmg = 50.0f; } else { dmg = ev->GetFloat( 1 ); } if ( ev->NumArgs() > 1 ) means_of_death = MOD_NameToNum( ev->GetString( 2 ) ); else means_of_death = MOD_CRUSH; if ( ev->NumArgs() > 2 ) { direction = ev->GetVector( 3 ); direction.normalize(); } else { direction = vec_zero; } normal = Vector( orientation[ 0 ] ); Damage( world, world, dmg, centroid, direction, normal, (int)dmg, 0, means_of_death ); } void Entity::IfSkillEvent ( Event *ev ) { float skilllevel; skilllevel = ev->GetFloat( 1 ); if ( skill->value == skilllevel ) { int argc; int numargs; Event *event; int i; numargs = ev->NumArgs(); argc = numargs - 2 + 1; event = new Event( ev->GetToken( 2 ) ); for( i = 1; i < argc; i++ ) { event->AddToken( ev->GetToken( 2 + i ) ); } ProcessEvent( event ); } } void Entity::Censor ( Event *ev ) { Vector delta; float oldsize; float newsize; if ( com_blood->integer ) return; oldsize = size.length(); setSolidType( SOLID_NOT ); setModel( "censored.tik" ); gi.CalculateBounds( edict->s.modelindex, 1.0f, mins, maxs ); delta = maxs - mins; newsize = delta.length(); edict->s.scale = oldsize / newsize; mins *= edict->s.scale; maxs *= edict->s.scale; setSize( mins, maxs ); setOrigin(); } void Entity::StationaryEvent ( Event *ev ) { setMoveType( MOVETYPE_STATIONARY ); } void Entity::Explosion ( Event *ev ) { str expmodel; str tag_name; //orientation_t orient; Vector explosion_origin; expmodel = ev->GetString( 1 ); explosion_origin = origin; if ( ev->NumArgs() > 1 ) { tag_name = ev->GetString( 2 ); //if ( GetRawTag( tag_name.c_str(), &orient, legs ) ) // VectorAdd( orient.origin, origin, explosion_origin ); GetTag( tag_name.c_str(), &explosion_origin ); } ExplosionAttack( explosion_origin, this, expmodel ); } //---------------------------------------------------------------- // Name: DoRadiusDamage // Class: Entity // // Description: Does radius damage from entity origin, // optionally re-posting itself to some future time // // Parameters: Event *ev // Event params: (1) float damage // (2) const char * means of death string // (3) float radius // (4) float knockback // (5) (optional) bool constant damage over distance // (6) (optional) float forward re-posting time // // Returns: NULL //---------------------------------------------------------------- void Entity::DoRadiusDamage( Event *ev ) { Entity *owner = this; float damage = ev->GetFloat(1); const char *modstring = ev->GetString(2); float radius = ev->GetFloat(3); float knockback = ev->GetFloat(4); bool constant_damage = false; float damageDone; if (ev->NumArgs() >= 5) // constant damage info is supplied constant_damage = ev->GetBoolean(5); if ( this->isSubclassOf( Projectile ) ) { Projectile *projectile = (Projectile *)this; owner = projectile->getOwner(); } if ( owner && owner->isSubclassOf( Player ) ) { Player *player = (Player *)owner; damage = player->getDamageDone( damage, MOD_NameToNum( modstring ), false ); } damageDone = RadiusDamage( this, owner, damage, this, MOD_NameToNum(modstring), radius, knockback, constant_damage ); if ( damageDone && this->isSubclassOf( Projectile ) ) { Projectile *projectile = (Projectile *)this; projectile->didDamage(); } if (ev->NumArgs() == 6) { // if repost time is set, re-post this event float postTime = ev->GetFloat(6); if (postTime >= FRAMETIME) { Event *ev2 = new Event(ev); PostEvent(ev2,postTime); } } } void Entity::SelfDetonate ( Event *ev ) { if ( explosionModel.length() == 0 ) explosionModel = "fx/fx-sml-exp.tik"; ExplosionAttack( origin , this , explosionModel ); } void Entity::Shader ( Event *ev ) { const char * token; if ( gi.IsModel( edict->s.modelindex ) ) { ev->Error( "shader event being called on TIKI model\n" ); } // // get sub shader command // token = ev->GetString( 1 ); // // WARNING: please change the Event decleration, // to match this function, if flags are added or // deleted the event must be updated. // if (!strcmpi( token, "translation")) { float x, y; x = ev->GetFloat( 2 ); y = ev->GetFloat( 3 ); TRANSLATION_TO_PKT( (int)x, edict->s.tag_num ); TRANSLATION_TO_PKT( (int)y, edict->s.skinNum ); } else if (!strcmpi( token, "offset")) { float x, y; x = ev->GetFloat( 2 ); y = ev->GetFloat( 3 ); OFFSET_TO_PKT( x, edict->s.tag_num ); OFFSET_TO_PKT( y, edict->s.skinNum ); } else if (!strcmpi (token, "rotation")) { float rot; rot = ev->GetFloat( 2 ); ROTATE_TO_PKT( rot, edict->s.tag_num ); } else if (!strcmpi (token, "frame")) { edict->s.frame = ev->GetInteger( 2 ); } else if (!strcmpi (token, "wavebase")) { float base; base = ev->GetFloat( 2 ); BASE_TO_PKT( base, edict->s.surfaces[ 0 ] ); } else if (!strcmpi (token, "waveamp")) { float amp; amp = ev->GetFloat( 2 ); AMPLITUDE_TO_PKT( amp, edict->s.surfaces[ 1 ] ); } else if (!strcmpi (token, "wavephase")) { float phase; phase = ev->GetFloat( 2 ); PHASE_TO_PKT( phase, edict->s.surfaces[ 2 ] ); } else if (!strcmpi (token, "wavefreq")) { float freq; freq = ev->GetFloat( 2 ); FREQUENCY_TO_PKT( freq, edict->s.surfaces[ 3 ] ); } } void Entity::DropToFloorEvent ( Event *ev ) { float range; if ( ev->NumArgs() > 0 ) { range = ev->GetFloat( 1 ); } else { range = WORLD_SIZE; } if ( !droptofloor( range ) ) { } } //************************************************************************* // // BIND code // //************************************************************************* qboolean Entity::isBoundTo ( const Entity *master ) { Entity *ent; if ( bind_info ) { for( ent = bind_info->bindmaster; ent != NULL; ent = ent->bind_info->bindmaster ) { if ( ent == master ) { return true; } } } return false; } void Entity::bind ( Entity *master, qboolean use_my_angles ) { float mat[ 3 ][ 3 ]; float local[ 3 ][ 3 ]; Vector ang; assert( master ); if ( !master ) { warning( "bind", "Null master entity" ); return; } if ( master == this ) { warning( "bind", "Trying to bind to oneself." ); return; } if ( !bind_info ) bind_info = CreateBindInfo(); // unbind myself from my master unbind(); bind_info->bindmaster = master; edict->s.bindparent = master->entnum; bind_info->bind_use_my_angles = use_my_angles; // We are now separated from our previous team and are either // an individual, or have a team of our own. Now we can join // the new bindmaster's team. Bindmaster must be set before // joining the team, or we will be placed in the wrong position // on the team. joinTeam( master ); // calculate local angles TransposeMatrix( bind_info->bindmaster->orientation, mat ); R_ConcatRotations( mat, orientation, local ); MatrixToEulerAngles( local, ang ); setAngles( ang ); setOrigin( getParentVector( GetLocalOrigin() - bind_info->bindmaster->origin ) ); return; } void Entity::unbind ( void ) { Entity *prev; Entity *next; Entity *last; Entity *ent; if ( !bind_info || !bind_info->bindmaster ) return; //bindmaster = NULL; // Check this GAMEFIX - should it be origin? SetLocalOrigin( edict->s.origin ); localangles = Vector( edict->s.angles ); if ( !bind_info->teammaster ) { bind_info->bindmaster = NULL; edict->s.bindparent = ENTITYNUM_NONE; //Teammaster already has been freed return; } // We're still part of a team, so that means I have to extricate myself // and any entities that are bound to me from the old team. // Find the node previous to me in the team prev = bind_info->teammaster; for( ent = bind_info->teammaster->bind_info->teamchain; ent && ( ent != this ); ent = ent->bind_info->teamchain ) { prev = ent; } // If ent is not pointing to me, then something is very wrong. assert( ent ); if ( !ent ) { error( "unbind", "corrupt team chain\n" ); } // Find the last node in my team that is bound to me. // Also find the first node not bound to me, if one exists. last = this; for( next = bind_info->teamchain; next != NULL; next = next->bind_info->teamchain ) { if ( !next->isBoundTo( this ) ) { break; } // Tell them I'm now the teammaster next->bind_info->teammaster = this; last = next; } // disconnect the last member of our team from the old team last->bind_info->teamchain = NULL; // connect up the previous member of the old team to the node that // follow the last node bound to me (if one exists). if ( bind_info->teammaster != this ) { prev->bind_info->teamchain = next; if ( !next && ( bind_info->teammaster == prev ) ) { prev->bind_info->teammaster = NULL; } } else if ( next ) { // If we were the teammaster, then the nodes that were not bound to me are now // a disconnected chain. Make them into their own team. for( ent = next; ent->bind_info->teamchain != NULL; ent = ent->bind_info->teamchain ) { ent->bind_info->teammaster = next; } next->bind_info->teammaster = next; next->flags &= ~FL_TEAMSLAVE; } // If we don't have anyone on our team, then clear the team variables. if ( bind_info->teamchain ) { // make myself my own team bind_info->teammaster = this; } else { // no longer a team bind_info->teammaster = NULL; } flags &= ~FL_TEAMSLAVE; bind_info->bindmaster = NULL; edict->s.bindparent = ENTITYNUM_NONE; } void Entity::EventUnbind ( Event *ev ) { unbind(); } void Entity::BindEvent ( Event *ev ) { Entity *ent; ent = ev->GetEntity( 1 ); if ( ent ) { bind( ent ); } } Vector Entity::getParentVector ( const Vector &vec ) { Vector pos; if ( !bind_info || !bind_info->bindmaster ) { return vec; } pos[ 0 ] = vec * bind_info->bindmaster->orientation[ 0 ]; pos[ 1 ] = vec * bind_info->bindmaster->orientation[ 1 ]; pos[ 2 ] = vec * bind_info->bindmaster->orientation[ 2 ]; return pos; } // // Team methods // void Entity::joinTeam ( Entity *teammember ) { Entity *ent; Entity *master; Entity *prev; Entity *next; if ( !bind_info ) bind_info = CreateBindInfo(); if ( bind_info->teammaster && ( bind_info->teammaster != this ) ) { quitTeam(); } assert( teammember ); if ( !teammember ) { warning( "joinTeam", "Null entity" ); return; } if ( !teammember->bind_info ) teammember->bind_info = CreateBindInfo(); master = teammember->bind_info->teammaster; if ( !master ) { master = teammember; teammember->bind_info->teammaster = teammember; teammember->bind_info->teamchain = this; // make anyone who's bound to me part of the new team for( ent = bind_info->teamchain; ent != NULL; ent = ent->bind_info->teamchain ) { ent->bind_info->teammaster = master; } } else { // skip past the chain members bound to the entity we're teaming up with prev = teammember; next = teammember->bind_info->teamchain; if ( bind_info->bindmaster ) { // if we have a bindmaster, joing after any entities bound to the entity // we're joining while( next && (( Entity *)next)->isBoundTo( teammember ) ) { prev = next; next = next->bind_info->teamchain; } } else { // if we're not bound to someone, then put us at the end of the team while( next ) { prev = next; next = next->bind_info->teamchain; } } // make anyone who's bound to me part of the new team and // also find the last member of my team for( ent = this; ent->bind_info->teamchain != NULL; ent = ent->bind_info->teamchain ) { ent->bind_info->teamchain->bind_info->teammaster = master; } prev->bind_info->teamchain = this; ent->bind_info->teamchain = next; } bind_info->teammaster = master; flags |= FL_TEAMSLAVE; } void Entity::quitTeam ( void ) { Entity *ent; if ( !bind_info || !bind_info->teammaster ) { return; } if ( bind_info->teammaster == this ) { if ( !bind_info->teamchain->bind_info->teamchain ) { bind_info->teamchain->bind_info->teammaster = NULL; } else { // make next teammate the teammaster for( ent = bind_info->teamchain; ent; ent = ent->bind_info->teamchain ) { ent->bind_info->teammaster = bind_info->teamchain; } } bind_info->teamchain->flags &= ~FL_TEAMSLAVE; } else { assert( flags & FL_TEAMSLAVE ); assert( bind_info->teammaster->bind_info->teamchain ); ent = bind_info->teammaster; while( ent->bind_info->teamchain != this ) { // this should never happen assert( ent->bind_info->teamchain ); ent = ent->bind_info->teamchain; } ent->bind_info->teamchain = bind_info->teamchain; if ( !bind_info->teammaster->bind_info->teamchain ) { bind_info->teammaster->bind_info->teammaster = NULL; } } bind_info->teammaster = NULL; bind_info->teamchain = NULL; flags &= ~FL_TEAMSLAVE; } void Entity::EventQuitTeam ( Event *ev ) { quitTeam(); } void Entity::JoinTeam ( Event *ev ) { Entity *ent; ent = ev->GetEntity( 1 ); if ( ent ) { joinTeam( ent ); } } void Entity::AddToSoundManager ( Event *ev ) { SoundMan.AddEntity( this ); } inline qboolean Entity::HitSky ( const trace_t *trace ) { assert( trace ); if ( trace->surfaceFlags & SURF_SKY ) { return true; } return false; } qboolean Entity::HitSky ( void ) { return HitSky( &level.impact_trace ); } void Entity::SetAngleEvent ( Event *ev ) { Vector movedir; movedir = G_GetMovedir( ev->GetFloat( 1 ) ); setAngles( movedir.toAngles() ); } void Entity::NoLerpThisFrame( void ) { gentity_t *checkEdict; edict->s.eFlags ^= EF_TELEPORT_BIT; // Make sure no one is standing on us for( checkEdict = active_edicts.next ; checkEdict != &active_edicts ; checkEdict = checkEdict->next ) { assert( checkEdict ); assert( checkEdict->inuse ); if ( checkEdict->entity && checkEdict->entity->groundentity == edict ) { checkEdict->entity->groundentity = NULL; } } } void Entity::Postthink ( void ) { } //----------------------------------------------------- // // Name: TouchTriggersEvent // Class: Entity // // Description: Specifies the entity can touch triggers by setting // the touch triggers flag. // // Parameters: ev - the event that specifies whether to turn // touch triggers on or off. If no param is specified, // touchtriggers is true. // // Returns: None //----------------------------------------------------- void Entity::TouchTriggersEvent(Event *ev) { if ( ( ev->NumArgs() == 0 ) || ( ev->GetBoolean( 1 ) == true ) ) { flags |= FL_TOUCH_TRIGGERS; turnThinkOn(); } else { flags &= ~FL_TOUCH_TRIGGERS; } } void Entity::IncreaseShotCount ( Event *ev ) { int parent_ent_num = edict->s.parent; Entity *parent = ( Entity * )G_GetEntity( parent_ent_num ); if ( !parent->isSubclassOf(Actor) ) return; Actor* act; act = (Actor*)parent; act->shotsFired++; } void Entity::DeathSinkStart ( Event *ev ) { float time; // Stop the sink when we can't be seen anymore if ( ( maxs[2] >= 0.0f ) && ( maxs[2] < 200.0f ) ) time = maxs[2] / 20.0f; else time = 1.0f; PostEvent( EV_Remove, time ); // Start the sinking ProcessEvent( EV_DeathSink ); } void Entity::DeathSink ( Event *ev ) { // Sink just a little origin[2] -= 1.0f; setOrigin( origin ); // Make sure the sink happens again next frame PostEvent( EV_DeathSink, FRAMETIME ); } void Entity::LookAtMe ( Event *ev ) { if ( ev->NumArgs() > 0 ) look_at_me = ev->GetBoolean( 1 ); else look_at_me = true; } void Entity::ProjectilesCanStickToMe( Event *ev ) { projectilesCanStickToMe = ev->GetBoolean( 1 ); } void Entity::VelocityModified ( void ) { } //-------------------------------------------------------------- // // Name: DetachAllChildren // Class: Entity // // Description: Detaches all attached models to this entity // // Parameters: Event *ev // // Returns: None // //-------------------------------------------------------------- void Entity::DetachAllChildren(Event *ev) { if ( !bind_info ) // Abort if bind_info is NULL for some reason return; for ( int i=0; ichildren[i] == ENTITYNUM_NONE ) continue; Entity *ent = ( Entity * )G_GetEntity( bind_info->children[i] ); if ( ent ) ent->PostEvent( EV_Remove, 0.0f ); } } inline void Entity::Archive( Archiver &arc ) { int tempInt; qboolean is_archived; qboolean true_bool = true; qboolean false_bool = false; Listener::Archive( arc ); arc.ArchiveVector( &_localOrigin ); _lastTouchedList.Archive( arc ); arc.ArchiveBool( &_fulltrace ); arc.ArchiveInteger( &_groupID ); // Don't archive entnum, it will be set elsewhere //int entnum; G_ArchiveEdict( arc, edict ); arc.ArchiveString(&_archetype); if(arc.Loading()) { edict->s.archeTypeIndex = gi.archetypeindex(_archetype); } arc.ArchiveBool( &_missionObjective ); arc.ArchiveString( &_targetPos ); arc.ArchiveBool( &_networkDetail ); if(arc.Loading()) { edict->s.missionObjective = _missionObjective; if(_missionObjective == true) G_AddEntityToExtraList(entnum); } // Don't archive client //gclient_t *client; arc.ArchiveInteger( &spawnflags ); arc.ArchiveString( &model ); if ( arc.Loading() && model.length() ) setModel( model.c_str() ); arc.ArchiveVector( &total_delta ); arc.ArchiveVector( &mins ); arc.ArchiveVector( &maxs ); arc.ArchiveVector( &absmin ); arc.ArchiveVector( &absmax ); arc.ArchiveVector( ¢roid ); arc.ArchiveVector( &velocity ); arc.ArchiveVector( &avelocity ); arc.ArchiveVector( &origin ); arc.ArchiveVector( &angles ); arc.ArchiveVector( &size ); arc.ArchiveInteger( &movetype ); arc.ArchiveInteger( &mass ); arc.ArchiveFloat( &gravity ); arc.ArchiveRaw( orientation, sizeof( orientation ) ); arc.ArchiveVector( &localangles ); if ( arc.Saving() ) { if ( groundentity ) { tempInt = groundentity - g_entities; } else { tempInt = -1; } } arc.ArchiveInteger( &tempInt ); if ( arc.Loading() ) { if ( tempInt == -1 ) { groundentity = NULL; } else { groundentity = &g_entities[ tempInt ]; } } arc.ArchiveRaw( &groundplane, sizeof( groundplane ) ); arc.ArchiveInteger( &groundcontents ); arc.ArchiveInteger( &numsurfaces ); arc.ArchiveFloat( &lightRadius ); arc.ArchiveString( &target ); arc.ArchiveString( &targetname ); arc.ArchiveString( &killtarget ); if ( arc.Loading() ) { // Don't set this here, they are handled in the world //SetTargetName( targetname.c_str() ); // Not needed //SetTarget( target.c_str() ); } arc.ArchiveFloat( &health ); arc.ArchiveFloat( &max_health ); arc.ArchiveInteger( &deadflag ); arc.ArchiveInteger( &flags ); arc.ArchiveInteger( &watertype ); arc.ArchiveInteger( &waterlevel ); ArchiveEnum( takedamage, damage_t ); arc.ArchiveInteger( &damage_type ); arc.ArchiveBoolean( &look_at_me ); arc.ArchiveBool( &projectilesCanStickToMe ); arc.ArchiveString( &explosionModel ); entityVars.Archive( arc ); arc.ArchiveUnsigned( &_affectingViewModes ); arc.ArchiveVector( &watch_offset ); // Pluggable modules if ( arc.Loading() ) { arc.ArchiveBoolean( &is_archived ); if ( is_archived ) { animate = new Animate( this ); arc.ArchiveObject( animate ); } arc.ArchiveBoolean( &is_archived ); if ( is_archived ) { mover = new Mover( this ); arc.ArchiveObject( mover ); } arc.ArchiveBoolean( &is_archived ); if ( is_archived ) { bind_info = CreateBindInfo(); bind_info->Archive( arc ); } arc.ArchiveBoolean( &is_archived ); if ( is_archived ) { morph_info = CreateMorphInfo(); morph_info->Archive( arc ); } } else { if ( animate ) { arc.ArchiveBoolean( &true_bool ); arc.ArchiveObject( animate ); } else { arc.ArchiveBoolean( &false_bool ); } if ( mover ) { arc.ArchiveBoolean( &true_bool ); arc.ArchiveObject( mover ); } else { arc.ArchiveBoolean( &false_bool ); } if ( bind_info ) { arc.ArchiveBoolean( &true_bool ); bind_info->Archive( arc ); } else { arc.ArchiveBoolean( &false_bool ); } if ( morph_info ) { arc.ArchiveBoolean( &true_bool ); morph_info->Archive( arc ); } else { arc.ArchiveBoolean( &false_bool ); } } if ( arc.Saving() ) { if ( ObjectProgram ) { arc.ArchiveBoolean( &true_bool ); ObjectProgram->Archive( arc ); } else { arc.ArchiveBoolean( &false_bool ); } } else { arc.ArchiveBoolean( &is_archived ); if ( is_archived ) ObjectProgram->Archive( arc ); else ObjectProgram = NULL; } // Archiving of the Damage Modification System if ( arc.Saving() ) { bool exists; int type; if ( damageModSystem ) { exists = true; arc.ArchiveBool( &exists ); int numobj = damageModSystem->getModifierList().NumObjects(); arc.ArchiveInteger( &numobj ); for ( tempInt = 1; tempInt <= numobj; tempInt++ ) { DamageModifier *dmod = damageModSystem->getModifierList().ObjectAt ( tempInt ); if ( dmod ) { type = (int)dmod->getType(); arc.ArchiveInteger( &type ); dmod->Archive(arc); } else { // How did a NULL get in the container?? assert( 0 ); } } } else { exists = false; arc.ArchiveBool( &exists ); } } else // Loading { bool exists; int numobj, type; arc.ArchiveBool( &exists ); if ( exists ) { damageModSystem = new DamageModificationSystem(); arc.ArchiveInteger( &numobj ); for ( tempInt = 1; tempInt <= numobj; tempInt++ ) { arc.ArchiveInteger( &type ); DamageModifier *newMod = 0; switch ( type ) { case TIKI_NAME: newMod = new DamageModifierTikiName(); break; case NAME: newMod = new DamageModifierName(); break; case GROUP: newMod = new DamageModifierGroup(); break; case ACTOR_TYPE: newMod = new DamageModifierActorType(); break; case TARGETNAME: newMod = new DamageModifierTargetName(); break; case DAMAGE_TYPE: newMod = new DamageModifierDamageType(); break; } if ( newMod ) { newMod->Archive(arc); damageModSystem->addDamageModifier( newMod ); } } } } // Archive the useData member if it exists if ( arc.Saving() ) { bool exists = false; if ( useData ) { exists = true; arc.ArchiveBool( &exists ); useData->Archive(arc); } else arc.ArchiveBool( &exists ); } else // Loading { bool exists; arc.ArchiveBool( &exists ); if ( exists ) { useData = new UseData(); useData->Archive(arc); } } } // Animate interface inline int Entity::CurrentFrame ( bodypart_t part ) { if ( animate ) return animate->CurrentFrame( part ); else return 0; } inline int Entity::CurrentAnim ( bodypart_t part ) { if ( animate ) return animate->CurrentAnim( part ); else return 0; } void Entity::PassToAnimate ( Event *ev ) { Event *new_event; if ( !animate ) animate = new Animate( this ); new_event = new Event( ev ); animate->ProcessEvent( new_event ); } void Entity::SetObjectProgram ( Event *ev ) { ObjectProgram = new Program; if ( !ObjectProgram ) return; ObjectProgram->Load( ev->GetString( 1 ) ); //CThread *gamescript = 0; //gamescript = Director.CreateThread( "obj_main" , ObjectProgram ); //gamescript->DelayedStart( 0 ); } void Entity::SetWatchOffset( Event *ev ) { watch_offset = ev->GetVector( 1 ); } void Entity::ExecuteProgram ( Event *ev ) { float exeTime = 0; if ( !ObjectProgram ) return; if ( ev->NumArgs() > 0 ) exeTime = ev->GetFloat( 1 ); CThread *gamescript = 0; gamescript = Director.CreateThread( "obj_main" , ObjectProgram ); gamescript->DelayedStart( exeTime ); } // BindInfo interface inline BindInfo *CreateBindInfo( void ) { BindInfo *new_bind_info; new_bind_info = new BindInfo; if ( !new_bind_info ) gi.Error( ERR_DROP, "Couldn't alloc BindInfo" ); return new_bind_info; } // MorphInfo interface void Entity::MorphEvent ( Event *ev ) { str morph_target_name; int morph_index; float final_percent = 100; float morph_time = 0.5; qboolean return_to_zero = false; int i; qboolean override = true; int morph_channel = MORPH_CHAN_NONE; qboolean channel_being_used; qboolean override_all = false; qboolean matching_channel; qboolean unmorph = false; // Get parms morph_target_name = ev->GetString( 1 ); if ( ev->NumArgs() > 1 ) final_percent = ev->GetFloat( 2 ); if ( ev->NumArgs() > 2 ) morph_time = ev->GetFloat( 3 ); if ( ev->NumArgs() > 3 ) return_to_zero = ev->GetBoolean( 4 ); if ( ev->NumArgs() > 4 ) override = ev->GetBoolean( 5 ); if ( ev->NumArgs() > 5 ) morph_channel = ev->GetInteger( 6 ); // See if this is an expression if ( strnicmp( morph_target_name.c_str(), "exp_", 4 ) == 0 ) { // Process this expression dtikimorphtarget_t *morph_targets; Event *new_event; const char *morph_name; int number_of_morph_targets; morph_targets = gi.GetExpression( edict->s.modelindex, morph_target_name.c_str(), &number_of_morph_targets ); if ( morph_targets ) { for ( i = 0 ; i < number_of_morph_targets ; i++ ) { morph_name = gi.Morph_NameForNum( edict->s.modelindex, morph_targets[ i ].morph_index ); if ( morph_name ) { new_event = new Event( EV_Morph ); new_event->AddString( morph_name ); new_event->AddFloat( morph_targets[ i ].percent ); new_event->AddFloat( morph_time ); new_event->AddInteger( return_to_zero ); new_event->AddInteger( override ); new_event->AddInteger( morph_channel ); ProcessEvent( new_event ); } } return; } } // Find this morph target morph_index = gi.Morph_NumForName( edict->s.modelindex, morph_target_name.c_str() ); // Check unmorphing stuff if ( ( stricmp( morph_target_name.c_str(), "morph_base" ) == 0 ) || ( stricmp( morph_target_name.c_str(), "morph_mouth_base" ) == 0 ) || ( stricmp( morph_target_name.c_str(), "morph_brows_base" ) == 0 ) || ( stricmp( morph_target_name.c_str(), "morph_eyes_base" ) == 0 ) ) unmorph = true; if ( morph_index == -1 && !unmorph ) return; if ( morph_channel == MORPH_CHAN_NONE ) morph_channel = GetMorphChannel( morph_target_name.c_str() ); // Make sure we have a morph controller block if ( !morph_info ) morph_info = CreateMorphInfo(); // Deal with current morphs channel_being_used = false; if ( unmorph && ( morph_channel == MORPH_CHAN_NONE ) ) override_all = true; for( i = 0 ; i < NUM_MORPH_CONTROLLERS ; i++ ) { if ( morph_info->controllers[ i ].index != -1 ) { // See if this is a matching channel if ( MorphChannelMatches( morph_channel, morph_info->controllers[ i ].channel ) ) matching_channel = true; else matching_channel = false; if ( override_all || ( matching_channel && override ) ) { if ( morph_info->controllers[ i ].final_percent != 0 ) { // Override this morph morph_info->controllers[ i ].speed = ( morph_info->controllers[ i ].current_percent * FRAMETIME ) / morph_time; morph_info->controllers[ i ].final_percent = 0; StartMorphController(); } } else if ( matching_channel ) { channel_being_used = true; } } } // If just unmorphing stuff, stop here if ( unmorph ) return; // If not overriding and the channel is already being used, stop here if ( !override && channel_being_used ) return; // Find a free morph controller & add this morph for( i = 0 ; i < NUM_MORPH_CONTROLLERS ; i++ ) { if ( morph_info->controllers[ i ].index == -1 ) { morph_info->controllers[ i ].index = morph_index; morph_info->controllers[ i ].current_percent = 0; morph_info->controllers[ i ].final_percent = final_percent; morph_info->controllers[ i ].speed = (final_percent * FRAMETIME ) / morph_time; morph_info->controllers[ i ].return_to_zero = return_to_zero; morph_info->controllers[ i ].channel = morph_channel; StartMorphController(); break; } } } void Entity::UnmorphEvent ( Event *ev ) { str morph_target_name; int morph_index; int i; float morph_time = 0.5; morph_target_name = ev->GetString( 1 ); if ( ev->NumArgs() > 2 ) morph_time = ev->GetFloat( 3 ); // See if this is an expression if ( strnicmp( morph_target_name.c_str(), "exp_", 4 ) == 0 ) { // Process this expression dtikimorphtarget_t *morph_targets; Event *new_event; const char *morph_name; int number_of_morph_targets; morph_targets = gi.GetExpression( edict->s.modelindex, morph_target_name.c_str(), &number_of_morph_targets ); if ( morph_targets ) { for ( i = 0 ; i < number_of_morph_targets ; i++ ) { morph_name = gi.Morph_NameForNum( edict->s.modelindex, morph_targets[ i ].morph_index ); if ( morph_name ) { new_event = new Event( EV_Unmorph ); new_event->AddString( morph_name ); new_event->AddFloat( morph_time ); ProcessEvent( new_event ); } } return; } } morph_index = gi.Morph_NumForName( edict->s.modelindex, morph_target_name.c_str() ); if ( morph_index == -1 || !morph_info ) return; // Find this morph controller for( i = 0 ; i < NUM_MORPH_CONTROLLERS ; i++ ) { if ( ( morph_info->controllers[ i ].index == morph_index ) && ( morph_info->controllers[ i ].final_percent != 0 ) ) { morph_info->controllers[ i ].speed = (morph_info->controllers[ i ].final_percent * FRAMETIME ) / morph_time; morph_info->controllers[ i ].final_percent = 0.0f; StartMorphController(); break; } } } void Entity::MorphControl ( Event *ev ) { int i; qboolean process_next_frame = false; morph_t *controller; for( i = 0 ; i < NUM_MORPH_CONTROLLERS ; i++ ) { controller = &morph_info->controllers[ i ]; if ( controller->index != -1 ) { // Lerp the morph percent if ( controller->current_percent != controller->final_percent ) { if ( controller->current_percent < controller->final_percent ) { controller->current_percent += controller->speed; if ( controller->current_percent > controller->final_percent ) controller->current_percent = controller->final_percent; } else { controller->current_percent -= controller->speed; if ( controller->current_percent < controller->final_percent ) controller->current_percent = controller->final_percent; } } if ( controller->current_percent == 0.0f ) { controller->index = -1; controller->current_percent = 0.0f; } // Return to zero if necessary if ( controller->current_percent == controller->final_percent && controller->return_to_zero ) controller->final_percent = 0.0f; if ( controller->current_percent != controller->final_percent ) process_next_frame = true; } // Copy to edict edict->s.morph_controllers[ i ].index = controller->index; edict->s.morph_controllers[ i ].percent = controller->current_percent / 100.0f; } if ( process_next_frame ) PostEvent( EV_MorphControl, FRAMETIME ); else morph_info->controller_on = false; } int Entity::GetMorphChannel ( const char *morph_name ) { int morph_channel; if ( stricmp( morph_name, "morph_a-i" ) == 0 ) morph_channel = MORPH_CHAN_MOUTH; else if ( stricmp( morph_name, "morph_c-t" ) == 0 ) morph_channel = MORPH_CHAN_MOUTH; else if ( stricmp( morph_name, "morph_e" ) == 0 ) morph_channel = MORPH_CHAN_MOUTH; else if ( stricmp( morph_name, "morph_f-v" ) == 0 ) morph_channel = MORPH_CHAN_MOUTH; else if ( stricmp( morph_name, "morph_l-th" ) == 0 ) morph_channel = MORPH_CHAN_MOUTH; else if ( stricmp( morph_name, "morph_m-b-p" ) == 0 ) morph_channel = MORPH_CHAN_MOUTH; else if ( stricmp( morph_name, "morph_o" ) == 0 ) morph_channel = MORPH_CHAN_MOUTH; else if ( stricmp( morph_name, "morph_q-w" ) == 0 ) morph_channel = MORPH_CHAN_MOUTH; else if ( stricmp( morph_name, "morph_u" ) == 0 ) morph_channel = MORPH_CHAN_MOUTH; else if ( stricmp( morph_name, "morph_frown" ) == 0 ) morph_channel = MORPH_CHAN_MOUTH; else if ( stricmp( morph_name, "morph_sneer-l" ) == 0 ) morph_channel = MORPH_CHAN_MOUTH; else if ( stricmp( morph_name, "morph_sneer-r" ) == 0 ) morph_channel = MORPH_CHAN_MOUTH; else if ( stricmp( morph_name, "morph_mouth_base" ) == 0 ) morph_channel = MORPH_CHAN_MOUTH; else if ( stricmp( morph_name, "morph_brows-up" ) == 0 ) morph_channel = MORPH_CHAN_BROW; else if ( stricmp( morph_name, "morph_brows_base" ) == 0 ) morph_channel = MORPH_CHAN_BROW; else if ( stricmp( morph_name, "morph_brow-ldn" ) == 0 ) morph_channel = MORPH_CHAN_LEFT_BROW; else if ( stricmp( morph_name, "morph_brow-rdn" ) == 0 ) morph_channel = MORPH_CHAN_RIGHT_BROW; else if ( stricmp( morph_name, "morph_lid-lshut" ) == 0 ) morph_channel = MORPH_CHAN_LEFT_LID; else if ( stricmp( morph_name, "morph_lid-rshut" ) == 0 ) morph_channel = MORPH_CHAN_RIGHT_LID; else if ( stricmp( morph_name, "morph_eyeshut" ) == 0 ) morph_channel = MORPH_CHAN_EYES; else if ( stricmp( morph_name, "morph_eyes_base" ) == 0 ) morph_channel = MORPH_CHAN_EYES; else morph_channel = MORPH_CHAN_NONE; return morph_channel; } void Entity::StartMorphController ( void ) { if ( !morph_info->controller_on ) { morph_info->controller_on = true; CancelEventsOfType( EV_MorphControl ); PostEvent( EV_MorphControl, FRAMETIME ); } } void Entity::SetAnimOnAttachedModel ( const str &AnimName, const str &TagName ) { int tag_num = gi.Tag_NumForName( this->edict->s.modelindex , TagName.c_str() ); if ( bind_info ) { Entity *attachment = 0; for ( int i = 0; i < MAX_MODEL_CHILDREN; i++ ) { //Check for valid entities if( bind_info->children[i] == ENTITYNUM_NONE ) continue; attachment = ( Entity * )G_GetEntity( bind_info->children[i] ); if ( attachment->edict->s.tag_num == tag_num ) { if ( !attachment->animate ) { attachment->animate = new Animate; } int anim_num = gi.Anim_Random ( attachment->edict->s.modelindex, AnimName.c_str() ); if ( anim_num != -1 ) { attachment->animate->NewAnim( anim_num ); } } } } } void Entity::SetAnimOnAttachedModel ( Event *ev ) { str attachmentAnim = ev->GetString( 1 ); str TagName = ev->GetString( 2 ); SetAnimOnAttachedModel( attachmentAnim , TagName ); } void Entity::SetCinematicAnim ( const str &AnimName ) { if (!animate) { animate = new Animate( this ); } int anim_num = gi.Anim_Random ( edict->s.modelindex, AnimName.c_str() ); if ( anim_num != -1 ) { animate->NewAnim( anim_num ); animate->SetAnimDoneEvent( new Event (EV_CinematicAnimDone) ); } gravity = 0.0f; edict->contents = CONTENTS_SETCLIP ; edict->clipmask = MASK_SETCLIP ; } void Entity::SetCinematicAnim ( Event *ev ) { str animName = ev->GetString( 1 ); SetCinematicAnim( animName); } void Entity::CinematicAnimDone ( void ) { gravity = 1.0f ; edict->contents &= ~CONTENTS_SETCLIP ; edict->clipmask = MASK_SOLID ; } void Entity::CinematicAnimDone ( Event *ev ) { CinematicAnimDone(); } void Entity::SetEntityExplosionModel ( Event *ev ) { explosionModel = ev->GetString( 1 ); } //---------------------------------------------------------------- // Name: ProjectileAtk // Class: Entity // // Description: launches a projectile either from the player // or from a tag // // Parameters: event ev contains projectile name and an // optional tag name // // Returns: None //---------------------------------------------------------------- void Entity::ProjectileAtk( Event *ev ) { str projectileName = ev->GetString( 1 ); Vector position; Vector direction; if ( ev->NumArgs() > 1 ) { // Projectile name, tagName str tagName( ev->GetString( 2 ) ); GetTag( tagName.c_str(), &position, &direction ); } else { // Projectile name, Player is the target position = origin; gentity_t *ed; Entity* enemy = NULL; for( int i = 0 ; i < game.maxclients; i++ ) { ed = &g_entities[ i ]; if ( !ed->inuse || !ed->entity ) continue; enemy = ed->entity; } if ( enemy ) { direction = enemy->origin - origin; direction.normalize(); } else { angles.AngleVectors( &direction, NULL, NULL ); } } ProjectileAttack( position, direction, this, projectileName.c_str(), 1.0f, 0.0f ); } //---------------------------------------------------------------- // Name: ProjectileAttackPoint // Class: Entity // // Description: launches a projectile towards the given point // // Parameters: event ev contains projectile name and entity name // // Returns: None //---------------------------------------------------------------- void Entity::ProjectileAttackPoint( Event *ev ) { const str projectileName( ev->GetString( 1 ) ); const Vector targetPosition( ev->GetVector( 2 ) ); Vector direction( targetPosition - origin ); direction.normalize(); Projectile *projectile = ProjectileAttack( origin, direction, this, projectileName.c_str(), 1.0f, 0.0f ); if ( ev->NumArgs() > 2 ) { Angle launchAngle( ev->GetFloat( 3 ) ); Trajectory trajectory( origin, targetPosition, launchAngle, projectile->gravity * sv_currentGravity->value ); projectile->velocity = trajectory.GetInitialVelocity(); Vector launchDirection( projectile->velocity ); launchDirection.normalize(); projectile->angles = launchDirection.toAngles(); projectile->CancelEventsOfType( EV_Projectile_Explode ); Event *event = new Event( EV_Projectile_Explode ); if ( ev->NumArgs() > 3 ) { projectile->PostEvent( event, ev->GetFloat( 4 ) ); } else { projectile->PostEvent( event, trajectory.GetTravelTime() ); } } } //---------------------------------------------------------------- // Name: ProjectileAttackEntity // Class: Entity // // Description: launches a projectile either from the named entity // // Parameters: event ev contains projectile name and entity name // // Returns: None //---------------------------------------------------------------- void Entity::ProjectileAttackEntity( Event *ev ) { const str projectileName( ev->GetString( 1 ) ); Entity *target = ev->GetEntity( 2 ); if ( target ) { Vector direction( target->origin - origin ); direction.normalize(); Projectile *projectile = ProjectileAttack( origin, direction, this, projectileName.c_str(), 1.0f, 0.0f ); if ( !projectile ) return; if ( ev->NumArgs() > 2 ) { Angle launchAngle( ev->GetFloat( 3 ) ); Trajectory trajectory( origin, target->centroid, launchAngle, projectile->gravity * -sv_currentGravity->value ); projectile->velocity = trajectory.GetInitialVelocity(); Vector launchDirection( projectile->velocity ); launchDirection.normalize(); projectile->angles = launchDirection.toAngles(); projectile->CancelEventsOfType( EV_Projectile_Explode ); Event *event = new Event( EV_Projectile_Explode ); if ( ev->NumArgs() > 3 ) { projectile->PostEvent( event, ev->GetFloat( 4 ) ); } else { projectile->PostEvent( event, trajectory.GetTravelTime() ); } } } } //---------------------------------------------------------------- // Name: ProjectileAttackFromTag // Class: Entity // // Description: launches a projectile either from the named tag // // Parameters: event ev contains projectile name and tag name // // Returns: None //---------------------------------------------------------------- void Entity::ProjectileAttackFromTag( Event *ev ) { str projectileName( ev->GetString( 1 ) ); str tagName( ev->GetString( 2 ) ); Vector position; Vector direction; GetTag( tagName.c_str(), &position, &direction ); float speed = 0.0f; if ( ev->NumArgs() > 3 ) { speed = ev->GetFloat( 4 ); } Projectile *projectile = ProjectileAttack( position, direction, this, projectileName.c_str(), 1.0f, speed ); if ( ev->NumArgs() > 4 ) { projectile->CancelEventsOfType( EV_Projectile_Explode ); Event *event = new Event( EV_Projectile_Explode ); projectile->PostEvent( event, ev->GetFloat( 5 ) ); } } //---------------------------------------------------------------- // Name: ProjectileAttackFromPoint // Class: Entity // // Description: launches a projectile either from the desired // location facing the desired direction // // Parameters: event ev contains projectile name, the // position and direction for the new projectile // // Returns: None //---------------------------------------------------------------- void Entity::ProjectileAttackFromPoint( Event *ev ) { str projectileName( ev->GetString( 1 ) ); Vector position( ev->GetVector( 2 ) ); Vector direction( ev->GetVector( 3 ) ); float speed = 0.0f; if ( ev->NumArgs() > 3 ) { speed = ev->GetFloat( 4 ); } Vector forward; direction.AngleVectors( &forward ); Projectile *projectile = ProjectileAttack( origin + position, forward, this, projectileName.c_str(), 1.0f, speed ); if ( ev->NumArgs() > 4 ) { projectile->CancelEventsOfType( EV_Projectile_Explode ); Event *event = new Event( EV_Projectile_Explode ); projectile->PostEvent( event, ev->GetFloat( 5 ) ); } } void Entity::TraceAtk ( Event *ev ) { Vector position; Vector forward; Vector right; Vector up; float range; float damage; float knockback = 0.0; str means_of_death_string = "bullet"; int means_of_death; //int offsetPitch = 0; damage = ev->GetFloat( 1 ); range = ev->GetFloat( 2 ); if ( ev->NumArgs() > 2 ) means_of_death_string = ev->GetString( 3 ); if ( ev->NumArgs() > 3 ) knockback = ev->GetFloat( 4 ); if ( ev->NumArgs() > 4 ) { str tag_name; tag_name = ev->GetString( 5 ); GetTag( tag_name.c_str(), &position, &forward, &right, &up ); } else { position = origin; angles.AngleVectors( &forward, &right, &up ); } means_of_death = MOD_NameToNum( means_of_death_string ); /* if ( ev->NumArgs() > 5 ) offsetPitch = ev->GetInteger( 6 ); if ( offsetPitch ) { Vector ForAngles; ForAngles = forward.toAngles(); ForAngles[YAW] = AngleNormalize180( ForAngles[YAW] ); ForAngles[PITCH] = AngleNormalize180( ForAngles[PITCH] ); ForAngles[ROLL] = AngleNormalize180( ForAngles[ROLL] ); offsetPitch = AngleNormalize180(offsetPitch); ForAngles[PITCH] += offsetPitch; ForAngles[YAW] = AngleNormalize360( ForAngles[YAW] ); ForAngles[PITCH] = AngleNormalize360( ForAngles[PITCH] ); ForAngles[ROLL] = AngleNormalize360( ForAngles[ROLL] ); ForAngles.AngleVectors( &forward ); } */ BulletAttack( position, forward, right, up, range, damage, knockback, 0, means_of_death, vec_zero, 1, this ); } qboolean Entity::MorphChannelMatches ( int morph_channel1, int morph_channel2 ) { // Nothing matches with non if ( morph_channel1 == MORPH_CHAN_NONE ) return false; // See if they match exactly if ( morph_channel1 == morph_channel2 ) return true; // Check special cases if ( ( morph_channel1 == MORPH_CHAN_BROW ) && ( ( morph_channel2 == MORPH_CHAN_LEFT_BROW ) || ( morph_channel2 == MORPH_CHAN_RIGHT_BROW ) ) ) return true; if ( ( morph_channel2 == MORPH_CHAN_BROW ) && ( ( morph_channel1 == MORPH_CHAN_LEFT_BROW ) || ( morph_channel1 == MORPH_CHAN_RIGHT_BROW ) ) ) return true; if ( ( morph_channel1 == MORPH_CHAN_EYES ) && ( ( morph_channel2 == MORPH_CHAN_LEFT_LID ) || ( morph_channel2 == MORPH_CHAN_RIGHT_LID ) ) ) return true; if ( ( morph_channel2 == MORPH_CHAN_EYES ) && ( ( morph_channel1 == MORPH_CHAN_LEFT_LID ) || ( morph_channel1 == MORPH_CHAN_RIGHT_LID ) ) ) return true; return false; } inline MorphInfo *CreateMorphInfo( void ) { MorphInfo *new_morph_info; new_morph_info = new MorphInfo; if ( !new_morph_info ) gi.Error( ERR_DROP, "Couldn't alloc MorphInfo" ); return new_morph_info; } #define CONTENTS_OPERATION_ADD 0 #define CONTENTS_OPERATION_SUBTRACT 1 #define CONTENTS_OPERATION_SET 2 void Entity::Contents ( Event* ev ) { if(ev == 0) return; int operation; const char* contentType; unsigned long contents = 0; unsigned long newFlags = 0; contents = getContents(); for(int i = 1; i <= ev->NumArgs(); i++) { contentType = ev->GetString(i); if( ( contentType == 0 ) || ( strlen( contentType ) == 0 ) ) continue; // Get operation type if(contentType[0] == '+') { operation = CONTENTS_OPERATION_ADD; contentType++; } else if(contentType[0] == '-') { operation = CONTENTS_OPERATION_SUBTRACT; contentType++; } else operation = CONTENTS_OPERATION_SET; // Get contents type if(stricmp(contentType, "shootable") == 0) newFlags = CONTENTS_SHOOTABLE_ONLY; else if(stricmp(contentType, "targetable") == 0) { newFlags = CONTENTS_TARGETABLE; setSolidType(SOLID_BBOX); } else if(stricmp(contentType, "body") == 0) newFlags = CONTENTS_BODY; else if(stricmp(contentType, "solid") == 0) newFlags = CONTENTS_SOLID; else if(stricmp(contentType, "usable") == 0) newFlags = CONTENTS_USABLE; else if(stricmp(contentType, "setclip") == 0) newFlags = CONTENTS_SETCLIP; else if(stricmp(contentType, "playerclip") == 0) newFlags = CONTENTS_PLAYERCLIP; else if(stricmp(contentType, "monsterclip") == 0) newFlags = CONTENTS_MONSTERCLIP; else if(stricmp(contentType, "cameraclip") == 0) newFlags = CONTENTS_CAMERACLIP; else if(stricmp(contentType, "weaponclip") == 0) newFlags = CONTENTS_WEAPONCLIP; else if(stricmp(contentType, "corpse") == 0) newFlags = CONTENTS_CORPSE; else if(stricmp(contentType, "all") == 0) newFlags = 0xFFFFFFFF; // Change contents appropriatly if ( operation == CONTENTS_OPERATION_ADD ) contents |= newFlags; else if ( operation == CONTENTS_OPERATION_SUBTRACT ) contents &= ~newFlags; else if ( operation == CONTENTS_OPERATION_SET ) contents = newFlags; } setContents(contents); link(); } void Entity::setMask( Event* ev ) { int i; int operation; const char* maskType; unsigned long currentMask; unsigned long newMask = 0; // Get the current mask currentMask = edict->clipmask; // Loop through all of the parms and change the mask appropriately for( i = 1 ; i <= ev->NumArgs() ; i++ ) { maskType = ev->GetString( i ); if( ( !maskType ) || ( strlen( maskType ) == 0 ) ) continue; // Get operation type if ( maskType[0] == '+' ) { operation = CONTENTS_OPERATION_ADD; maskType++; } else if ( maskType[0] == '-' ) { operation = CONTENTS_OPERATION_SUBTRACT; maskType++; } else { operation = CONTENTS_OPERATION_SET; } // Get contents type // Masks if ( stricmp( maskType, "solid" ) == 0 ) newMask = MASK_SOLID; else if ( stricmp( maskType, "usable" ) == 0 ) newMask = MASK_USABLE; else if ( stricmp( maskType, "playersolid" ) == 0 ) newMask = MASK_PLAYERSOLID; else if ( stricmp( maskType, "deadsolid" ) == 0 ) newMask = MASK_DEADSOLID; else if ( stricmp( maskType, "monstersolid" ) == 0 ) newMask = MASK_MONSTERSOLID; else if ( stricmp( maskType, "water" ) == 0 ) newMask = MASK_WATER; else if ( stricmp( maskType, "opaque" ) == 0 ) newMask = MASK_OPAQUE; else if ( stricmp( maskType, "shot" ) == 0 ) newMask = MASK_SHOT; else if ( stricmp( maskType, "projectile" ) == 0 ) newMask = MASK_PROJECTILE; else if ( stricmp( maskType, "melee" ) == 0 ) newMask = MASK_MELEE; else if ( stricmp( maskType, "pathsolid" ) == 0 ) newMask = MASK_PATHSOLID; else if ( stricmp( maskType, "camerasolid" ) == 0 ) newMask = MASK_CAMERASOLID; else if ( stricmp( maskType, "setclip" ) == 0 ) newMask = MASK_SETCLIP; // Contents else if ( stricmp( maskType, "contents_solid" ) == 0 ) newMask = CONTENTS_SOLID; else if ( stricmp( maskType, "contents_usable" ) == 0 ) newMask = CONTENTS_USABLE; else if ( stricmp( maskType, "contents_setclip" ) == 0 ) newMask = CONTENTS_SETCLIP; else if ( stricmp( maskType, "contents_targetable" ) == 0 ) newMask = CONTENTS_TARGETABLE; else if ( stricmp( maskType, "contents_playerclip" ) == 0 ) newMask = CONTENTS_PLAYERCLIP; else if ( stricmp( maskType, "contents_monsterclip" ) == 0 ) newMask = CONTENTS_MONSTERCLIP; else if ( stricmp( maskType, "contents_cameraclip" ) == 0 ) newMask = CONTENTS_CAMERACLIP; else if ( stricmp( maskType, "contents_weaponclip" ) == 0 ) newMask = CONTENTS_WEAPONCLIP; else if ( stricmp( maskType, "contents_shootable" ) == 0 ) newMask = CONTENTS_SHOOTABLE_ONLY; else if ( stricmp( maskType, "contents_body" ) == 0 ) newMask = CONTENTS_BODY; else if ( stricmp( maskType, "contents_corpse" ) == 0 ) newMask = CONTENTS_CORPSE; // All else if ( stricmp( maskType, "all" ) == 0 ) newMask = 0xFFFFFFFF; // Change mask appropriately if ( operation == CONTENTS_OPERATION_ADD ) currentMask |= newMask; else if ( operation == CONTENTS_OPERATION_SUBTRACT ) currentMask &= ~newMask; else if ( operation == CONTENTS_OPERATION_SET ) currentMask = newMask; } // Save the new mask edict->clipmask = currentMask; } void Entity::getCustomShaderInfo( const str &customShader, str &shaderName, str &soundName ) { // Setup the defaults shaderName = customShader; soundName = ""; // See if we need to get info from the database if ( ( customShader.length() > 4 ) && ( stricmp( customShader.c_str() + customShader.length() - 4, ".gdb" ) == 0 ) ) { str gameplayObjectName = customShader; gameplayObjectName.CapLength( customShader.length() - 4 ); shaderName = G_GetDatabaseString( "CustomShader", gameplayObjectName, "ShaderName" ); soundName = G_GetDatabaseString( "CustomShader", gameplayObjectName, "SoundName" ); } } //---------------------------------------------------------------- // Name: setCustomShader // Class: Entity // // Description: Sets the custom shader for this entity // // Parameters: const char *customShader - name of the shader // // Returns: none //---------------------------------------------------------------- void Entity::setCustomShader( const char *customShader ) { str shaderName; str soundName; if ( !customShader || ( strlen( customShader ) == 0 ) ) return; getCustomShaderInfo( customShader, shaderName, soundName ); // Apply the custom shader edict->s.customShader = gi.imageindex( shaderName ); edict->s.eFlags |= EF_EFFECT_CUSTOM; // Start the loop sound associated with the customshader if ( soundName.length() ) { LoopSound( soundName ); } } //---------------------------------------------------------------- // Name: setCustomShader // Class: Entity // // Description: Sets the custom shader for this entity // // Parameters: Event *ev - event that contains the name of the shader // // Returns: none //---------------------------------------------------------------- void Entity::setCustomShader( Event *ev ) { setCustomShader( ev->GetString( 1 ) ); } //---------------------------------------------------------------- // Name: clearCustomShader // Class: Entity // // Description: Clears the custom shader for this entity if it matches the shader name passed in // // Parameters: const char *customShader - name of the shader to clear, if NULL will always clear // // Returns: none //---------------------------------------------------------------- void Entity::clearCustomShader( const char *customShader ) { int tempImageIndex; str shaderName; str soundName; if ( customShader && edict->s.customShader ) { getCustomShaderInfo( customShader, shaderName, soundName ); // Only clear the custom shader if it matches the one passed in tempImageIndex = gi.imageindex( shaderName ); if ( edict->s.customShader == tempImageIndex ) { edict->s,customShader = 0; edict->s.eFlags &= ~EF_EFFECT_CUSTOM; } // Get rid of the loopsound associated with this custom shader if ( soundName.length() ) { StopLoopSound(); } } else { edict->s,customShader = 0; edict->s.eFlags &= ~EF_EFFECT_CUSTOM; } } //---------------------------------------------------------------- // Name: clearCustomShader // Class: Entity // // Description: Clears the custom shader for this entity if it matches the shader name passed in // // Parameters: Event *ev - event that optionally contains the name of the shader to clear // // Returns: none //---------------------------------------------------------------- void Entity::clearCustomShader( Event *ev ) { if ( ev->NumArgs() > 0 ) clearCustomShader( ev->GetString( 1 ) ); else clearCustomShader(); } bool Entity::hasCustomShader( const char *customShader ) { // See if any custom shader is currently being used if ( !( edict->s.eFlags & EF_EFFECT_CUSTOM ) ) return false; if ( customShader && edict->s.customShader ) { int tempImageIndex; // Check to see if the shader matches the one passed in tempImageIndex = gi.imageindex( customShader ); if ( edict->s.customShader != tempImageIndex ) { // Not the correct shader return false; } } // Everything matches return true; } void Entity::setCustomEmitter( const char *customEmitter ) { if ( !customEmitter ) return; edict->s.customEmitter = gi.imageindex( customEmitter ); edict->s.eFlags |= EF_EMITTER_CUSTOM; } void Entity::setCustomEmitter( Event *ev ) { setCustomEmitter( ev->GetString( 1 ) ); } void Entity::clearCustomEmitter( const char *customEmitter ) { int tempImageIndex; if ( customEmitter && edict->s.customEmitter ) { // Only clear the custom shader if it matches the one passed in tempImageIndex = gi.imageindex( customEmitter ); if ( edict->s.customEmitter == tempImageIndex ) { edict->s,customEmitter = 0; edict->s.eFlags &= ~EF_EMITTER_CUSTOM; } } else { edict->s,customEmitter = 0; edict->s.eFlags &= ~EF_EMITTER_CUSTOM; } } void Entity::clearCustomEmitter( Event *ev ) { if ( ev->NumArgs() > 0 ) clearCustomEmitter( ev->GetString( 1 ) ); else clearCustomEmitter(); } void Entity::hideFeaturesForFade( void ) { Event *event; // Shut the eyes event = new Event( EV_Morph ); event->AddString( "morph_lid-lshut" ); event->AddFloat( 100.0f ); event->AddFloat( 0.05f ); ProcessEvent ( event ); event = new Event( EV_Morph ); event->AddString( "morph_lid-rshut" ); event->AddFloat( 100.0f ); event->AddFloat( 0.05f ); ProcessEvent ( event ); // Hide the eyes and mouth surfaces event = new Event( EV_SurfaceModelEvent ); event->AddString( "material3" ); event->AddString( "+nodraw" ); ProcessEvent ( event ); // Don't let the entity blink event = new Event( EV_Actor_Blink ); event->AddInteger( 0 ); ProcessEvent ( event ); } void Entity::showFeaturesForFade( void ) { Event *event; // Open the eyes event = new Event( EV_Unmorph ); event->AddString( "morph_lid-lshut" ); event->AddFloat( 100.0f ); event->AddFloat( 0.5f ); ProcessEvent ( event ); event = new Event( EV_Unmorph ); event->AddString( "morph_lid-rshut" ); event->AddFloat( 100.0f ); event->AddFloat( 0.5f ); ProcessEvent ( event ); // Show the eyes and mouth surfaces event = new Event( EV_SurfaceModelEvent ); event->AddString( "material3" ); event->AddString( "-nodraw" ); ProcessEvent ( event ); // Allow the entity to blink again event = new Event( EV_Actor_Blink ); event->AddInteger( 1 ); ProcessEvent ( event ); } void Entity::DisplayEffect ( Event *ev ) { str effectType; str effectName; Event *event; int i; Entity *ent; bool passEvent = false; bool cancelEvents = false; Vector effectPosition; GameplayManager *gpm; str gameplayObjectName; // Get the parms effectType = ev->GetString( 1 ); if ( ev->NumArgs() > 1 ) { effectName = ev->GetString( 2 ); } else { const char *delimiterPtr; // There is only 1 parm, so lets see if the type and name are both crammed into the 1 parm delimiterPtr = strstr( effectType.c_str(), "-" ); if ( delimiterPtr ) { // Effect name is everything after the - effectName = delimiterPtr + 1; // Effect type is everything before the - effectType.CapLength( delimiterPtr - effectType ); } } // Get a pointer to the gameplay manager gpm = GameplayManager::getTheGameplayManager(); // See if there is a gameplay object (if necessary) if ( ( strnicmp( effectType.c_str(), "TransportOut", strlen( "TransportOut" ) ) == 0 ) || ( strnicmp( effectType.c_str(), "TransportIn", strlen( "TransportIn" ) ) == 0 ) || ( strnicmp( effectType.c_str(), "FadeOut", strlen( "FadeOut" ) ) == 0 ) || ( strnicmp( effectType.c_str(), "FadeIn", strlen( "FadeIn" ) ) == 0 ) ) { if ( ( strnicmp( effectType.c_str(), "TransportOut", strlen( "TransportOut" ) ) == 0 ) ) { gameplayObjectName = "TransportOut"; } else if ( ( strnicmp( effectType.c_str(), "TransportIn", strlen( "TransportIn" ) ) == 0 ) ) { gameplayObjectName = "TransportIn"; } else if ( ( strnicmp( effectType.c_str(), "FadeOut", strlen( "FadeOut" ) ) == 0 ) ) { gameplayObjectName = "FadeOut"; } else if ( ( strnicmp( effectType.c_str(), "FadeIn", strlen( "FadeIn" ) ) == 0 ) ) { gameplayObjectName = "FadeIn"; } gameplayObjectName += effectName; if ( !gpm->hasObject( gameplayObjectName ) ) { gi.WDPrintf( "Can't find object called %s to use in DisplayEffect\n", gameplayObjectName.c_str() ); return; } } if ( effectType == "TransportOut" ) { str shaderName; str soundName; str effectTikiName; str animName; str effectPos; str currentEffectPos; float effectTime; float transportTime; int numEffects; int immobilize; int hideFeatures; int attach; // Get all of the info from the gameplay database shaderName = gpm->getStringValue( gameplayObjectName, "ShaderName" ); soundName = gpm->getStringValue( gameplayObjectName, "SoundName" ); effectTikiName = gpm->getStringValue( gameplayObjectName, "EffectName" ); attach = (int) gpm->getFloatValue( gameplayObjectName, "AttachEffect" ); animName = gpm->getStringValue( gameplayObjectName, "AnimName" ); effectPos = gpm->getStringValue( gameplayObjectName, "EffectPosName" ); numEffects = (int) gpm->getFloatValue( gameplayObjectName, "NumEffects" ); effectTime = gpm->getFloatValue( gameplayObjectName, "EffectTime" ); transportTime = gpm->getFloatValue( gameplayObjectName, "TransportTime" ); immobilize = (int) gpm->getFloatValue( gameplayObjectName, "Immobilize" ); hideFeatures = (int) gpm->getFloatValue( gameplayObjectName, "HideFeatures" ); setCustomShader( shaderName ); setAlpha( 0.0f ); edict->s.renderfx |= RF_FORCE_ALPHA_EFFECTS; edict->s.renderfx &= ~RF_FORCE_ALPHA; event = new Event( EV_FadeIn ); event->AddFloat( effectTime ); ProcessEvent( event ); event = new Event( EV_DisplayEffect ); event->AddString( "TransportOut2" ); event->AddString( effectName ); PostEvent( event, effectTime ); if ( hideFeatures ) hideFeaturesForFade(); if ( edict->s.parent == ENTITYNUM_NONE ) { if ( soundName.length() ) { Sound( soundName ); } if ( effectTikiName.length() > 4 ) { Vector pos; currentEffectPos = effectPos; for ( i = 0 ; i < numEffects ; i++ ) { if ( !effectPos.length() || effectPos == "centroid" ) pos = centroid; else if ( effectPos == "origin" ) pos = origin; else if ( effectPos == "randombone" ) { int tagNum; tagNum = (int) G_Random( gi.NumTags( edict->s.modelindex ) ); currentEffectPos = gi.Tag_NameForNum( edict->s.modelindex, tagNum ); GetTag( tagNum, &pos, NULL ); } else if ( gi.Tag_NumForName( edict->s.modelindex, effectPos.c_str() ) >= 0 ) GetTag( effectPos, &pos, NULL ); else pos = centroid; if ( attach ) attachEffect( effectTikiName, currentEffectPos, effectTime + transportTime ); else SpawnEffect( effectTikiName, pos, angles, effectTime + transportTime ); } } } if ( animate && animName.length() && animate->HasAnim( animName.c_str() ) ) { animate->RandomAnimate( animName.c_str() ); } if ( immobilize ) { takedamage = DAMAGE_NO; flags |= FL_IMMOBILE; } passEvent = true; } else if ( effectType == "TransportOut2" ) { str shaderName; float transportTime; // Get all of the info from the gameplay database shaderName = gpm->getStringValue( gameplayObjectName, "ShaderName" ); transportTime = gpm->getFloatValue( gameplayObjectName, "TransportTime" ); CancelEventsOfType( EV_FadeIn ); edict->s.renderfx &= ~RF_FORCE_ALPHA_EFFECTS; setCustomShader( shaderName.c_str() ); setAlpha( 1.0f ); edict->s.renderfx |= RF_FORCE_ALPHA; event = new Event( EV_FadeNoRemove ); event->AddFloat( transportTime ); event->AddFloat( 0.0f ); ProcessEvent( event ); event = new Event( EV_DisplayEffect ); event->AddString( "TransportOut3" ); event->AddString( effectName ); PostEvent( event, transportTime ); } else if ( effectType == "TransportOut3" ) { str shaderName; int hideFeatures; // Get all of the info from the gameplay database shaderName = gpm->getStringValue( gameplayObjectName, "ShaderName" ); hideFeatures = (int) gpm->getFloatValue( gameplayObjectName, "HideFeatures" ); if ( hideFeatures ) showFeaturesForFade(); if ( shaderName.length() ) { clearCustomShader( shaderName ); } // Finish up the transport out event = new Event( EV_EntityRenderEffects ); event->AddString( "-shadow" ); ProcessEvent( event ); takedamage = DAMAGE_YES; flags &= ~FL_IMMOBILE; } else if ( effectType == "TransportIn" ) { str shaderName; str soundName; str effectPos; str currentEffectPos; str effectTikiName; float effectTime; float transportTime; int numEffects; int immobilize; int attach; int hideFeatures; // Cancel other display events so we don't get any overlap (can't do this because it breaks animations that // have both since animate posts all frame commands un front //CancelEventsOfType( EV_DisplayEffect ); clearCustomShader(); // Get all of the info from the gameplay database shaderName = gpm->getStringValue( gameplayObjectName, "ShaderName" ); soundName = gpm->getStringValue( gameplayObjectName, "SoundName" ); effectTikiName = gpm->getStringValue( gameplayObjectName, "EffectName" ); attach = (int) gpm->getFloatValue( gameplayObjectName, "AttachEffect" ); effectPos = gpm->getStringValue( gameplayObjectName, "EffectPosName" ); numEffects = (int) gpm->getFloatValue( gameplayObjectName, "NumEffects" ); effectTime = gpm->getFloatValue( gameplayObjectName, "EffectTime" ); transportTime = gpm->getFloatValue( gameplayObjectName, "TransportTime" ); immobilize = (int) gpm->getFloatValue( gameplayObjectName, "Immobilize" ); hideFeatures = (int) gpm->getFloatValue( gameplayObjectName, "HideFeatures" ); edict->s.renderfx &= ~RF_FORCE_ALPHA_EFFECTS; if ( shaderName.length() ) { setCustomShader( shaderName ); } setAlpha( 0.0f ); edict->s.renderfx |= RF_FORCE_ALPHA; event = new Event( EV_FadeIn ); event->AddFloat( transportTime ); ProcessEvent( event ); event = new Event( EV_DisplayEffect ); event->AddString( "TransportIn2" ); event->AddString( effectName ); PostEvent( event, transportTime ); if ( hideFeatures ) hideFeaturesForFade(); if ( edict->s.parent == ENTITYNUM_NONE ) { if ( soundName.length() ) { Sound( soundName ); } if ( effectTikiName.length() > 4 ) { Vector pos; currentEffectPos = effectPos; for ( i = 0 ; i < numEffects ; i++ ) { if ( !effectPos.length() || effectPos == "centroid" ) pos = centroid; else if ( effectPos == "origin" ) pos = origin; else if ( effectPos == "randombone" ) { int tagNum; tagNum = (int) G_Random( gi.NumTags( edict->s.modelindex ) ); currentEffectPos = gi.Tag_NameForNum( edict->s.modelindex, tagNum ); GetTag( tagNum, &pos, NULL ); } else if ( gi.Tag_NumForName( edict->s.modelindex, effectPos.c_str() ) >= 0 ) GetTag( effectPos, &pos, NULL ); else pos = centroid; if ( attach ) attachEffect( effectTikiName, currentEffectPos, effectTime + transportTime ); else SpawnEffect( effectTikiName, pos, angles, effectTime + transportTime ); } } } if ( immobilize ) { takedamage = DAMAGE_NO; flags |= FL_IMMOBILE; } passEvent = true; cancelEvents = true; } else if ( effectType == "TransportIn2" ) { str shaderName; float effectTime; // Get all of the info from the gameplay database shaderName = gpm->getStringValue( gameplayObjectName, "ShaderName" ); effectTime = gpm->getFloatValue( gameplayObjectName, "EffectTime" ); CancelEventsOfType( EV_FadeIn ); edict->s.renderfx &= ~RF_FORCE_ALPHA; if ( shaderName.length() ) { setCustomShader( shaderName ); } setAlpha( 1.0f ); if ( shaderName.length() ) { edict->s.renderfx |= RF_FORCE_ALPHA_EFFECTS; event = new Event( EV_FadeNoRemove ); event->AddFloat( effectTime ); event->AddFloat( 0.0f ); ProcessEvent( event ); } event = new Event( EV_DisplayEffect ); event->AddString( "TransportIn3" ); event->AddString( effectName ); PostEvent( event, effectTime ); } else if ( effectType == "TransportIn3" ) { str shaderName; str animName; int hideFeatures; // Get all of the info from the gameplay database shaderName = gpm->getStringValue( gameplayObjectName, "ShaderName" ); animName = gpm->getStringValue( gameplayObjectName, "AnimName" ); hideFeatures = (int) gpm->getFloatValue( gameplayObjectName, "HideFeatures" ); if ( shaderName.length() ) { clearCustomShader( shaderName ); edict->s.renderfx &= ~RF_FORCE_ALPHA_EFFECTS; } if ( animate && animName.length() && animate->HasAnim( animName.c_str() ) ) { animate->RandomAnimate( animName.c_str() ); } CancelEventsOfType( EV_FadeNoRemove ); if ( hideFeatures ) showFeaturesForFade(); setAlpha( 1.0f ); event = new Event( EV_EntityRenderEffects ); event->AddString( "+shadow" ); ProcessEvent( event ); takedamage = DAMAGE_YES; flags &= ~FL_IMMOBILE; } else if ( effectType == "FadeOut" ) { str shaderName; str soundName; str effectTikiName; str effectPos; str currentEffectPos; float effectTime; float fadeTime; int numEffects; int attach; int hideFeatures; // Get all of the info from the gameplay database shaderName = gpm->getStringValue( gameplayObjectName, "ShaderName" ); soundName = gpm->getStringValue( gameplayObjectName, "SoundName" ); effectTikiName = gpm->getStringValue( gameplayObjectName, "EffectName" ); attach = (int) gpm->getFloatValue( gameplayObjectName, "AttachEffect" ); effectPos = gpm->getStringValue( gameplayObjectName, "EffectPosName" ); numEffects = (int) gpm->getFloatValue( gameplayObjectName, "NumEffects" ); effectTime = gpm->getFloatValue( gameplayObjectName, "EffectTime" ); fadeTime = gpm->getFloatValue( gameplayObjectName, "FadeTime" ); hideFeatures = (int) gpm->getFloatValue( gameplayObjectName, "HideFeatures" ); setCustomShader( shaderName ); setAlpha( 0.0f ); edict->s.renderfx |= RF_FORCE_ALPHA_EFFECTS; edict->s.renderfx &= ~RF_FORCE_ALPHA; if ( effectTime > 0.0f ) { event = new Event( EV_FadeIn ); event->AddFloat( effectTime ); ProcessEvent( event ); } event = new Event( EV_DisplayEffect ); event->AddString( "FadeOut2" ); event->AddString( effectName ); PostEvent( event, effectTime ); if ( hideFeatures ) hideFeaturesForFade(); if ( edict->s.parent == ENTITYNUM_NONE ) { // Play sound if ( soundName.length() ) { Sound( soundName ); } // Spawn an effect if ( effectTikiName.length() > 4 ) { Vector pos; currentEffectPos = effectPos; for ( i = 0 ; i < numEffects ; i++ ) { if ( !effectPos.length() || effectPos == "centroid" ) pos = centroid; else if ( effectPos == "origin" ) pos = origin; else if ( effectPos == "randombone" ) { int tagNum; tagNum = (int) G_Random( gi.NumTags( edict->s.modelindex ) ); currentEffectPos = gi.Tag_NameForNum( edict->s.modelindex, tagNum ); GetTag( tagNum, &pos, NULL ); } else if ( gi.Tag_NumForName( edict->s.modelindex, effectPos.c_str() ) >= 0 ) GetTag( effectPos, &pos, NULL ); else pos = centroid; if ( attach ) attachEffect( effectTikiName, currentEffectPos, effectTime + fadeTime ); else SpawnEffect( effectTikiName, pos, angles, effectTime + fadeTime ); } } } passEvent = true; } else if ( effectType == "FadeOut2" ) { str shaderName; float fadeTime; // Get all of the info from the gameplay database shaderName = gpm->getStringValue( gameplayObjectName, "ShaderName" ); fadeTime = gpm->getFloatValue( gameplayObjectName, "FadeTime" ); CancelEventsOfType( EV_FadeIn ); edict->s.renderfx &= ~RF_FORCE_ALPHA_EFFECTS; setCustomShader( shaderName ); setAlpha( 1.0f ); edict->s.renderfx |= RF_FORCE_ALPHA; event = new Event( EV_FadeNoRemove ); event->AddFloat( fadeTime ); event->AddFloat( 0.0f ); ProcessEvent( event ); event = new Event( EV_DisplayEffect ); event->AddString( "FadeOut3" ); event->AddString( effectName ); PostEvent( event, fadeTime ); } else if ( effectType == "FadeOut3" ) { /* int hideFeatures; hideFeatures = gpm->getFloatValue( gameplayObjectName, "HideFeatures" ); if ( hideFeatures ) showFeaturesForFade(); */ } else if ( effectType == "FadeIn" ) { str shaderName; str soundName; str effectTikiName; str effectPos; str currentEffectPos; //float effectTime; float fadeTime; int numEffects; int attach; int hideFeatures; // Get all of the info from the gameplay database shaderName = gpm->getStringValue( gameplayObjectName, "ShaderName" ); soundName = gpm->getStringValue( gameplayObjectName, "SoundName" ); effectTikiName = gpm->getStringValue( gameplayObjectName, "EffectName" ); attach = (int) gpm->getFloatValue( gameplayObjectName, "AttachEffect" ); effectPos = gpm->getStringValue( gameplayObjectName, "EffectPosName" ); numEffects = (int) gpm->getFloatValue( gameplayObjectName, "NumEffects" ); //effectTime = gpm->getFloatValue( gameplayObjectName, "EffectTime" ); fadeTime = gpm->getFloatValue( gameplayObjectName, "FadeTime" ); hideFeatures = (int) gpm->getFloatValue( gameplayObjectName, "HideFeatures" ); setCustomShader( shaderName ); setAlpha( 0.0f ); edict->s.renderfx &= ~RF_FORCE_ALPHA_EFFECTS; edict->s.renderfx |= RF_FORCE_ALPHA; event = new Event( EV_FadeIn ); event->AddFloat( fadeTime ); ProcessEvent( event ); event = new Event( EV_DisplayEffect ); event->AddString( "FadeIn2" ); event->AddString( effectName ); PostEvent( event, fadeTime ); if ( hideFeatures ) hideFeaturesForFade(); if ( edict->s.parent == ENTITYNUM_NONE ) { // Play sound if ( soundName.length() ) { Sound( soundName ); } // Spawn an effect if ( effectTikiName.length() > 4 ) { Vector pos; currentEffectPos = effectPos; for ( i = 0 ; i < numEffects ; i++ ) { if ( !effectPos.length() || effectPos == "centroid" ) pos = centroid; else if ( effectPos == "origin" ) pos = origin; else if ( effectPos == "randombone" ) { int tagNum; tagNum = (int) G_Random( gi.NumTags( edict->s.modelindex ) ); currentEffectPos = gi.Tag_NameForNum( edict->s.modelindex, tagNum ); GetTag( tagNum, &pos, NULL ); } else if ( gi.Tag_NumForName( edict->s.modelindex, effectPos.c_str() ) >= 0 ) GetTag( effectPos, &pos, NULL ); else pos = centroid; if ( attach ) attachEffect( effectTikiName, currentEffectPos, fadeTime + fadeTime ); else SpawnEffect( effectTikiName, pos, angles, fadeTime + fadeTime ); } } } passEvent = true; } else if ( effectType == "FadeIn2" ) { str shaderName; float effectTime; // Get all of the info from the gameplay database shaderName = gpm->getStringValue( gameplayObjectName, "ShaderName" ); effectTime = gpm->getFloatValue( gameplayObjectName, "EffectTime" ); CancelEventsOfType( EV_FadeIn ); setAlpha( 1.0f ); if ( shaderName.length() > 0 ) { setCustomShader( shaderName ); edict->s.renderfx |= RF_FORCE_ALPHA_EFFECTS; edict->s.renderfx |= RF_FORCE_ALPHA; event = new Event( EV_FadeNoRemove ); event->AddFloat( effectTime ); event->AddFloat( 0.0f ); ProcessEvent( event ); } event = new Event( EV_DisplayEffect ); event->AddString( "FadeIn3" ); event->AddString( effectName ); PostEvent( event, effectTime ); } else if ( effectType == "FadeIn3" ) { str shaderName; int hideFeatures; // Get all of the info from the gameplay database shaderName = gpm->getStringValue( gameplayObjectName, "ShaderName" ); hideFeatures = (int) gpm->getFloatValue( gameplayObjectName, "HideFeatures" ); CancelEventsOfType( EV_FadeNoRemove ); edict->s.renderfx &= ~RF_FORCE_ALPHA_EFFECTS; edict->s.renderfx &= ~RF_FORCE_ALPHA; if ( hideFeatures ) showFeaturesForFade(); clearCustomShader( shaderName ); setAlpha( 1.0f ); } else if ( effectType == "start_invisibility" ) { edict->s.eFlags |= EF_EFFECT_ELECTRIC; setAlpha( 0.1f ); edict->s.renderfx |= RF_FORCE_ALPHA; addAffectingViewModes( gi.GetViewModeMask( "forcevisible" ) ); passEvent = true; } else if ( effectType == "stop_invisibility" ) { edict->s.eFlags &= ~EF_EFFECT_ELECTRIC; setAlpha( 1.0f ); edict->s.renderfx &= ~RF_FORCE_ALPHA; removeAffectingViewModes( gi.GetViewModeMask( "forcevisible" ) ); passEvent = true; } else if ( effectType == "electric" ) { edict->s.eFlags |= EF_EFFECT_ELECTRIC; passEvent = true; } else if ( effectType == "noelectric" ) { edict->s.eFlags &= ~EF_EFFECT_ELECTRIC; passEvent = true; } else if ( effectType == "failure" ) { edict->s.eFlags |= EF_BEHAVIOR_FAILURE; passEvent = true; } else if ( effectType == "nofailure" ) { edict->s.eFlags &= ~EF_BEHAVIOR_FAILURE; passEvent = true; } else if ( ( effectType == "transport_out" ) || ( effectType == "transport_in" ) || ( effectType == "borg_transport_out" ) || ( effectType == "borg_transport_in" ) ) { gi.WDPrintf( "Support for %s will soon be removed please use the new way\n", effectType.c_str() ); event = new Event( EV_DisplayEffect ); if ( effectType == "transport_out" ) { event->AddString( "TransportOut" ); event->AddString( "Federation" ); } else if ( effectType == "transport_in" ) { event->AddString( "TransportIn" ); event->AddString( "Federation" ); } else if ( effectType == "borg_transport_out" ) { event->AddString( "TransportOut" ); event->AddString( "Borg" ); } else if ( effectType == "borg_transport_in" ) { event->AddString( "TransportIn" ); event->AddString( "Borg" ); } ProcessEvent( event ); return; } if ( passEvent && bind_info && bind_info->numchildren ) { for( i = 0 ; i < MAX_MODEL_CHILDREN ; i++ ) { if ( bind_info->children[ i ] == ENTITYNUM_NONE ) continue; ent = G_GetEntity( bind_info->children[ i ] ); if ( ent ) { /* if ( cancelEvents ) { // Cancel other display events so we don't get any overlap ent->CancelEventsOfType( EV_DisplayEffect ); } */ ent->ProcessEvent( *ev ); } } } } //---------------------------------------------------------------- // Name: clearDisplayEffects // Class: Entity // // Description: Clears all of the display type of effects and sets the alpha back to normal // // Parameters: None // // Returns: None //---------------------------------------------------------------- void Entity::clearDisplayEffects( void ) { if ( edict->s.eFlags | EF_EFFECTS ) { edict->s.eFlags &= ~EF_EFFECTS; setAlpha( 1.0f ); edict->s.renderfx &= ~RF_FORCE_ALPHA_EFFECTS; edict->s.renderfx &= ~RF_FORCE_ALPHA; } } void Entity::SpawnEffect( Event *ev ) { //const str &name, const Vector &origin, const Vector &angles, float removeTime ) str tagName; str modelName; Vector tagPos; Vector tagForward; Vector tagAngles; float removeTime; modelName = ev->GetString( 1 ); tagName = ev->GetString( 2 ); GetTag( tagName, &tagPos, &tagForward ); tagAngles = tagForward.toAngles(); if ( ev->NumArgs() > 2 ) removeTime = ev->GetFloat( 3 ); else removeTime = 0.0f; SpawnEffect( modelName, tagPos, tagAngles, removeTime ); } Entity *Entity::SpawnEffect( const str &name, const Vector &origin, const Vector &angles, float removeTime ) { Entity *newEntity; str modelName; int nameLength; nameLength = name.length(); if ( stricmp( name.c_str() + nameLength - 4, ".gdb" ) == 0 ) { str gameplayObjectName; GameplayManager *gpm; gpm = GameplayManager::getTheGameplayManager(); gameplayObjectName = name; gameplayObjectName.CapLength( nameLength - 4 ); if ( gpm->hasObject( gameplayObjectName ) ) { modelName = gpm->getStringValue( gameplayObjectName, "ModelName" ); } else { const char *dash; dash = strstr( gameplayObjectName.c_str(), "-" ); if ( dash ) { gameplayObjectName.CapLength( dash - gameplayObjectName.c_str() ); gameplayObjectName += "-default"; if ( gpm->hasObject( gameplayObjectName ) ) { modelName = gpm->getStringValue( gameplayObjectName, "ModelName" ); } else { gi.WDPrintf( "%s not find in the gameplay database\n", name.c_str() ); return NULL; } } else { gi.WDPrintf( "%s not find in the gameplay database\n", name.c_str() ); return NULL; } } } else { modelName = name; } newEntity = new Entity( ENTITY_CREATE_FLAG_ANIMATE ); newEntity->setModel( modelName ); newEntity->angles = angles; newEntity->setAngles(); newEntity->setOrigin( origin ); newEntity->setSolidType( SOLID_NOT ); if ( removeTime > 0.0f ) newEntity->PostEvent( EV_Remove, removeTime ); newEntity->CancelEventsOfType( EV_ProcessInitCommands ); newEntity->ProcessInitCommands( newEntity->edict->s.modelindex ); newEntity->animate->RandomAnimate( "idle" ); return newEntity; } Entity* Entity::SpawnSound( const str &sound, const Vector &pos, float volume, float removeTime ) { Entity *newEntity; Vector soundOrigin; newEntity = new Entity( ENTITY_CREATE_FLAG_ANIMATE ); newEntity->setOrigin( pos ); newEntity->setSolidType( SOLID_NOT ); if ( removeTime > 0.0f ) newEntity->PostEvent( EV_Remove, removeTime ); soundOrigin = pos; newEntity->CancelEventsOfType( EV_ProcessInitCommands ); newEntity->ProcessInitCommands( newEntity->edict->s.modelindex ); newEntity->Sound( sound , CHAN_BODY, volume, -1.0f, &soundOrigin ); return newEntity; } void Entity::attachEffect( Event *ev ) { str tagName; str modelName; //Vector tagPos; //Vector tagForward; //Vector tagAngles; float removeTime; modelName = ev->GetString( 1 ); tagName = ev->GetString( 2 ); //GetTag( tagName, &tagPos, &tagForward ); //tagAngles = tagForward.toAngles(); if ( ev->NumArgs() > 2 ) removeTime = ev->GetFloat( 3 ); else removeTime = 0.0f; attachEffect( modelName, tagName, removeTime ); } void Entity::attachEffect( const str &modelName, const str &tagName, float removeTime ) { Event *newEvent = new Event( EV_AttachModel ); newEvent->AddString( modelName ); newEvent->AddString( tagName ); newEvent->AddFloat( 1.0f ); newEvent->AddString( "" ); newEvent->AddInteger( 0 ); newEvent->AddFloat( removeTime ); ProcessEvent( newEvent ); } void Entity::ForceAlpha( Event *ev ) { if ( ev->NumArgs() == 0 ) edict->s.renderfx |= RF_FORCE_ALPHA; else if ( ev->GetBoolean( 1 ) ) edict->s.renderfx |= RF_FORCE_ALPHA; else edict->s.renderfx &= ~RF_FORCE_ALPHA; } void Entity::CreateEarthquake( Event *ev ) { Earthquake *earthquake; float duration; float magnitude; float distance = 0; Event *newEvent; if ( origin == vec_zero ) { gi.WDPrintf( "Earthquake being started when origin hasn't been set yet in model %s\n", model.c_str() ); return; } magnitude = ev->GetFloat( 1 ); duration = ev->GetFloat( 2 ); if ( ev->NumArgs() > 2 ) distance = ev->GetFloat( 3 ); earthquake = new Earthquake; newEvent = new Event( EV_SetOrigin ); newEvent->AddVector( origin ); earthquake->ProcessEvent( newEvent ); newEvent = new Event( EV_Earthquake_SetMagnitude ); newEvent->AddFloat( magnitude ); earthquake->ProcessEvent( newEvent ); newEvent = new Event( EV_Earthquake_SetDuration ); newEvent->AddFloat( duration ); earthquake->ProcessEvent( newEvent ); if ( distance ) { newEvent = new Event( EV_Earthquake_SetDistance ); newEvent->AddFloat( distance ); earthquake->ProcessEvent( newEvent ); } earthquake->ProcessEvent( EV_Trigger_Effect ); earthquake->PostEvent( EV_Remove, duration ); } void Entity::SetFloatVar ( Event *ev ) { str var_name; float value; var_name = ev->GetString( 1 ); value = ev->GetFloat( 2 ); entityVars.SetVariable( var_name, value ); } void Entity::SetVectorVar ( Event *ev ) { str var_name; Vector value; var_name = ev->GetString( 1 ); value = ev->GetVector( 2 ); entityVars.SetVariable( var_name, value ); } void Entity::SetStringVar ( Event *ev ) { str var_name; str value; var_name = ev->GetString( 1 ); value = ev->GetString( 2 ); entityVars.SetVariable( var_name, value ); } void Entity::doesVarExist( Event *ev ) { ScriptVariable *var = NULL; var = entityVars.GetVariable( ev->GetString( 1 ) ); if ( var ) ev->ReturnFloat( 1.0f ); else ev->ReturnFloat( 0.0f ); } void Entity::GetFloatVar ( Event *ev ) { str var_name; ScriptVariable *var = NULL; var_name = ev->GetString( 1 ); var = entityVars.GetVariable( var_name ); if ( var ) ev->ReturnFloat( var->floatValue() ); else { gi.WDPrintf( "%s variable not found\n", var_name.c_str() ); ev->ReturnFloat( 0.0f ); } } void Entity::RemoveVariable( Event* ev ) { str var_name; if( ev->NumArgs() > 0 ) { var_name = ev->GetString( 1 ); if( var_name.length() > 0 ) { entityVars.RemoveVariable( var_name ); } } } void Entity::GetVectorVar ( Event *ev ) { str var_name; ScriptVariable *var = NULL; var_name = ev->GetString( 1 ); var = entityVars.GetVariable( var_name ); if ( var ) ev->ReturnVector( var->vectorValue() ); else { gi.WDPrintf( "%s variable not found\n", var_name.c_str() ); ev->ReturnVector( Vector(0, 0, 0) ); } } void Entity::GetStringVar ( Event *ev ) { str var_name; ScriptVariable *var = NULL; var_name = ev->GetString( 1 ); var = entityVars.GetVariable( var_name ); if ( var ) ev->ReturnString( var->stringValue() ); else { gi.WDPrintf( "%s variable not found\n", var_name.c_str() ); ev->ReturnString( "" ); } } void Entity::SetUserVar1 ( Event *ev ) { entityVars.SetVariable( "uservar1", ev->GetString( 1 ) ); } void Entity::SetUserVar2 ( Event *ev ) { entityVars.SetVariable( "uservar2", ev->GetString( 1 ) ); } void Entity::SetUserVar3 ( Event *ev ) { entityVars.SetVariable( "uservar3", ev->GetString( 1 ) ); } void Entity::SetUserVar4 ( Event *ev ) { entityVars.SetVariable( "uservar4", ev->GetString( 1 ) ); } Vector Entity::GetClosestCorner( const Vector &position ) { Vector corner; Vector closestCorner; Vector compare; float distance; float length; distance = 999999999.9f; for ( int i = 0 ; i < 4 ; i++ ) { // corner's based on a top down view of the bounding box -- I am returning // the vector for the corner on a plane with the origin only. switch ( i ) { // Upper Left case 0: corner.x = origin.x + mins.x; corner.y = origin.y + maxs.y; corner.z = origin.z; break; // Upper Right case 1: corner.x = origin.x + maxs.x; corner.y = origin.y + maxs.y; corner.z = origin.z; break; // Lower Left case 2: corner.x = origin.x + mins.x; corner.y = origin.y + mins.y; corner.z = origin.z; break; case 3: corner.x = origin.x + maxs.x; corner.y = origin.y + mins.y; corner.z = origin.z; break; } compare = corner - position; length = compare.length(); if ( length < distance ) { closestCorner = corner; distance = length; } } return closestCorner; } //---------------------------------------------------------------- // Name: affectingViewMode // Class: Entity // // Description: Adds a new viewmode to the entities applicable viewmodes // // Parameters: Event *ev - event (name of the view mode) // // Returns: None //---------------------------------------------------------------- void Entity::affectingViewMode( Event *ev ) { addAffectingViewModes( gi.GetViewModeMask( ev->GetString( 1 ) ) ); } //---------------------------------------------------------------- // Name: addAffectingViewMode // Class: Entity // // Description: Adds the specified viewmode bits to the entities applicable viewmodes // // Parameters: unsigned int mask - the bit mask of the relevant viewmodes // // Returns: None //---------------------------------------------------------------- void Entity::addAffectingViewModes( unsigned int mask ) { _affectingViewModes |= mask; edict->s.affectingViewModes = _affectingViewModes; } void Entity::removeAffectingViewModes( unsigned int mask ) { _affectingViewModes &= ~mask; edict->s.affectingViewModes = _affectingViewModes; } //-------------------------------------------------------------- // Name: SetGroupID() // Class: Entity // // Description: Grabs the ID from the event, and sends it to the // group coordinator for registration. In the future // we need to migrate this so that all group registration // is done throught he group coordinator alone. // // Parameters: Event *ev // // Returns: None //-------------------------------------------------------------- void Entity::SetGroupID( Event *ev ) { AddToGroup( ev->GetInteger( 1 ) ); } void Entity::AddToGroup( int ID ) { groupcoordinator->AddEntityToGroup( this , ID ); } //---------------------------------------------------------------- // Name: TikiTodo and TikiNote // Class: Entity // // Description: These commands may come from tiki files (via TikiMaster). // They are here so the commands are not considered errors. // In the future we might want cvars to print todo items. // // Parameters: Event *ev // // Returns: None //---------------------------------------------------------------- void Entity::TikiTodo( Event *ev ) { } void Entity::TikiNote( Event *ev ) { } //---------------------------------------------------------------- // Name: MultiplayerEvent // Class: Entity // // Description: This is a passthrough event. It only allows the contained event to be called // if we are in a multiplayer game // // Parameters: Event *ev - contains the real event to process // // Returns: None //---------------------------------------------------------------- void Entity::MultiplayerEvent( Event *ev ) { // Make sure we are in a multiplayer game if ( multiplayerManager.inMultiplayer() ) { Event *event; str eventName; str token; int i; // Get the event name eventName = ev->GetString( 1 ); event = new Event( eventName ); // Get all of the event parms for ( i = 2 ; i <= ev->NumArgs() ; i++ ) { event->AddToken( ev->GetToken( i ) ); } // Process the event ProcessEvent( event ); } } //-------------------------------------------------------------- // // Name: AddDamageModifier // Class: Entity // // Description: Adds a new damage modifier to this entities list // // Parameters: Event *ev // // Returns: None // //-------------------------------------------------------------- void Entity::AddDamageModifier( Event *ev ) { str damagemodtype, value; float multiplier, chance = 1.0f, painBaseLine = 50.0f; damagemodtype = ev->GetString( 1 ); value = ev->GetString( 2 ); multiplier = ev->GetFloat( 3 ); if ( ev->NumArgs() > 3 ) chance = ev->GetFloat( 4 ); if ( ev->NumArgs() > 4 ) painBaseLine = ev->GetFloat( 5 ); if ( !damageModSystem ) damageModSystem = new DamageModificationSystem; damageModSystem->addDamageModifier(damagemodtype, value, multiplier, chance, painBaseLine ); } //-------------------------------------------------------------- // // Name: ResolveDamage // Class: Entity // // Description: Calls the DamageModificationSystem to resolve damage // // Parameters: Damage &damage -- Reference to damage to modifiy // // Returns: // //-------------------------------------------------------------- void Entity::ResolveDamage( ::Damage &damage ) { if ( damageModSystem ) damageModSystem->resolveDamage(damage); } //-------------------------------------------------------------- // Name: setMoveType() // Class: Entity // // Description: Will convert a string to the appropriate moveType // and then call setMoveType( type ) // // Parameters: Event *ev // // Returns: None //-------------------------------------------------------------- void Entity::setMoveType( Event *ev ) { str type; type = ev->GetString( 1 ); if ( !stricmp( type.c_str() , "none" ) ) { velocity = vec_zero; setMoveType( MOVETYPE_NONE ); } else if ( !stricmp( type.c_str() , "stationary" ) ) setMoveType( MOVETYPE_STATIONARY ); else if ( !stricmp( type.c_str() , "noclip" ) ) setMoveType( MOVETYPE_NOCLIP ); else if ( !stricmp( type.c_str() , "push" ) ) setMoveType( MOVETYPE_PUSH ); else if ( !stricmp( type.c_str() , "stop" ) ) setMoveType( MOVETYPE_STOP ); else if ( !stricmp( type.c_str() , "walk" ) ) setMoveType( MOVETYPE_WALK ); else if ( !stricmp( type.c_str() , "step" ) ) setMoveType( MOVETYPE_STEP ); else if ( !stricmp( type.c_str() , "fly" ) ) setMoveType( MOVETYPE_FLY ); else if ( !stricmp( type.c_str() , "toss" ) ) setMoveType( MOVETYPE_TOSS ); else if ( !stricmp( type.c_str() , "flymissile" ) ) setMoveType( MOVETYPE_FLYMISSILE ); else if ( !stricmp( type.c_str() , "bounce" ) ) setMoveType( MOVETYPE_BOUNCE ); else if ( !stricmp( type.c_str() , "slide" ) ) setMoveType( MOVETYPE_SLIDE ); else if ( !stricmp( type.c_str() , "rope" ) ) setMoveType( MOVETYPE_ROPE ); else if ( !stricmp( type.c_str() , "gib" ) ) setMoveType( MOVETYPE_GIB ); else if ( !stricmp( type.c_str() , "vehicle" ) ) setMoveType( MOVETYPE_VEHICLE ); } //=============================================================== // Name: buildUseData // Class: Entity // // Description: Creates a usedata structure if need be. Also // ensures the contents type of a usable object is // set to something that can be detected by the // use trace (so it can indeed be used). // // Testing this idea out. // // Parameters: None // // Returns: None // //=============================================================== void Entity::buildUseData ( void ) { if ( useData ) return ; useData = new UseData(); } //-------------------------------------------------------------- // Name: useDataAnim // Class: Entity // // Description: Sets the useData anim member // // Parameters: Event *ev // // Returns: None //-------------------------------------------------------------- void Entity::useDataAnim( Event *ev ) { buildUseData(); useData->setUseAnim(ev->GetString( 1 )); } //-------------------------------------------------------------- // Name: useDataType // Class: Entity // // Description: Sets the useData type member // // Parameters: Event *ev // // Returns: None //-------------------------------------------------------------- void Entity::useDataType( Event *ev ) { buildUseData(); useData->setUseType(ev->GetString( 1 )); } //-------------------------------------------------------------- // Name: useDataThread // Class: Entity // // Description: Sets the useData thread member // // Parameters: Event *ev // // Returns: None //-------------------------------------------------------------- void Entity::useDataThread( Event *ev ) { buildUseData(); useData->setUseThread(ev->GetString( 1 )); } //-------------------------------------------------------------- // Name: useDataEvent // Class: Entity // // Description: Sets the useData variables // // Parameters: Event *ev // // Returns: None //-------------------------------------------------------------- void Entity::useDataEvent( Event *ev ) { buildUseData(); useData->setUseAnim(ev->GetString( 1 )); useData->setUseType(ev->GetString( 2 )); useData->setUseThread(ev->GetString( 3 )); } //-------------------------------------------------------------- // Name: useDataMaxDist // Class: Entity // // Description: Sets the maximum distance this entity can be used. // // Parameters: Event *ev // // Returns: None //-------------------------------------------------------------- void Entity::useDataMaxDist( Event *ev ) { buildUseData(); useData->setUseMaxDist(ev->GetFloat( 1 )); } //-------------------------------------------------------------- // Name: useDataCount // Class: Entity // // Description: Sets the number of times this entity can be used. // // Parameters: Event *ev // // Returns: None //-------------------------------------------------------------- void Entity::useDataCount( Event *ev ) { buildUseData(); useData->setUseCount(ev->GetInteger( 1 )); } //-------------------------------------------------------------- // Name: setArchetype // Class: Entity // // Description: Sets the archetype name for this entity // // Parameters: Event *ev // // Returns: None //-------------------------------------------------------------- void Entity::setArchetype( Event *ev ) { _archetype = ev->GetString(1); //Sets the archetype index edict->s.archeTypeIndex = gi.archetypeindex(_archetype); } //-------------------------------------------------------------- // Name: getArchetype // Class: Entity // // Description: Gets the archetype name for this entity, // // Parameters: None // // Returns: const str //-------------------------------------------------------------- const str Entity::getArchetype() const { if ( _archetype.length() == 0 ) return getName(); return _archetype; } //----------------------------------------------------- // // Name: setMissionObjective // Class: Entity // // Description: Sets the entity to be a mission objective or not. // // Parameters: ev // // Returns: None //----------------------------------------------------- void Entity::setMissionObjective(Event* ev) { _missionObjective = ev->GetBoolean(1); edict->s.missionObjective = ev->GetBoolean(1); if( edict->s.missionObjective ) { G_AddEntityToExtraList(entnum); } else { G_RemoveEntityFromExtraList(entnum); } } //-------------------------------------------------------------- // Name: GetVelocity // Class: Entity // // Description: Gets the Velocity of the Entity // // Parameters: Event *ev // // Returns: None //-------------------------------------------------------------- void Entity::GetVelocity( Event *ev ) { ev->ReturnVector( velocity ); } //-------------------------------------------------------------- // Name: SetVelocity // Class: Entity // // Description: Sets the Velocity of the Entity // // Parameters: Event *ev // // Returns: None //-------------------------------------------------------------- void Entity::SetVelocity( Event *ev ) { velocity = ev->GetVector( 1 ); } void Entity::startStasis( Event *ev ) { startStasis(); } void Entity::stopStasis( Event *ev ) { stopStasis(); } void Entity::startStasis( void ) { // Immobilize the character flags |= FL_IMMOBILE; flags |= FL_STUNNED; // Show an effect setCustomShader( "stasis" ); if ( animate ) { animate->StopAnimating(); if ( edict->s.torso_anim & ANIM_BLEND ) animate->StopAnimating( torso ); } } void Entity::stopStasis( void ) { // Unimmobilize the character flags &= ~FL_IMMOBILE; flags &= ~FL_STUNNED; // Stop the effect clearCustomShader( "stasis" ); if ( this->isSubclassOf( Actor ) ) { Actor *actor = (Actor *)this; actor->resetStateMachine(); } else if ( this->isSubclassOf( Player ) ) { Player *player = (Player *)this; player->SetState("STAND", "START"); } } void Entity::setTargetPos( Event *ev ) { setTargetPos( ev->GetString( 1 ) ); } void Entity::setTargetPos( const str &targetPos ) { _targetPos = targetPos; } str Entity::getTargetPos() { return _targetPos; } void Entity::addHealthOverTime( Event *ev ) { Event *event; float healthToAdd; float healthToAddThisFrame; float timeLeft; int numFrames; healthToAdd = ev->GetFloat( 1 ); timeLeft = ev->GetFloat( 2 ); // Figure out how much health to add this frame if ( timeLeft < level.frametime ) numFrames = 1; else numFrames = (int) (timeLeft / level.frametime); healthToAddThisFrame = healthToAdd / numFrames; // Actually add the health to the entity addHealth( healthToAddThisFrame ); // Post the event for the next frame healthToAdd -= healthToAddThisFrame; timeLeft -= level.frametime; //CancelEventsOfType( EV_AddHealthOverTime ); if ( timeLeft > 0.0f ) { event = new Event( EV_AddHealthOverTime ); event->AddFloat( healthToAdd ); event->AddFloat( timeLeft ); PostEvent( event, level.frametime ); } } void Entity::simplePlayDialog( Event *ev ) { str dialogName; float volume = DEFAULT_VOL; float min_dist = DEFAULT_MIN_DIST; char localizedDialogName[MAX_QPATH]; Player *player; // Get all of the parms dialogName = ev->GetString( 1 ); if ( ev->NumArgs() > 1 ) volume = ev->GetFloat( 2 ); if ( ev->NumArgs() > 2 ) { str minDistString; min_dist = ev->GetFloat( 3 ); minDistString = ev->GetString( 3 ); if ( stricmp( minDistString.c_str(), LEVEL_WIDE_STRING ) == 0 ) min_dist = LEVEL_WIDE_MIN_DIST; else min_dist = ev->GetFloat( 3 ); if ( min_dist >= LEVEL_WIDE_MIN_DIST_CUTOFF ) min_dist = LEVEL_WIDE_MIN_DIST; } // Get the localized name of the dialog gi.LocalizeFilePath( dialogName.c_str(), localizedDialogName ); // Play the sound Sound( localizedDialogName, CHAN_DIALOG, volume, min_dist ); // Tell the player about the dialog player = GetPlayer( 0 ); if ( player ) { player->SetupDialog( NULL, localizedDialogName ); } } void Entity::warp( Event *ev ) { Entity *ent; int i; if ( ev->NumArgs() > 0 ) setOrigin( ev->GetVector( 1 ) ); else setOrigin(); setAngles(); NoLerpThisFrame(); if ( bind_info ) { // Make sure everyone bound to us doesn't lerp for( ent = bind_info->teamchain; ent != NULL; ent = ent->bind_info->teamchain ) { if ( ent->bind_info->teammaster == this ) { ent->ProcessEvent( EV_Warp ); } } // Make sure everyone attached bound to us doesn't lerp for( i = 0; i < MAX_MODEL_CHILDREN ; i++ ) { if ( bind_info->children[ i ] == ENTITYNUM_NONE ) { continue; } ent = G_GetEntity( bind_info->children[ i ] ); if ( ent ) { ent->ProcessEvent( EV_Warp ); } } } } void Entity::traceHitsEntity( Event *ev ) { trace_t trace; Vector end; Vector start; Vector dir; str tagName; float length; Entity *entityToCheck; // Get the event info tagName = ev->GetString( 1 ); length = ev->GetFloat( 2 ); entityToCheck = ev->GetEntity( 3 ); // Determine the start and end point of the trace GetTag( tagName, &start, &dir ); end = start + dir * length; // Do the actual trace trace = G_Trace( start, vec_zero, vec_zero, end, NULL, MASK_SHOT, false, "traceHitsEntity" ); // Determine if we hit this entity if ( trace.ent && entityToCheck && trace.ent->entity == entityToCheck ) ev->ReturnFloat( 1.0f ); else ev->ReturnFloat( 0.0f ); } void Entity::setOriginEveryFrame( Event *ev ) { Event *repost; repost = new Event(EV_SetOriginEveryFrame); setOrigin(); PostEvent( repost , FRAMETIME ); } void Entity::isWithinDistanceOf( Event *ev ) { Entity *destination; float distance; float returnValue; destination = ev->GetEntity( 1 ); distance = ev->GetFloat( 2 ); returnValue = 0.0f; if ( WithinDistance( destination , distance ) ) returnValue = 1.0; ev->ReturnFloat( returnValue ); } void Entity::setNetworkDetail( Event *ev ) { _networkDetail = true; } bool Entity::isNetworkDetail( void ) { return _networkDetail; }