mirror of
https://github.com/openmoh/openmohaa.git
synced 2025-04-28 21:57:57 +03:00
6514 lines
153 KiB
C++
6514 lines
153 KiB
C++
/*
|
|
===========================================================================
|
|
Copyright (C) 2015 the OpenMoHAA team
|
|
|
|
This file is part of OpenMoHAA source code.
|
|
|
|
OpenMoHAA source code is free software; you can redistribute it
|
|
and/or modify it under the terms of the GNU General Public License as
|
|
published by the Free Software Foundation; either version 2 of the License,
|
|
or (at your option) any later version.
|
|
|
|
OpenMoHAA source code is distributed in the hope that it will be
|
|
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with OpenMoHAA source code; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
===========================================================================
|
|
*/
|
|
|
|
// entity.cpp: Base class for all enities that are controlled by Sin. 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 Sin is any object that is not part of the world-> Any non-world
|
|
// object that is visible in Sin 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 Sin 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 "entity.h"
|
|
#include "scriptmaster.h"
|
|
#include "sentient.h"
|
|
#include "misc.h"
|
|
#include "specialfx.h"
|
|
#include "object.h"
|
|
#include "player.h"
|
|
#include "weaputils.h"
|
|
#include "soundman.h"
|
|
#include "scriptexception.h"
|
|
#include "qfiles.h"
|
|
#include "g_spawn.h"
|
|
#include "g_phys.h"
|
|
#include "debuglines.h"
|
|
#include <tiki.h>
|
|
#include <utility>
|
|
|
|
// Generic entity events
|
|
Event EV_SoundDone(
|
|
"saydone", EV_DEFAULT, NULL, NULL, "Even used by sound-only special case of say to trigger waittill saydone"
|
|
);
|
|
Event EV_Classname
|
|
(
|
|
"classname",
|
|
EV_DEFAULT,
|
|
"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.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_SpawnFlags
|
|
(
|
|
"spawnflags",
|
|
EV_DEFAULT,
|
|
"i",
|
|
"flags",
|
|
"spawnflags from the BSP,"
|
|
"these are set inside the editor",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_GetNormalHealth
|
|
(
|
|
"normal_health",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"The Normal Health of the Actor",
|
|
EV_GETTER
|
|
);
|
|
Event EV_NormalDamage
|
|
(
|
|
"normal_damage",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"The Normal Health of the Actor (0 - 100)",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_SetTeam
|
|
(
|
|
"team",
|
|
EV_DEFAULT,
|
|
"s",
|
|
"moveTeam",
|
|
"used to make multiple entities move together.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Trigger
|
|
(
|
|
"trigger",
|
|
EV_DEFAULT,
|
|
"s",
|
|
"name",
|
|
"Trigger the specified target or entity.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Activate
|
|
(
|
|
"doActivate",
|
|
EV_DEFAULT,
|
|
"e",
|
|
"activatingEntity",
|
|
"General trigger event for all entities",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Use
|
|
(
|
|
"doUse",
|
|
EV_DEFAULT,
|
|
"e",
|
|
"activatingEntity",
|
|
"sent to entity when it is used by another entity",
|
|
EV_NORMAL
|
|
);
|
|
|
|
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",
|
|
EV_NORMAL
|
|
);
|
|
|
|
Event EV_FadeOut
|
|
(
|
|
"_fadeout",
|
|
EV_DEFAULT,
|
|
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.",
|
|
EV_NORMAL
|
|
);
|
|
|
|
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.",
|
|
EV_NORMAL
|
|
);
|
|
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.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Killed(
|
|
"killed",
|
|
EV_DEFAULT,
|
|
"eievvviiii",
|
|
"attacker damage inflictor position direction normal knockback damageflags meansofdeath location",
|
|
"event which is sent to an entity once it as been killed\n\nLocation values:\n-1 General\n0 Pelvis\n1 Lower "
|
|
"Torso\n2 Mid Torso\n3 Upper Torso\n4 Neck\n5 Head\n6 RUpperArm\n7 RForearm\n8 RHand\n9 LUpperArm\n10 LForearm\n11 "
|
|
"LHand\n12 RThigh\n13 RCalf\n14 RFoot\n15 LThigh\n16 LCalf\n17 LFoot\n",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_GotKill
|
|
(
|
|
"gotkill",
|
|
EV_DEFAULT,
|
|
"eieib",
|
|
"victim damage inflictor meansofdeath gib",
|
|
"event sent to attacker when an entity dies",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Pain
|
|
(
|
|
"pain",
|
|
EV_DEFAULT,
|
|
"eievvviiii",
|
|
"attacker damage inflictor position direction normal knockback damageflags meansofdeath location",
|
|
"used to inflict pain to an entity",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Damage(
|
|
"damage",
|
|
EV_DEFAULT,
|
|
"eievvviiii",
|
|
"attacker damage inflictor position direction normal knockback damageflags meansofdeath location",
|
|
"general damage event used by all entities\n\nLocation values:\n-1 General\n0 Pelvis\n1 Lower Torso\n2 Mid "
|
|
"Torso\n3 Upper Torso\n4 Neck\n5 Head\n6 RUpperArm\n7 RForearm\n8 RHand\n9 LUpperArm\n10 LForearm\n11 LHand\n12 "
|
|
"RThigh\n13 RCalf\n14 RFoot\n15 LThigh\n16 LCalf\n17 LFoot\n",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Stun
|
|
(
|
|
"_stun",
|
|
EV_DEFAULT,
|
|
"f",
|
|
"time",
|
|
"Stun this entity for the specified time",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Kill
|
|
(
|
|
"kill",
|
|
EV_CONSOLE,
|
|
NULL,
|
|
NULL,
|
|
"console based command to kill yourself if stuck.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Gib
|
|
(
|
|
"gib",
|
|
EV_DEFAULT,
|
|
"iIFS",
|
|
"number power scale gibmodel",
|
|
"causes entity to spawn a number of gibs",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Hurt
|
|
(
|
|
"hurt",
|
|
EV_DEFAULT,
|
|
"iSV",
|
|
"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.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Heal(
|
|
"heal",
|
|
EV_DEFAULT,
|
|
"f",
|
|
"health",
|
|
"Adds health to an entity,"
|
|
"0-1 fraction of max_health"
|
|
);
|
|
|
|
Event EV_TakeDamage
|
|
(
|
|
"takedamage",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"makes entity take damage.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_NoDamage
|
|
(
|
|
"nodamage",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"entity does not take damage.",
|
|
EV_NORMAL
|
|
);
|
|
|
|
Event EV_Stationary
|
|
(
|
|
"stationary",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"entity does not move,"
|
|
"causes no physics to be run on it.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Toss
|
|
(
|
|
"toss",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"entity has gravity applied to it.",
|
|
EV_NORMAL
|
|
);
|
|
|
|
// Physics events
|
|
Event EV_MoveDone
|
|
(
|
|
"movedone",
|
|
EV_DEFAULT,
|
|
"e",
|
|
"finishedEntity",
|
|
"Sent to commanding thread when done with move .",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Touch
|
|
(
|
|
"doTouch",
|
|
EV_DEFAULT,
|
|
"e",
|
|
"touchingEntity",
|
|
"sent to entity when touched.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Blocked
|
|
(
|
|
"doBlocked",
|
|
EV_DEFAULT,
|
|
"e",
|
|
"obstacle",
|
|
"sent to entity when blocked.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_UseBoundingBox
|
|
(
|
|
"usebbox",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"do not perform perfect collision,"
|
|
"use bounding box instead.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Gravity
|
|
(
|
|
"gravity",
|
|
EV_DEFAULT,
|
|
"f",
|
|
"gravityValue",
|
|
"Change the gravity on this entity",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Stop
|
|
(
|
|
"stopped",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"sent when entity has stopped bouncing for MOVETYPE_TOSS.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Attach
|
|
(
|
|
"attach",
|
|
EV_DEFAULT,
|
|
"esI",
|
|
"parent tagname use_angles",
|
|
"attach this entity to the parent's legs tag called tagname",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_AttachModel
|
|
(
|
|
"attachmodel",
|
|
EV_DEFAULT,
|
|
"ssFSBFFFFV",
|
|
"modelname tagname scale targetname detach_at_death removetime fadeintime fadeoutdelay fadetime 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",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_RemoveAttachedModel
|
|
(
|
|
"removeattachedmodel",
|
|
EV_DEFAULT,
|
|
"s",
|
|
"tagname",
|
|
"Removes the model attached to this entity at the specified tag.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_AttachedModelAnim(
|
|
"attachedmodelanim",
|
|
EV_DEFAULT,
|
|
"ssfs",
|
|
"tagname anim_name crossblend_time model_name",
|
|
"Tells models (or specified model) attached to specified tag to play \n"
|
|
"specified animation. Crossblend time doesn't work yet."
|
|
);
|
|
Event EV_Detach
|
|
(
|
|
"detach",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"detach this entity from its parent.",
|
|
EV_NORMAL
|
|
);
|
|
|
|
// script stuff
|
|
Event EV_Model
|
|
(
|
|
"model",
|
|
EV_DEFAULT,
|
|
"e",
|
|
"modelName",
|
|
"set the model to modelName.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_SetModel
|
|
(
|
|
"model",
|
|
EV_DEFAULT,
|
|
"e",
|
|
"modelName",
|
|
"set the model to modelName.",
|
|
EV_SETTER
|
|
);
|
|
Event EV_GetModel
|
|
(
|
|
"model",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"get the modelName.",
|
|
EV_GETTER
|
|
);
|
|
Event EV_GetBrushModel
|
|
(
|
|
"brushmodel",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"get the brush modelName.",
|
|
EV_GETTER
|
|
);
|
|
Event EV_Hide
|
|
(
|
|
"hide",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"hide the entity,"
|
|
"opposite of show.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Show
|
|
(
|
|
"show",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"show the entity,"
|
|
"opposite of hide.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_BecomeSolid
|
|
(
|
|
"solid",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"make solid.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_SafeSolid
|
|
(
|
|
"safesolid",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"make solid but first make sure no one is in my bounds that is thinking.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_BecomeNonSolid
|
|
(
|
|
"notsolid",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"make non-solid.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Ghost
|
|
(
|
|
"ghost",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"make non-solid but still send to client regardless of hide status.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_TouchTriggers
|
|
(
|
|
"touchtriggers",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"this entity should touch triggers.",
|
|
EV_NORMAL
|
|
);
|
|
|
|
Event EV_Sound
|
|
(
|
|
"playsound",
|
|
EV_DEFAULT,
|
|
"sIFS",
|
|
"soundName channel volume min_distance",
|
|
"play a sound coming from this entity.\n"
|
|
"default channel, CHAN_BODY.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_StopSound
|
|
(
|
|
"stopsound",
|
|
EV_DEFAULT,
|
|
"I",
|
|
"channel",
|
|
"stop the current sound on the specified channel.\n"
|
|
"default channel, CHAN_BODY.",
|
|
EV_NORMAL
|
|
);
|
|
|
|
Event EV_DuckableGlue
|
|
(
|
|
"duckableglue",
|
|
EV_DEFAULT,
|
|
"eI",
|
|
"parent glueAngles",
|
|
"glue this entity to the specified entity, but allow ducking.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Glue
|
|
(
|
|
"glue",
|
|
EV_DEFAULT,
|
|
"eI",
|
|
"parent glueAngles",
|
|
"glue this entity to the specified entity.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Unglue
|
|
(
|
|
"unglue",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"unglue this entity.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Bind
|
|
(
|
|
"bind",
|
|
EV_DEFAULT,
|
|
"eB",
|
|
"parent bind_child",
|
|
"bind this entity to the specified entity.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Unbind
|
|
(
|
|
"unbind",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"unbind this entity.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_MakeClayPidgeon
|
|
(
|
|
"claypidgeon",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"turn the entity into a non-solid shootable thing",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_JoinTeam
|
|
(
|
|
"joinTeam",
|
|
EV_DEFAULT,
|
|
"e",
|
|
"teamMember",
|
|
"join a bind team.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_QuitTeam
|
|
(
|
|
"quitTeam",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"quit the current bind team",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_SetScale
|
|
(
|
|
"scale",
|
|
EV_DEFAULT,
|
|
"f",
|
|
"newScale",
|
|
"set the scale of the entity",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_SetScale2
|
|
(
|
|
"scale",
|
|
EV_DEFAULT,
|
|
"f",
|
|
"newScale",
|
|
"set the scale of the entity",
|
|
EV_SETTER
|
|
);
|
|
Event EV_GetScale
|
|
(
|
|
"scale",
|
|
EV_DEFAULT,
|
|
"f",
|
|
"newScale",
|
|
"set the scale of the entity",
|
|
EV_GETTER
|
|
);
|
|
Event EV_SetSize
|
|
(
|
|
"setsize",
|
|
EV_DEFAULT,
|
|
"vv",
|
|
"mins maxs",
|
|
"Set the bounding box of the entity to mins and maxs.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_SetMins
|
|
(
|
|
"_setmins",
|
|
EV_DEFAULT,
|
|
"v",
|
|
"mins",
|
|
"Set the mins of the bounding box of the entity to mins.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_SetMaxs
|
|
(
|
|
"_setmaxs",
|
|
EV_DEFAULT,
|
|
"v",
|
|
"maxs",
|
|
"Set the maxs of the bounding box of the entity to maxs.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_GetMins
|
|
(
|
|
"getmins",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Get the mins of the bounding box of the entity to mins.",
|
|
EV_GETTER
|
|
);
|
|
Event EV_GetMaxs
|
|
(
|
|
"getmaxs",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Get the maxs of the bounding box of the entity to maxs.",
|
|
EV_GETTER
|
|
);
|
|
Event EV_SetAlpha
|
|
(
|
|
"alpha",
|
|
EV_DEFAULT,
|
|
"f",
|
|
"newAlpha",
|
|
"Set the alpha of the entity to alpha.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_SetKillTarget
|
|
(
|
|
"killtarget",
|
|
EV_DEFAULT,
|
|
"s",
|
|
"targetName",
|
|
"when dying kill entities with this targetName.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_SetMass
|
|
(
|
|
"mass",
|
|
EV_DEFAULT,
|
|
"f",
|
|
"massAmount",
|
|
"set the mass of this entity.",
|
|
EV_NORMAL
|
|
);
|
|
|
|
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.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_StopLoopSound
|
|
(
|
|
"stoploopsound",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Stop the looped-sound on this entity.",
|
|
EV_NORMAL
|
|
);
|
|
|
|
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",
|
|
EV_NORMAL
|
|
);
|
|
// AI sound events
|
|
Event EV_BroadcastAIEvent
|
|
(
|
|
"ai_event",
|
|
EV_DEFAULT,
|
|
"SF",
|
|
"type radius",
|
|
"Let the AI know that this entity made a sound,\ntype is a string specifying what type of sound it is.\nradius "
|
|
"determines how far the sound reaches.",
|
|
EV_NORMAL
|
|
);
|
|
|
|
// Lighting
|
|
Event EV_SetLight
|
|
(
|
|
"light",
|
|
EV_DEFAULT,
|
|
"ffff",
|
|
"red green blue radius",
|
|
"Create a dynamic light on this entity.",
|
|
EV_NORMAL
|
|
);
|
|
|
|
Event EV_LightOn
|
|
(
|
|
"lightOn",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Turn the configured dynamic light on this entity on.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_LightOff
|
|
(
|
|
"lightOff",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Turn the configured dynamic light on this entity off.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_LightStyle
|
|
(
|
|
"lightStyle",
|
|
EV_DEFAULT,
|
|
"i",
|
|
"lightStyleIndex",
|
|
"What light style to use for this dynamic light on this entity.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_LightRed
|
|
(
|
|
"lightRed",
|
|
EV_DEFAULT,
|
|
"f",
|
|
"red",
|
|
"Set the red component of the dynamic light on this entity.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_LightGreen
|
|
(
|
|
"lightGreen",
|
|
EV_DEFAULT,
|
|
"f",
|
|
"red",
|
|
"Set the red component of the dynamic light on this entity.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_LightBlue
|
|
(
|
|
"lightBlue",
|
|
EV_DEFAULT,
|
|
"f",
|
|
"red",
|
|
"Set the red component of the dynamic light on this entity.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_LightRadius
|
|
(
|
|
"lightRadius",
|
|
EV_DEFAULT,
|
|
"f",
|
|
"red",
|
|
"Set the red component of the dynamic light on this entity.",
|
|
EV_NORMAL
|
|
);
|
|
|
|
// 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",
|
|
EV_NORMAL
|
|
);
|
|
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"
|
|
"'lightstyle' command to set the index of the light style to be used",
|
|
EV_NORMAL
|
|
);
|
|
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"
|
|
"antisbjuice - anti sucknblow juice"
|
|
"everyframe - process commands every time entity is rendered",
|
|
EV_NORMAL
|
|
);
|
|
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",
|
|
EV_NORMAL
|
|
);
|
|
|
|
// Special Effects
|
|
Event EV_Censor
|
|
(
|
|
"censor",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"used to ban certain contact when in parentmode\n",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Explosion
|
|
(
|
|
"explosionattack",
|
|
EV_DEFAULT,
|
|
"sS",
|
|
"explosionModel tagName",
|
|
"Spawn an explosion optionally from a specific tag",
|
|
EV_NORMAL
|
|
);
|
|
|
|
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",
|
|
EV_NORMAL
|
|
);
|
|
|
|
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",
|
|
EV_NORMAL
|
|
);
|
|
|
|
Event EV_KillAttach
|
|
(
|
|
"killattach",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"kill all the attached entities.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_DropToFloor
|
|
(
|
|
"droptofloor",
|
|
EV_DEFAULT,
|
|
"F",
|
|
"maxRange",
|
|
"drops the entity to the ground, if maxRange is not specified 8192 is used.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_AddToSoundManager
|
|
(
|
|
"_addtosoundmanager",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"adds the current entity to the sound manager.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_SetControllerAngles
|
|
(
|
|
"setcontrollerangles",
|
|
EV_DEFAULT,
|
|
"iv",
|
|
"num angles",
|
|
"Sets the control angles for the specified bone.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_GetControllerAngles
|
|
(
|
|
"getcontrollerangles",
|
|
EV_DEFAULT,
|
|
"i",
|
|
"num",
|
|
"Gets the control angles for the specified bone.",
|
|
EV_RETURN
|
|
);
|
|
Event EV_GetTagPosition
|
|
(
|
|
"gettagposition",
|
|
EV_DEFAULT,
|
|
"s",
|
|
"tag_name",
|
|
"Gets the world position of the tag",
|
|
EV_RETURN
|
|
);
|
|
Event EV_GetTagAngles
|
|
(
|
|
"gettagangles",
|
|
EV_DEFAULT,
|
|
"s",
|
|
"tag_name",
|
|
"Gets the world angles of the tag",
|
|
EV_RETURN
|
|
);
|
|
Event EV_DeathSinkStart
|
|
(
|
|
"deathsinkstart",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Makes the entity sink into the ground and then get removed (this starts it).",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_DeathSink
|
|
(
|
|
"deathsinkeachframe",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Makes the entity sink into the ground and then get removed (this gets called each frame).",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_DamageType
|
|
(
|
|
"damage_type",
|
|
EV_DEFAULT,
|
|
"s",
|
|
"meansofdeathstring",
|
|
"Set the type of damage that this entity can take",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_DetachAllChildren
|
|
(
|
|
"detachallchildren",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Detach all the children from the entity.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Entity_MovementStealth
|
|
(
|
|
"movementstealth",
|
|
EV_DEFAULT,
|
|
"f",
|
|
"scale",
|
|
"Sets the current movement stealth scalar for the sentient",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Entity_Revive
|
|
(
|
|
"revive",
|
|
EV_DEFAULT,
|
|
"f",
|
|
"health",
|
|
"sets the health,"
|
|
"even if dead"
|
|
);
|
|
Event EV_SetHealth
|
|
(
|
|
"health",
|
|
EV_CONSOLE | EV_CHEAT,
|
|
"i",
|
|
"newHealth",
|
|
"set the health of the entity to newHealth",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_SetHealth2
|
|
(
|
|
"health",
|
|
EV_CONSOLE | EV_CHEAT,
|
|
"i",
|
|
"newHealth",
|
|
"set the health of the entity to newHealth",
|
|
EV_SETTER
|
|
);
|
|
Event EV_Entity_GetHealth
|
|
(
|
|
"health",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"entity's health",
|
|
EV_GETTER
|
|
);
|
|
Event EV_Entity_GetMaxHealth
|
|
(
|
|
"max_health",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"gets the entity's max health",
|
|
EV_GETTER
|
|
);
|
|
Event EV_Entity_SetMaxHealth
|
|
(
|
|
"max_health",
|
|
EV_DEFAULT,
|
|
"i",
|
|
"newHealth",
|
|
"sets max_health without changing health",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Entity_SetMaxHealth2
|
|
(
|
|
"max_health",
|
|
EV_DEFAULT,
|
|
"i",
|
|
"newHealth",
|
|
"sets max_health without changing health",
|
|
EV_SETTER
|
|
);
|
|
Event EV_Entity_SetHealthOnly
|
|
(
|
|
"healthonly",
|
|
EV_CONSOLE | EV_CHEAT,
|
|
"i",
|
|
"newHealth",
|
|
"set the health of the entity to newHealth without changing max_health",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Entity_SetHealthOnly2
|
|
(
|
|
"healthonly",
|
|
EV_CONSOLE | EV_CHEAT,
|
|
"i",
|
|
"newHealth",
|
|
"set the health of the entity to newHealth without changing max_health",
|
|
EV_SETTER
|
|
);
|
|
Event EV_Entity_GetYaw
|
|
(
|
|
"yaw",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"entity's yaw",
|
|
EV_GETTER
|
|
);
|
|
Event EV_Pusher
|
|
(
|
|
"pusher",
|
|
EV_DEFAULT,
|
|
"eevf",
|
|
"inflictor attacker direction force",
|
|
"Push an entity in the specified direction with the specified force",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_NeverDraw
|
|
(
|
|
"neverdraw",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Sets this entity to never draw",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_NormalDraw
|
|
(
|
|
"normaldraw",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Sets this entity to normal draw",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_AlwaysDraw
|
|
(
|
|
"alwaysdraw",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Sets this entity to always draw",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_IsTouching
|
|
(
|
|
"istouching",
|
|
EV_DEFAULT,
|
|
"e",
|
|
"ent",
|
|
"returns 1 if the entities are touching,"
|
|
"0 if not",
|
|
EV_RETURN
|
|
);
|
|
Event EV_IsInside
|
|
(
|
|
"isinside",
|
|
EV_DEFAULT,
|
|
"e",
|
|
"ent",
|
|
"returns 1 if the entity is inside,"
|
|
"0 if not",
|
|
EV_RETURN
|
|
);
|
|
Event EV_CanSee
|
|
(
|
|
"cansee",
|
|
EV_DEFAULT,
|
|
"eFF",
|
|
"entity fov vision_distance",
|
|
"returns 1 if the entities can see eachother, 0 if not",
|
|
EV_RETURN
|
|
);
|
|
|
|
// Added in 2.0
|
|
Event EV_CanSeeNoEnts
|
|
(
|
|
"canseenoents",
|
|
EV_DEFAULT,
|
|
"eFF",
|
|
"entity fov vision_distance",
|
|
"returns 1 if the entities can see eachother, 0 if not; ignores any entities between them",
|
|
EV_RETURN
|
|
);
|
|
|
|
Event EV_Entity_InPVS
|
|
(
|
|
"inpvs",
|
|
EV_DEFAULT,
|
|
"e",
|
|
"entity",
|
|
"returns 1 if the entities have connected pvs,"
|
|
"0 if not",
|
|
EV_RETURN
|
|
);
|
|
Event EV_SetShaderData
|
|
(
|
|
"setshaderdata",
|
|
EV_DEFAULT,
|
|
"ff",
|
|
"data0 data1",
|
|
"sets the shader controllers for this entity.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_SetVelocity
|
|
(
|
|
"velocity",
|
|
EV_DEFAULT,
|
|
"v",
|
|
"velocity",
|
|
"sets the velocity for this entity.",
|
|
EV_SETTER
|
|
);
|
|
Event EV_GetVelocity
|
|
(
|
|
"velocity",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"gets the velocity for this entity.",
|
|
EV_GETTER
|
|
);
|
|
Event EV_GetAVelocity
|
|
(
|
|
"avelocity",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"gets the angular velocity for this entity.",
|
|
EV_GETTER
|
|
);
|
|
Event EV_ForceActivate
|
|
(
|
|
"forceactivate",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Forces an entity to activate outside of the player's PVS",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_ConnectPaths
|
|
(
|
|
"connect_paths",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Connects all navigation paths which intersect with the specified entity's volume",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_DisconnectPaths
|
|
(
|
|
"disconnect_paths",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"disonnects all navigation paths which intersect with the specified entity's volume",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_VolumeDamage
|
|
(
|
|
"volumedamage",
|
|
EV_DEFAULT,
|
|
"f",
|
|
"damage",
|
|
"does damage to any entity within this's volume",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Entity_AddImmunity
|
|
(
|
|
"immune",
|
|
EV_DEFAULT,
|
|
"sSSSSS",
|
|
"immune_string1 immune_string2 immune_string3 immune_string4 immune_string5 immune_string6",
|
|
"Adds to the immunity list for this sentient.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Entity_RemoveImmunity
|
|
(
|
|
"removeimmune",
|
|
EV_DEFAULT,
|
|
"sSSSSS",
|
|
"immune_string1 immune_string2 immune_string3 immune_string4 immune_string5 immune_string6",
|
|
"Removes from the immunity list for this sentient.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_GetEntnum
|
|
(
|
|
"entnum",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"The entity's entity number",
|
|
EV_GETTER
|
|
);
|
|
Event EV_GetClassname
|
|
(
|
|
"classname",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"The entity's classname",
|
|
EV_GETTER
|
|
);
|
|
Event EV_Entity_SetRadnum
|
|
(
|
|
"radnum",
|
|
EV_DEFAULT,
|
|
"i",
|
|
"radnum",
|
|
"set the radnum",
|
|
EV_SETTER
|
|
);
|
|
Event EV_Entity_GetRadnum
|
|
(
|
|
"radnum",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"entity's radnum",
|
|
EV_GETTER
|
|
);
|
|
Event EV_Entity_SetRotatedBbox
|
|
(
|
|
"rotatedbbox",
|
|
EV_DEFAULT,
|
|
"i",
|
|
"on_off",
|
|
"Sets the entity's bbox to rotate with it.",
|
|
EV_SETTER
|
|
);
|
|
Event EV_Entity_GetRotatedBbox
|
|
(
|
|
"rotatedbbox",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Gets te entity's bbox to rotate with it.",
|
|
EV_GETTER
|
|
);
|
|
Event EV_Entity_SinglePlayerCommand
|
|
(
|
|
"sp",
|
|
EV_DEFAULT,
|
|
"sSSS",
|
|
"command parm1 parm2 ...",
|
|
"Makes a command be executed only in single player",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Entity_MultiPlayerCommand
|
|
(
|
|
"dm",
|
|
EV_DEFAULT,
|
|
"sSSS",
|
|
"command parm1 parm2 ...",
|
|
"Makes a command be executed only in multiplayer",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Entity_RealismModeCommand
|
|
(
|
|
"realism",
|
|
EV_DEFAULT,
|
|
"sSSS",
|
|
"command parm1 parm2 ...",
|
|
"Makes a command be executed only in realism mode",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Entity_SPRealismModeCommand
|
|
(
|
|
"sprealism",
|
|
EV_DEFAULT,
|
|
"sSSS",
|
|
"command parm1 parm2 ...",
|
|
"Makes a command be executed only in single player realism mode",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Entity_DMRealismModeCommand
|
|
(
|
|
"dmrealism",
|
|
EV_DEFAULT,
|
|
"sSSS",
|
|
"command parm1 parm2 ...",
|
|
"Makes a command be executed only in multiplayer realism mode",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Entity_GetLocalYawFromVector
|
|
(
|
|
"GetLocalYawFromVector",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Turn a worldspace vector into a local space yaw",
|
|
EV_RETURN
|
|
);
|
|
Event EV_ShootableOnly
|
|
(
|
|
"shootableonly",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Makes the entity shootable only.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_SetShaderTime
|
|
(
|
|
"setshadertime",
|
|
EV_DEFAULT,
|
|
"FF",
|
|
"timeOffset randomTimeOffset",
|
|
"reset the shader time for this entity.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_NoTarget
|
|
(
|
|
"notarget",
|
|
EV_DEFAULT,
|
|
"b",
|
|
"setNoTarget",
|
|
"flag an entity as no target.",
|
|
EV_NORMAL
|
|
);
|
|
|
|
Event EV_Entity_SetDHack
|
|
(
|
|
"depthhack",
|
|
EV_DEFAULT,
|
|
"i",
|
|
"bEnable",
|
|
"Enable or disable depth hack.",
|
|
EV_NORMAL
|
|
);
|
|
/*
|
|
Event EV_Entity_GetZone
|
|
(
|
|
"getzone",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Gets the current entity zone. NIL if the entity is not in a zone.",
|
|
EV_RETURN
|
|
);
|
|
Event EV_Entity_Zone
|
|
(
|
|
"zone",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Get the current entity zone. NIL if the entity is not in a zone.",
|
|
EV_GETTER
|
|
);
|
|
Event EV_Entity_IsInZone
|
|
(
|
|
"isinzone",
|
|
EV_DEFAULT,
|
|
"e",
|
|
"zone",
|
|
"Returns 1 if the entity is in the specified zone. 0 otherwise",
|
|
EV_RETURN
|
|
);
|
|
*/
|
|
#ifdef OPM_FEATURES
|
|
Event EV_Entity_SetHintRequireLookAt
|
|
(
|
|
"setuselookat",
|
|
EV_DEFAULT,
|
|
"b",
|
|
"lookat",
|
|
"Sets if the client must look at the entity to use it",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Entity_SetHintString
|
|
(
|
|
"sethintstring",
|
|
EV_DEFAULT,
|
|
"s",
|
|
"string",
|
|
"Sets the hint string for an entity when touching it. '&&1' will be replaced by the +use key on the client. If "
|
|
"hideent/hide is called on this entity, the hint string won't be shown for the client",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Entity_SetShader
|
|
(
|
|
"setshader",
|
|
EV_DEFAULT,
|
|
"s",
|
|
"shadername",
|
|
"Sets a shader for the entity. An empty string will revert to the normal entity shader.",
|
|
EV_NORMAL
|
|
);
|
|
#endif
|
|
|
|
CLASS_DECLARATION(SimpleEntity, Entity, NULL) {
|
|
{&EV_Damage, &Entity::DamageEvent },
|
|
{&EV_DamageType, &Entity::DamageType },
|
|
{&EV_Kill, &Entity::Kill },
|
|
{&EV_Killed, &Entity::Killed },
|
|
{&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_SafeSolid, &Entity::SafeSolid },
|
|
{&EV_BecomeNonSolid, &Entity::BecomeNonSolid },
|
|
{&EV_Ghost, &Entity::Ghost },
|
|
{&EV_TouchTriggers, &Entity::TouchTriggersEvent },
|
|
{&EV_Sound, &Entity::Sound },
|
|
{&EV_StopSound, &Entity::StopSound },
|
|
{&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_SetScale2, &Entity::SetScale },
|
|
{&EV_GetScale, &Entity::GetScale },
|
|
{&EV_SetAlpha, &Entity::SetAlpha },
|
|
{&EV_SetKillTarget, &Entity::SetKillTarget },
|
|
{&EV_SetMass, &Entity::SetMassEvent },
|
|
{&EV_LoopSound, &Entity::LoopSound },
|
|
{&EV_StopLoopSound, &Entity::StopLoopSound },
|
|
{&EV_Model, &Entity::SetModelEvent },
|
|
{&EV_SetModel, &Entity::SetModelEvent },
|
|
{&EV_GetModel, &Entity::GetModelEvent },
|
|
{&EV_GetBrushModel, &Entity::GetBrushModelEvent },
|
|
{&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_BroadcastAIEvent, &Entity::BroadcastAIEvent },
|
|
{&EV_SurfaceModelEvent, &Entity::SurfaceModelEvent },
|
|
{&EV_Attach, &Entity::AttachEvent },
|
|
{&EV_AttachModel, &Entity::AttachModelEvent },
|
|
{&EV_RemoveAttachedModel, &Entity::RemoveAttachedModelEvent },
|
|
{&EV_AttachedModelAnim, &Entity::AttachedModelAnimEvent },
|
|
{&EV_Detach, &Entity::DetachEvent },
|
|
{&EV_TakeDamage, &Entity::TakeDamageEvent },
|
|
{&EV_NoDamage, &Entity::NoDamageEvent },
|
|
{&EV_Gravity, &Entity::Gravity },
|
|
{&EV_UseBoundingBox, &Entity::UseBoundingBoxEvent },
|
|
{&EV_Hurt, &Entity::HurtEvent },
|
|
{&EV_Heal, &Entity::HealEvent },
|
|
{&EV_Classname, &Entity::ClassnameEvent },
|
|
{&EV_SoundDone, &Entity::EventSoundDone },
|
|
{&EV_SpawnFlags, &Entity::SpawnFlagsEvent },
|
|
{&EV_SetTeam, &Entity::SetTeamEvent },
|
|
{&EV_Trigger, &Entity::TriggerEvent },
|
|
{&EV_Censor, &Entity::Censor },
|
|
{&EV_Stationary, &Entity::StationaryEvent },
|
|
{&EV_Toss, &Entity::TossEvent },
|
|
{&EV_Explosion, &Entity::Explosion },
|
|
{&EV_ShaderEvent, &Entity::Shader },
|
|
{&EV_ScriptShaderEvent, &Entity::Shader },
|
|
{&EV_KillAttach, &Entity::KillAttach },
|
|
{&EV_DropToFloor, &Entity::DropToFloorEvent },
|
|
{&EV_Glue, &Entity::GlueEvent },
|
|
{&EV_DuckableGlue, &Entity::DuckableGlueEvent },
|
|
{&EV_Unglue, &Entity::EventUnglue },
|
|
{&EV_Bind, &Entity::BindEvent },
|
|
{&EV_Unbind, &Entity::EventUnbind },
|
|
{&EV_MakeClayPidgeon, &Entity::MakeClayPidgeon },
|
|
{&EV_JoinTeam, &Entity::JoinTeam },
|
|
{&EV_QuitTeam, &Entity::EventQuitTeam },
|
|
{&EV_AddToSoundManager, &Entity::AddToSoundManager },
|
|
{&EV_SetControllerAngles, &Entity::SetControllerAngles },
|
|
{&EV_GetControllerAngles, &Entity::GetControllerAngles },
|
|
{&EV_GetTagPosition, &Entity::GetTagPosition },
|
|
{&EV_GetTagAngles, &Entity::GetTagAngles },
|
|
{&EV_DeathSinkStart, &Entity::DeathSinkStart },
|
|
{&EV_DeathSink, &Entity::DeathSink },
|
|
{&EV_DetachAllChildren, &Entity::DetachAllChildren },
|
|
{&EV_Entity_MovementStealth, &Entity::EventMovementStealth },
|
|
{&EV_Pusher, &Entity::PusherEvent },
|
|
{&EV_AlwaysDraw, &Entity::AlwaysDraw },
|
|
{&EV_NeverDraw, &Entity::NeverDraw },
|
|
{&EV_NormalDraw, &Entity::NormalDraw },
|
|
{&EV_IsTouching, &Entity::IsTouching },
|
|
{&EV_IsInside, &Entity::IsInside },
|
|
{&EV_CanSee, &Entity::CanSee },
|
|
{&EV_CanSeeNoEnts, &Entity::CanSeeNoEnts }, // Added in 2.0
|
|
{&EV_Entity_InPVS, &Entity::EventInPVS },
|
|
{&EV_SetShaderData, &Entity::SetShaderData },
|
|
{&EV_GetVelocity, &Entity::GetVelocity },
|
|
{&EV_SetVelocity, &Entity::SetVelocity },
|
|
{&EV_GetAVelocity, &Entity::GetAVelocity },
|
|
{&EV_ForceActivate, &Entity::ForceActivate },
|
|
{&EV_ScriptThread_Trace, &Entity::EventTrace },
|
|
{&EV_ScriptThread_SightTrace, &Entity::EventSightTrace },
|
|
{&EV_Entity_Revive, &Entity::EventRevive },
|
|
{&EV_SetHealth, &Entity::SetHealth },
|
|
{&EV_SetHealth2, &Entity::SetHealth },
|
|
{&EV_Entity_GetHealth, &Entity::GetHealth },
|
|
{&EV_Entity_GetMaxHealth, &Entity::EventGetMaxHealth },
|
|
{&EV_Entity_SetMaxHealth, &Entity::EventSetMaxHealth },
|
|
{&EV_Entity_SetMaxHealth2, &Entity::EventSetMaxHealth },
|
|
{&EV_Entity_SetHealthOnly, &Entity::EventSetHealthOnly },
|
|
{&EV_Entity_SetHealthOnly2, &Entity::EventSetHealthOnly },
|
|
{&EV_Entity_GetYaw, &Entity::GetYaw },
|
|
{&EV_ConnectPaths, &Entity::EventConnectPaths },
|
|
{&EV_DisconnectPaths, &Entity::EventDisconnectPaths },
|
|
{&EV_Remove, &Entity::Remove },
|
|
{&EV_Delete, &Listener::ScriptRemove },
|
|
{&EV_ScriptRemove, &Listener::ScriptRemove },
|
|
{&EV_VolumeDamage, &Entity::EventVolumeDamage },
|
|
{&EV_GetNormalHealth, &Entity::EventGetNormalHealth },
|
|
{&EV_NormalDamage, &Entity::EventNormalDamage },
|
|
{&EV_Entity_AddImmunity, &Entity::AddImmunity },
|
|
{&EV_Entity_RemoveImmunity, &Entity::RemoveImmunity },
|
|
{&EV_GetEntnum, &Entity::EventGetEntnum },
|
|
{&EV_GetClassname, &Entity::EventGetClassname },
|
|
{&EV_Entity_SetRadnum, &Entity::EventSetRadnum },
|
|
{&EV_Entity_GetRadnum, &Entity::EventGetRadnum },
|
|
{&EV_Entity_SetRotatedBbox, &Entity::EventSetRotatedBbox },
|
|
{&EV_Entity_GetRotatedBbox, &Entity::EventGetRotatedBbox },
|
|
{&EV_Entity_SinglePlayerCommand, &Entity::EventSinglePlayerCommand },
|
|
{&EV_Entity_MultiPlayerCommand, &Entity::EventMultiPlayerCommand },
|
|
{&EV_Entity_RealismModeCommand, &Entity::EventRealismModeCommand },
|
|
{&EV_Entity_SPRealismModeCommand, &Entity::EventSPRealismModeCommand},
|
|
{&EV_Entity_DMRealismModeCommand, &Entity::EventDMRealismModeCommand},
|
|
{&EV_Entity_GetLocalYawFromVector, &Entity::GetLocalYawFromVector },
|
|
{&EV_ShootableOnly, &Entity::EventShootableOnly },
|
|
{&EV_SetShaderTime, &Entity::SetShaderTime },
|
|
{&EV_NoTarget, &Entity::NoTarget },
|
|
/*
|
|
{&EV_Entity_GetZone, &Entity::GetZone },
|
|
{&EV_Entity_Zone, &Entity::GetZone },
|
|
{&EV_Entity_IsInZone, &Entity::IsInZone },
|
|
*/
|
|
{&EV_Entity_SetDHack, &Entity::SetDepthHack },
|
|
#ifdef OPM_FEATURES
|
|
{&EV_Entity_SetHintRequireLookAt, &Entity::SetHintRequireLookAt },
|
|
{&EV_Entity_SetHintString, &Entity::SetHintString },
|
|
{&EV_Entity_SetShader, &Entity::SetShader },
|
|
#endif
|
|
{NULL, NULL }
|
|
};
|
|
|
|
Entity::Entity()
|
|
{
|
|
int i;
|
|
|
|
edict = level.AllocEdict(this);
|
|
client = edict->client;
|
|
entnum = edict->s.number;
|
|
radnum = -1;
|
|
|
|
entflags |= ECF_ENTITY;
|
|
|
|
AddWaitTill(STRING_DAMAGE);
|
|
AddWaitTill(STRING_SPAWN);
|
|
AddWaitTill(STRING_SOUNDDONE);
|
|
|
|
if (LoadingSavegame) {
|
|
return;
|
|
}
|
|
|
|
edict->tiki = NULL;
|
|
|
|
// spawning variables
|
|
spawnflags = level.spawnflags;
|
|
level.spawnflags = 0;
|
|
|
|
// rendering variables
|
|
setAlpha(1.0f);
|
|
setScale(1.0f);
|
|
|
|
// physics variables
|
|
mass = 0;
|
|
gravity = 1.0;
|
|
groundentity = NULL;
|
|
groundcontents = 0;
|
|
velocity = vec_zero;
|
|
avelocity = vec_zero;
|
|
edict->clipmask = MASK_SOLID;
|
|
|
|
m_iNumGlues = 0;
|
|
m_bGlueAngles = true;
|
|
m_pGlueMaster = NULL;
|
|
|
|
// team variables
|
|
teamchain = NULL;
|
|
teammaster = NULL;
|
|
|
|
// bind variables
|
|
bindmaster = NULL;
|
|
|
|
// this is an generic entity
|
|
edict->s.eType = ET_GENERAL;
|
|
|
|
setContents(0);
|
|
|
|
edict->s.parent = ENTITYNUM_NONE;
|
|
edict->r.ownerNum = ENTITYNUM_NONE;
|
|
|
|
// model binding variables
|
|
numchildren = 0;
|
|
|
|
for (i = 0; i < MAX_MODEL_CHILDREN; i++) {
|
|
children[i] = ENTITYNUM_NONE;
|
|
}
|
|
|
|
for (int i = 0; i < MAX_GLUE_CHILDREN; i++) {
|
|
m_pGluesFlags[i] = 0;
|
|
}
|
|
|
|
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;
|
|
enemy = NULL;
|
|
pain_finished = 0;
|
|
damage_debounce_time = 0;
|
|
damage_type = -1;
|
|
|
|
detach_at_death = qtrue;
|
|
|
|
// Light variables
|
|
lightRadius = 0;
|
|
|
|
stealthMovementScale = 1.0f;
|
|
m_iNumBlockedPaths = 0;
|
|
m_BlockedPaths = NULL;
|
|
|
|
#ifdef OPM_FEATURES
|
|
m_bHintRequiresLookAt = true;
|
|
#endif
|
|
}
|
|
|
|
Entity::~Entity()
|
|
{
|
|
Container<Entity *> bindlist;
|
|
Container<Entity *> gluelist;
|
|
Entity *ent;
|
|
int num;
|
|
int i;
|
|
|
|
// 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 = teamchain; ent; ent = ent->teamchain) {
|
|
if (ent->bindmaster == this) {
|
|
bindlist.AddObject(ent);
|
|
}
|
|
}
|
|
|
|
num = bindlist.NumObjects();
|
|
for (i = 1; i <= num; i++) {
|
|
bindlist.ObjectAt(i)->unbind();
|
|
}
|
|
|
|
// detach all glued entities
|
|
num = 0;
|
|
for (i = 0; i < m_iNumGlues; i++) {
|
|
if (m_pGlues[i] && m_pGlues[i]->m_pGlueMaster == this) {
|
|
gluelist.AddObject(m_pGlues[i]);
|
|
}
|
|
}
|
|
|
|
num = gluelist.NumObjects();
|
|
for (i = 1; i <= num; i++) {
|
|
gluelist.ObjectAt(i)->unglue();
|
|
}
|
|
|
|
bindlist.FreeObjectList();
|
|
gluelist.FreeObjectList();
|
|
|
|
unbind();
|
|
quitTeam();
|
|
|
|
unglue();
|
|
detach();
|
|
|
|
//
|
|
// go through and set our children
|
|
//
|
|
num = numchildren;
|
|
for (i = 0; (i < MAX_MODEL_CHILDREN) && num; i++) {
|
|
if (children[i] == ENTITYNUM_NONE) {
|
|
continue;
|
|
}
|
|
ent = G_GetEntity(children[i]);
|
|
if (ent) {
|
|
ent->PostEvent(EV_Remove, 0);
|
|
}
|
|
num--;
|
|
}
|
|
|
|
if (m_BlockedPaths) {
|
|
delete[] m_BlockedPaths;
|
|
m_BlockedPaths = NULL;
|
|
}
|
|
|
|
level.FreeEdict(edict);
|
|
|
|
entflags &= ~ECF_ENTITY;
|
|
}
|
|
|
|
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::EventRevive(Event *ev)
|
|
{
|
|
float multiplier = 1.0f;
|
|
|
|
if (!ev->IsFromScript() && IsSubclassOfSentient()) {
|
|
Sentient *pThis = static_cast<Sentient *>(this);
|
|
|
|
if (pThis->m_Team == TEAM_AMERICAN) {
|
|
switch (skill->integer) {
|
|
case 0:
|
|
multiplier = 1.3f;
|
|
break;
|
|
case 1:
|
|
multiplier = 1.0f;
|
|
break;
|
|
case 2:
|
|
multiplier = 0.7f;
|
|
break;
|
|
}
|
|
} else {
|
|
switch (skill->integer) {
|
|
case 0:
|
|
multiplier = 0.7f;
|
|
break;
|
|
case 1:
|
|
multiplier = 1.0f;
|
|
break;
|
|
case 2:
|
|
multiplier = 1.3f;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
health = ev->GetFloat(1) * multiplier;
|
|
if (health <= 0.f) {
|
|
throw ScriptException("health must be greated than 0");
|
|
}
|
|
|
|
deadflag = DEAD_NO;
|
|
}
|
|
|
|
void Entity::SetHealth(Event *ev)
|
|
{
|
|
if (IsDead()) {
|
|
ScriptError("cannot give health to dead entities");
|
|
}
|
|
|
|
health = ev->GetFloat(1);
|
|
|
|
if (health <= 0) {
|
|
ScriptError("health must be greater than 0");
|
|
}
|
|
|
|
max_health = health;
|
|
}
|
|
|
|
void Entity::GetHealth(Event *ev)
|
|
{
|
|
ev->AddFloat(health);
|
|
}
|
|
|
|
void Entity::EventSetMaxHealth(Event *ev)
|
|
{
|
|
max_health = ev->GetFloat(1);
|
|
if (max_health <= 0.0f) {
|
|
ScriptError("max_health must be greater than 0");
|
|
}
|
|
|
|
if (health > max_health) {
|
|
health = max_health;
|
|
}
|
|
}
|
|
|
|
void Entity::EventGetMaxHealth(Event *ev)
|
|
{
|
|
ev->AddFloat(max_health);
|
|
}
|
|
|
|
void Entity::EventSetHealthOnly(Event *ev)
|
|
{
|
|
if (IsDead()) {
|
|
ScriptError("cannot give health to dead entities");
|
|
}
|
|
|
|
health = ev->GetFloat(1);
|
|
if (health <= 0.0f) {
|
|
ScriptError("health must be greater than 0");
|
|
}
|
|
|
|
if (health > max_health) {
|
|
health = max_health;
|
|
}
|
|
}
|
|
|
|
void Entity::GetYaw(Event *ev)
|
|
{
|
|
ev->AddFloat(angles[1]);
|
|
}
|
|
|
|
void Entity::SetKillTarget(const char *text)
|
|
|
|
{
|
|
if (text) {
|
|
killtarget = text;
|
|
} else {
|
|
killtarget = "";
|
|
}
|
|
}
|
|
|
|
const char *Entity::getModel() const
|
|
{
|
|
return gi.TIKI_NameForNum(edict->tiki);
|
|
}
|
|
|
|
void Entity::setModel(const str& mdl)
|
|
{
|
|
int animnum;
|
|
int i;
|
|
|
|
if (mdl != "" && mdl[0] == '*') {
|
|
model = mdl;
|
|
|
|
gi.SetBrushModel(edict, mdl);
|
|
if ((edict->solid == SOLID_BSP) && !edict->s.modelindex) {
|
|
const char* name;
|
|
|
|
name = getClassID();
|
|
if (!name) {
|
|
name = getClassname();
|
|
}
|
|
gi.DPrintf("%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);
|
|
}
|
|
|
|
SetSize();
|
|
return;
|
|
}
|
|
|
|
model = CanonicalTikiName(mdl);
|
|
|
|
if (!setModel()) {
|
|
Com_Printf("^~^~^ Bad model name '%s'\n", mdl.c_str());
|
|
return;
|
|
}
|
|
|
|
mins = vec_zero;
|
|
maxs = vec_zero;
|
|
|
|
// make sure to clear all frame infos when switching models
|
|
// so the entity doesn't end up with 'broken' animations
|
|
for (i = 0; i < MAX_FRAMEINFOS; i++) {
|
|
ClearAnimSlot(i);
|
|
}
|
|
|
|
if (mdl == "") {
|
|
SetSize();
|
|
return;
|
|
}
|
|
|
|
// search for the default idle animation
|
|
animnum = gi.Anim_NumForName(edict->tiki, "idle");
|
|
if (animnum < 0) {
|
|
animnum = 0;
|
|
}
|
|
|
|
// play the default idle animation
|
|
StartAnimSlot(0, animnum, 1);
|
|
// process all TIKI init commands
|
|
ProcessInitCommands();
|
|
SetSize();
|
|
|
|
if (edict->tiki && !mins.length() && !maxs.length()) {
|
|
vec3_t tempmins, tempmaxs;
|
|
gi.TIKI_CalculateBounds(edict->tiki, edict->s.scale, tempmins, tempmaxs);
|
|
setSize(tempmins, tempmaxs);
|
|
}
|
|
}
|
|
|
|
qboolean Entity::setModel(void)
|
|
{
|
|
level.skel_index[edict->s.number] = -1;
|
|
return gi.setmodel(edict, model);
|
|
}
|
|
|
|
void Entity::SetSize(void)
|
|
{
|
|
mins = edict->r.mins;
|
|
maxs = edict->r.maxs;
|
|
|
|
size = maxs - mins;
|
|
|
|
edict->r.radius = size.length() * 0.5;
|
|
edict->radius2 = edict->r.radius * edict->r.radius;
|
|
}
|
|
|
|
void Entity::ProcessInitCommands(void)
|
|
{
|
|
dtikianim_t *a;
|
|
|
|
if (LoadingSavegame) {
|
|
// 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;
|
|
}
|
|
|
|
a = edict->tiki->a;
|
|
|
|
if (a->num_server_initcmds) {
|
|
int i, j;
|
|
Event *event;
|
|
for (i = 0; i < a->num_server_initcmds; i++) {
|
|
event = new Event(a->server_initcmds[i].args[0], a->server_initcmds[i].num_args);
|
|
|
|
for (j = 1; j < a->server_initcmds[i].num_args; j++) {
|
|
event->AddToken(a->server_initcmds[i].args[j]);
|
|
}
|
|
|
|
if (!ProcessEvent(event)) {
|
|
Com_Printf(
|
|
"^~^~^ Entity::ProcessInitCommands: Bad init server command '%s' in '%s'\n",
|
|
a->server_initcmds[i].args[0],
|
|
edict->tiki->name
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Entity::EventHideModel(Event *ev)
|
|
|
|
{
|
|
hideModel();
|
|
}
|
|
|
|
void Entity::EventShowModel(Event *ev)
|
|
|
|
{
|
|
showModel();
|
|
}
|
|
|
|
void Entity::SetTeamEvent(Event *ev)
|
|
|
|
{
|
|
moveteam = ev->GetString(1);
|
|
}
|
|
|
|
void Entity::TriggerEvent(Event *ev)
|
|
{
|
|
SimpleEntity *ent;
|
|
ScriptVariable arrayVar;
|
|
int i;
|
|
|
|
arrayVar = ev->GetValue(1);
|
|
arrayVar.CastConstArrayValue();
|
|
|
|
for (i = arrayVar.arraysize(); i > 0; i--) {
|
|
const ScriptVariable *variable = arrayVar[i];
|
|
ent = variable->simpleEntityValue();
|
|
if (ent) {
|
|
Event* event = new Event(EV_Activate);
|
|
event->AddEntity(this);
|
|
ent->ProcessEvent(event);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Entity::setAlpha(float alpha)
|
|
|
|
{
|
|
if (alpha > 1.0f) {
|
|
alpha = 1.0f;
|
|
}
|
|
if (alpha < 0) {
|
|
alpha = 0;
|
|
}
|
|
edict->s.alpha = alpha;
|
|
}
|
|
|
|
void Entity::setScale(float scale)
|
|
{
|
|
edict->s.scale = scale;
|
|
}
|
|
|
|
void Entity::setContentsSolid(void)
|
|
{
|
|
setContents(CONTENTS_BBOX);
|
|
}
|
|
|
|
void Entity::setSolidType(solid_t type)
|
|
{
|
|
if ((!LoadingSavegame) && (type == SOLID_BSP) && (this != world)
|
|
&& (!model.length() || ((model[0] != '*') && (!strstr(model.c_str(), ".bsp"))))) {
|
|
gi.Printf(
|
|
"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()) {
|
|
setContentsSolid();
|
|
}
|
|
} else if (edict->solid == SOLID_NOT) {
|
|
if (getContents()
|
|
& (CONTENTS_CORPSE | CONTENTS_BODY | CONTENTS_UNKNOWN3 | CONTENTS_UNKNOWN2 | CONTENTS_NOBOTCLIP
|
|
| CONTENTS_BBOX | CONTENTS_NOTTEAM2 | CONTENTS_NOTTEAM1 | CONTENTS_LADDER)) {
|
|
setContents(0);
|
|
}
|
|
} else if (edict->solid == SOLID_BSP) {
|
|
if (!getContents()) {
|
|
setContents(CONTENTS_SOLID);
|
|
}
|
|
}
|
|
|
|
link();
|
|
}
|
|
|
|
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->mat, tempmins, tempmaxs);
|
|
|
|
VectorCopy(tempmins, edict->r.mins);
|
|
VectorCopy(tempmaxs, edict->r.maxs);
|
|
|
|
SetSize();
|
|
} else {
|
|
if ((min == edict->r.mins) && (max == edict->r.maxs)) {
|
|
return;
|
|
}
|
|
|
|
VectorCopy(min, edict->r.mins);
|
|
VectorCopy(max, edict->r.maxs);
|
|
|
|
SetSize();
|
|
|
|
//
|
|
// get the full mins and maxs for this model
|
|
//
|
|
if (edict->tiki) {
|
|
vec3_t fullmins, fullmaxs;
|
|
Vector delta;
|
|
|
|
gi.TIKI_CalculateBounds(edict->tiki, edict->s.scale, fullmins, fullmaxs);
|
|
|
|
delta = Vector(fullmaxs) - Vector(fullmins);
|
|
edict->r.radius = delta.length() * 0.5;
|
|
edict->radius2 = edict->r.radius * edict->r.radius;
|
|
}
|
|
}
|
|
|
|
link();
|
|
}
|
|
|
|
Vector Entity::getLocalVector(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)
|
|
{
|
|
gi.linkentity(edict);
|
|
absmin = edict->r.absmin;
|
|
absmax = edict->r.absmax;
|
|
centroid = (absmin + absmax) * 0.5;
|
|
centroid.copyTo(edict->r.centroid);
|
|
|
|
// If this has a parent, then set the areanum the same
|
|
// as the parent's
|
|
if (edict->s.parent != ENTITYNUM_NONE) {
|
|
edict->r.areanum = g_entities[edict->s.parent].r.areanum;
|
|
}
|
|
}
|
|
|
|
void Entity::addOrigin(Vector add)
|
|
{
|
|
setLocalOrigin(localorigin + add);
|
|
}
|
|
|
|
void Entity::setOrigin(void)
|
|
{
|
|
Entity *ent;
|
|
|
|
if (edict->s.parent == ENTITYNUM_NONE) {
|
|
setLocalOrigin(localorigin);
|
|
}
|
|
// 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 {
|
|
Vector forward, left, up;
|
|
|
|
VectorClear(edict->s.netorigin);
|
|
ent = (Entity *)G_GetEntity(edict->s.parent);
|
|
|
|
ent->GetTag(edict->s.tag_num & TAG_MASK, &origin, &forward, &left, &up);
|
|
|
|
origin += edict->s.attach_offset[0] * forward;
|
|
origin += edict->s.attach_offset[1] * left;
|
|
origin += edict->s.attach_offset[2] * up;
|
|
|
|
localorigin = vec_zero;
|
|
|
|
updateOrigin();
|
|
}
|
|
}
|
|
|
|
void Entity::updateOrigin(void)
|
|
{
|
|
Entity *ent;
|
|
int i, num;
|
|
|
|
origin.copyTo(edict->s.origin);
|
|
|
|
link();
|
|
|
|
//
|
|
// go through and set our children
|
|
//
|
|
num = numchildren;
|
|
for (i = 0; (i < MAX_MODEL_CHILDREN) && num; i++) {
|
|
if (children[i] == ENTITYNUM_NONE) {
|
|
continue;
|
|
}
|
|
ent = (Entity *)G_GetEntity(children[i]);
|
|
ent->setOrigin();
|
|
num--;
|
|
}
|
|
|
|
for (i = 0; i < m_iNumGlues; i++) {
|
|
m_pGlues[i]->setOrigin();
|
|
}
|
|
}
|
|
|
|
void Entity::setOrigin(Vector org)
|
|
{
|
|
if (m_pGlueMaster) {
|
|
org = m_pGlueMaster->origin;
|
|
velocity = m_pGlueMaster->velocity;
|
|
}
|
|
|
|
if (bindmaster) {
|
|
origin = org;
|
|
|
|
org -= bindmaster->origin;
|
|
|
|
if (bind_use_my_angles) {
|
|
MatrixTransformVectorRight(orientation, org, localorigin);
|
|
} else {
|
|
MatrixTransformVectorRight(bindmaster->orientation, org, localorigin);
|
|
}
|
|
|
|
origin.copyTo(edict->s.netorigin);
|
|
} else {
|
|
if (edict->s.parent != ENTITYNUM_NONE) {
|
|
detach();
|
|
}
|
|
|
|
origin = org;
|
|
localorigin = org;
|
|
origin.copyTo(edict->s.netorigin);
|
|
}
|
|
|
|
updateOrigin();
|
|
}
|
|
|
|
void Entity::setOriginEvent(Vector org)
|
|
{
|
|
velocity = vec_zero;
|
|
setOrigin(org);
|
|
}
|
|
|
|
void Entity::setLocalOrigin(Vector org)
|
|
{
|
|
if (m_pGlueMaster) {
|
|
org = m_pGlueMaster->origin;
|
|
velocity = m_pGlueMaster->velocity;
|
|
}
|
|
|
|
if (bindmaster) {
|
|
localorigin = org;
|
|
|
|
if (bind_use_my_angles) {
|
|
MatrixTransformVector(localorigin, orientation, origin);
|
|
} else {
|
|
MatrixTransformVector(localorigin, bindmaster->orientation, origin);
|
|
}
|
|
|
|
origin += bindmaster->origin;
|
|
origin.copyTo(edict->s.netorigin);
|
|
} else {
|
|
origin = org;
|
|
localorigin = org;
|
|
origin.copyTo(edict->s.netorigin);
|
|
}
|
|
|
|
updateOrigin();
|
|
}
|
|
|
|
void Entity::GetRawTag(int tagnum, orientation_t *orient)
|
|
{
|
|
*orient = G_TIKI_Orientation(edict, tagnum & TAG_MASK);
|
|
}
|
|
|
|
qboolean Entity::GetRawTag(const char *name, orientation_t *orient)
|
|
{
|
|
int tagnum;
|
|
|
|
tagnum = gi.Tag_NumForName(edict->tiki, name);
|
|
|
|
if (tagnum < 0) {
|
|
return false;
|
|
}
|
|
|
|
GetRawTag(tagnum, orient);
|
|
return true;
|
|
}
|
|
|
|
void Entity::GetTag(int tagnum, orientation_t *orient)
|
|
{
|
|
orientation_t o;
|
|
int i;
|
|
|
|
GetRawTag(tagnum, &o);
|
|
|
|
VectorCopy(origin, orient->origin);
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
VectorMA(orient->origin, o.origin[i], orientation[i], orient->origin);
|
|
}
|
|
MatrixMultiply(o.axis, orientation, orient->axis);
|
|
}
|
|
|
|
qboolean Entity::GetTag(const char *name, orientation_t *orient)
|
|
{
|
|
int tagnum;
|
|
|
|
tagnum = gi.Tag_NumForName(edict->tiki, 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 orient;
|
|
|
|
GetTag(tagnum, &orient);
|
|
|
|
if (pos) {
|
|
*pos = Vector(orient.origin);
|
|
}
|
|
if (forward) {
|
|
*forward = Vector(orient.axis[0]);
|
|
}
|
|
if (left) {
|
|
*left = Vector(orient.axis[1]);
|
|
}
|
|
if (up) {
|
|
*up = Vector(orient.axis[2]);
|
|
}
|
|
}
|
|
|
|
qboolean Entity::GetTag(const char *name, Vector *pos, Vector *forward, Vector *left, Vector *up)
|
|
{
|
|
int tagnum;
|
|
|
|
tagnum = gi.Tag_NumForName(edict->tiki, name);
|
|
|
|
if (tagnum < 0) {
|
|
return false;
|
|
}
|
|
|
|
GetTag(tagnum, pos, forward, left, up);
|
|
return true;
|
|
}
|
|
|
|
int Entity::CurrentAnim(int slot) const
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
float Entity::CurrentTime(int slot) const
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
void Entity::ClearAnimSlot(int slot)
|
|
{
|
|
edict->s.frameInfo[slot].index = 0;
|
|
edict->s.frameInfo[slot].weight = 0;
|
|
edict->s.frameInfo[slot].time = 0;
|
|
}
|
|
|
|
void Entity::StartAnimSlot(int slot, int index, float weight)
|
|
{
|
|
edict->s.frameInfo[slot].index = index;
|
|
edict->s.frameInfo[slot].weight = weight;
|
|
edict->s.frameInfo[slot].time = 0;
|
|
}
|
|
|
|
void Entity::RestartAnimSlot(int slot)
|
|
{
|
|
edict->s.frameInfo[slot].time = 0;
|
|
}
|
|
|
|
void Entity::addAngles(Vector add)
|
|
{
|
|
if (bindmaster) {
|
|
setAngles(localangles + add);
|
|
} else {
|
|
setAngles(angles + add);
|
|
}
|
|
}
|
|
|
|
void Entity::setAngles(void)
|
|
{
|
|
if (bindmaster) {
|
|
setAngles(localangles);
|
|
} else {
|
|
setAngles(angles);
|
|
}
|
|
}
|
|
|
|
void Entity::setAngles(Vector ang)
|
|
{
|
|
Entity *ent;
|
|
int num, i;
|
|
|
|
if (m_bGlueAngles && m_pGlueMaster) {
|
|
ang = m_pGlueMaster->angles;
|
|
avelocity = m_pGlueMaster->avelocity;
|
|
}
|
|
|
|
angles[0] = AngleMod(ang[0]);
|
|
angles[1] = AngleMod(ang[1]);
|
|
angles[2] = AngleMod(ang[2]);
|
|
|
|
localangles = angles;
|
|
if (bindmaster) {
|
|
float mat[3][3];
|
|
AnglesToAxis(localangles, mat);
|
|
R_ConcatRotations(mat, bindmaster->orientation, orientation);
|
|
MatrixToEulerAngles(orientation, angles);
|
|
} else {
|
|
AnglesToAxis(angles, orientation);
|
|
}
|
|
|
|
angles.copyTo(edict->s.netangles);
|
|
angles.copyTo(edict->s.angles);
|
|
|
|
// Fill the edicts matrix
|
|
VectorCopy(orientation[0], edict->mat[0]);
|
|
VectorCopy(orientation[1], edict->mat[1]);
|
|
VectorCopy(orientation[2], edict->mat[2]);
|
|
|
|
if (edict->s.eFlags & EF_LINKANGLES) {
|
|
angles.copyTo(edict->r.currentAngles);
|
|
link();
|
|
}
|
|
|
|
//
|
|
// go through and set our children
|
|
//
|
|
num = numchildren;
|
|
for (i = 0; (i < MAX_MODEL_CHILDREN) && num; i++) {
|
|
if (children[i] == ENTITYNUM_NONE) {
|
|
continue;
|
|
}
|
|
ent = (Entity *)G_GetEntity(children[i]);
|
|
ent->setAngles();
|
|
num--;
|
|
}
|
|
|
|
for (i = 0; i < m_iNumGlues; i++) {
|
|
m_pGlues[i]->setAngles();
|
|
}
|
|
}
|
|
|
|
qboolean Entity::droptofloor(float maxfall)
|
|
|
|
{
|
|
trace_t trace;
|
|
Vector end;
|
|
Vector start;
|
|
|
|
start = origin + Vector("0 0 1");
|
|
end = origin;
|
|
end[2] -= maxfall;
|
|
|
|
trace = G_Trace(start, mins, maxs, end, this, edict->clipmask, false, "Entity::droptofloor");
|
|
if (trace.fraction == 1 || trace.startsolid || trace.allsolid || !trace.ent) {
|
|
return false;
|
|
}
|
|
|
|
setOrigin(trace.endpos);
|
|
|
|
return true;
|
|
}
|
|
|
|
void Entity::DamageType(Event *ev)
|
|
|
|
{
|
|
str damage;
|
|
damage = ev->GetString(1);
|
|
if (damage == "all") {
|
|
damage_type = -1;
|
|
} else {
|
|
damage_type = MOD_string_to_int(damage);
|
|
}
|
|
}
|
|
|
|
void Entity::Damage(
|
|
Entity *inflictor,
|
|
Entity *attacker,
|
|
float damage,
|
|
Vector position,
|
|
Vector direction,
|
|
Vector normal,
|
|
int knockback,
|
|
int dflags,
|
|
int meansofdeath,
|
|
int location
|
|
)
|
|
{
|
|
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->AddEntity(attacker);
|
|
ev->AddFloat(damage);
|
|
ev->AddEntity(inflictor);
|
|
ev->AddVector(position);
|
|
ev->AddVector(direction);
|
|
ev->AddVector(normal);
|
|
ev->AddInteger(knockback);
|
|
ev->AddInteger(dflags);
|
|
ev->AddInteger(meansofdeath);
|
|
ev->AddInteger(location);
|
|
ProcessEvent(ev);
|
|
}
|
|
|
|
void Entity::DamageEvent(Event *ev)
|
|
{
|
|
Entity *inflictor;
|
|
Entity *attacker;
|
|
int damage;
|
|
Vector dir;
|
|
Vector momentum;
|
|
Vector position, direction, normal;
|
|
int knockback, damageflags, meansofdeath, location;
|
|
Event event;
|
|
float m;
|
|
EntityPtr This;
|
|
|
|
// Use a safe pointer because it might be deleted in Unregister
|
|
This = this;
|
|
|
|
if ((takedamage == DAMAGE_NO) || (movetype == MOVETYPE_NOCLIP) || health <= 0) {
|
|
return;
|
|
}
|
|
|
|
attacker = ev->GetEntity(1);
|
|
damage = ev->GetInteger(2);
|
|
inflictor = ev->GetEntity(3);
|
|
meansofdeath = ev->GetInteger(9);
|
|
|
|
if (Immune(meansofdeath)) {
|
|
return;
|
|
}
|
|
|
|
if (!attacker) {
|
|
ScriptError("attacker is NULL");
|
|
return;
|
|
}
|
|
|
|
if (!inflictor) {
|
|
ScriptError("inflictor is NULL");
|
|
return;
|
|
}
|
|
|
|
position = ev->GetVector(4);
|
|
direction = ev->GetVector(5);
|
|
normal = ev->GetVector(6);
|
|
|
|
knockback = ev->GetInteger(7);
|
|
damageflags = ev->GetInteger(8);
|
|
location = ev->GetInteger(10);
|
|
|
|
// figure momentum add
|
|
if ((inflictor != world) && (movetype != MOVETYPE_NONE) && (movetype != MOVETYPE_STATIONARY)
|
|
&& (movetype != MOVETYPE_BOUNCE) && (movetype != MOVETYPE_PUSH) && (movetype != MOVETYPE_STOP)
|
|
&& (movetype != MOVETYPE_VEHICLE)) {
|
|
dir = origin - (inflictor->origin + (inflictor->mins + inflictor->maxs) * 0.5);
|
|
dir.normalize();
|
|
|
|
if (mass < 20) {
|
|
m = 20;
|
|
} else {
|
|
m = mass;
|
|
}
|
|
|
|
momentum = dir * damage * (1000.0 / m);
|
|
velocity += momentum;
|
|
}
|
|
|
|
if (!deathmatch->integer && IsSubclassOfPlayer()) {
|
|
damage *= 0.15;
|
|
}
|
|
|
|
// check for godmode or invincibility
|
|
if (!(flags & FL_GODMODE)) {
|
|
// do the damage
|
|
health -= damage;
|
|
}
|
|
|
|
/*
|
|
if (deadflag) {
|
|
// Check for gib.
|
|
if (inflictor->IsSubclassOfProjectile()) {
|
|
Event *gibEv;
|
|
|
|
health -= damage;
|
|
|
|
gibEv = new Event(EV_Gib);
|
|
gibEv->AddEntity(this);
|
|
gibEv->AddFloat(health);
|
|
|
|
ProcessEvent(gibEv);
|
|
}
|
|
|
|
return;
|
|
}
|
|
*/
|
|
|
|
if (health <= 0) {
|
|
if (attacker) {
|
|
const EntityPtr attackerPtr = attacker;
|
|
|
|
event = Event(EV_GotKill, 5);
|
|
event.AddEntity(this);
|
|
event.AddInteger(damage);
|
|
event.AddEntity(inflictor);
|
|
event.AddInteger(meansofdeath);
|
|
event.AddInteger(0);
|
|
|
|
attackerPtr->ProcessEvent(event);
|
|
if (attackerPtr) {
|
|
attackerPtr->delegate_gotKill.Execute(event);
|
|
}
|
|
}
|
|
|
|
if (!This) {
|
|
return;
|
|
}
|
|
|
|
event = Event(EV_Killed, 10);
|
|
event.AddEntity(attacker);
|
|
event.AddFloat(damage);
|
|
event.AddEntity(inflictor);
|
|
event.AddVector(position);
|
|
event.AddVector(direction);
|
|
event.AddVector(normal);
|
|
event.AddInteger(knockback);
|
|
event.AddInteger(damageflags);
|
|
event.AddInteger(meansofdeath);
|
|
event.AddInteger(location);
|
|
|
|
ProcessEvent(event);
|
|
if (!This) {
|
|
return;
|
|
}
|
|
|
|
// Notify scripts
|
|
Unregister(STRING_DAMAGE);
|
|
if (!This) {
|
|
return;
|
|
}
|
|
|
|
delegate_killed.Execute(event);
|
|
return;
|
|
}
|
|
|
|
event = Event(EV_Pain, 10);
|
|
event.AddEntity(attacker);
|
|
event.AddFloat(damage);
|
|
event.AddEntity(inflictor);
|
|
event.AddVector(position);
|
|
event.AddVector(direction);
|
|
event.AddVector(normal);
|
|
event.AddInteger(knockback);
|
|
event.AddInteger(damageflags);
|
|
event.AddInteger(meansofdeath);
|
|
event.AddInteger(location);
|
|
|
|
ProcessEvent(event);
|
|
if (!This) {
|
|
return;
|
|
}
|
|
|
|
// Notify scripts
|
|
Unregister(STRING_DAMAGE);
|
|
if (!This) {
|
|
return;
|
|
}
|
|
|
|
delegate_damage.Execute(event);
|
|
}
|
|
|
|
qboolean Entity::IsTouching(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;
|
|
}
|
|
|
|
qboolean Entity::IsInside(Entity *e1)
|
|
{
|
|
return gi.HitEntity(edict, e1->edict);
|
|
}
|
|
|
|
qboolean Entity::FovCheck(float *delta, float fovdot)
|
|
{
|
|
float deltadot = DotProduct2D(delta, orientation[0]);
|
|
|
|
if (fovdot < 0.0f) {
|
|
if (deltadot >= 0.0f) {
|
|
return true;
|
|
}
|
|
|
|
return VectorLength2DSquared(delta) * Square(fovdot) > Square(deltadot);
|
|
} else {
|
|
if (deltadot < 0.0f) {
|
|
return false;
|
|
}
|
|
|
|
return VectorLength2DSquared(delta) * Square(fovdot) < Square(deltadot);
|
|
}
|
|
}
|
|
|
|
bool Entity::CanSee(Entity *ent, float fov, float vision_distance, bool bNoEnts)
|
|
{
|
|
vec2_t delta;
|
|
int mask;
|
|
|
|
VectorSub2D(ent->centroid, centroid, delta);
|
|
|
|
if (vision_distance > 0 && Square(vision_distance) < VectorLength2DSquared(delta)) {
|
|
return false;
|
|
}
|
|
|
|
if (!AreasConnected(ent)) {
|
|
return false;
|
|
}
|
|
|
|
if (fov > 0 && fov < 360 && !FovCheck(delta, cos(DEG2RAD(fov / 2.f)))) {
|
|
return false;
|
|
}
|
|
|
|
if (bNoEnts) {
|
|
mask = MASK_CANSEE_NOENTS;
|
|
} else {
|
|
mask = MASK_CANSEE;
|
|
}
|
|
|
|
return G_SightTrace(centroid, vec_zero, vec_zero, ent->centroid, this, ent, mask, qfalse, "Sentient::CanSee");
|
|
}
|
|
|
|
bool Entity::CanSee(const Vector& org, float fov, float vision_distance, bool bNoEnts)
|
|
{
|
|
vec2_t delta;
|
|
int mask;
|
|
|
|
VectorSub2D(org, centroid, delta);
|
|
|
|
if (vision_distance > 0 && Square(vision_distance) < VectorLength2DSquared(delta)) {
|
|
return false;
|
|
}
|
|
|
|
if (fov > 0 && fov < 360 && !FovCheck(delta, cos(DEG2RAD(fov / 2.f)))) {
|
|
return false;
|
|
}
|
|
|
|
if (bNoEnts) {
|
|
mask = MASK_CANSEE_NOENTS;
|
|
} else {
|
|
mask = MASK_CANSEE;
|
|
}
|
|
|
|
return G_SightTrace(centroid, vec_zero, vec_zero, org, this, NULL, mask, qfalse, "Sentient::CanSee");
|
|
}
|
|
|
|
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) {
|
|
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) {
|
|
myscale = 0;
|
|
}
|
|
if (myalpha < 0) {
|
|
myalpha = 0;
|
|
}
|
|
|
|
if (myscale <= 0 && myalpha <= 0) {
|
|
PostEvent(EV_Remove, 0);
|
|
} 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);
|
|
if (rate > 0) {
|
|
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);
|
|
if (rate > 0) {
|
|
rate = FRAMETIME / rate;
|
|
}
|
|
} else {
|
|
rate = 0.03f;
|
|
}
|
|
|
|
myalpha = edict->s.alpha;
|
|
myalpha -= rate;
|
|
|
|
if (myalpha <= 0) {
|
|
PostEvent(EV_Remove, 0);
|
|
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::Sink(Event *ev)
|
|
{
|
|
float rate;
|
|
float target;
|
|
float myalpha;
|
|
Vector org;
|
|
|
|
rate = 0.3f;
|
|
|
|
if (ev->NumArgs() > 1) {
|
|
target = ev->GetFloat(2);
|
|
} else {
|
|
target = 0;
|
|
}
|
|
|
|
if (ev->NumArgs() > 0) {
|
|
rate = ev->GetFloat(1);
|
|
if (rate > 0) {
|
|
rate /= level.frametime;
|
|
}
|
|
}
|
|
|
|
myalpha = edict->s.alpha - rate;
|
|
if (myalpha > 0) {
|
|
if (myalpha < target) {
|
|
myalpha = target;
|
|
}
|
|
|
|
if (target < myalpha) {
|
|
Event *event = new Event(std::move(*ev));
|
|
PostEvent(event, FRAMETIME);
|
|
}
|
|
|
|
setAlpha(myalpha);
|
|
|
|
org = origin;
|
|
setOrigin(org);
|
|
|
|
Com_Printf("This is the sink?!? alpha: %f origin: %f %f %f\n", myalpha, org.x, org.y, org.z);
|
|
G_SetConstantLight(&edict->s.constantLight, &myalpha, &myalpha, &myalpha, NULL, NULL);
|
|
edict->s.alpha = myalpha;
|
|
} else {
|
|
// No alpha, the entity can be immediately removed
|
|
PostEvent(EV_Remove, 0);
|
|
}
|
|
}
|
|
|
|
void Entity::SetMassEvent(Event *ev)
|
|
|
|
{
|
|
mass = ev->GetFloat(1);
|
|
}
|
|
|
|
void Entity::CheckGround(void)
|
|
{
|
|
Vector point;
|
|
trace_t trace;
|
|
|
|
if (flags & (FL_SWIM | FL_FLY)) {
|
|
return;
|
|
}
|
|
|
|
if (velocity.z > 100) {
|
|
groundentity = NULL;
|
|
return;
|
|
}
|
|
|
|
// if the hull point one-quarter unit down is solid the entity is on ground
|
|
point = origin;
|
|
point.z -= 0.25;
|
|
trace = G_Trace(origin, mins, maxs, point, this, edict->clipmask, false, "Entity::CheckGround");
|
|
|
|
// check steepness
|
|
if ((trace.plane.normal[2] <= 0.7) && !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::SafeSolid(Event *ev)
|
|
{
|
|
trace_t trace;
|
|
|
|
setSolidType(SOLID_NOT);
|
|
|
|
trace = G_Trace(origin, mins, maxs, origin, this, MASK_SAFESOLID, qfalse, "Entity::SafeSolid");
|
|
|
|
if ((trace.startsolid || trace.allsolid) && trace.ent && (trace.ent->entity->IsSubclassOfSentient())) {
|
|
// Try again
|
|
PostEvent(EV_SafeSolid, 0.5f);
|
|
} else {
|
|
ProcessEvent(EV_BecomeSolid);
|
|
}
|
|
}
|
|
|
|
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->r.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;
|
|
}
|
|
|
|
if (level.spawning) {
|
|
// If the server is currently spawning
|
|
// repost the event as loopsound wouldn't work properly
|
|
// on the client
|
|
Event* newev = new Event(*ev);
|
|
PostEvent(newev, level.frametime);
|
|
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);
|
|
}
|
|
}
|
|
|
|
// Add this sound to loop
|
|
|
|
LoopSound(sound_name.c_str(), volume, min_dist);
|
|
}
|
|
|
|
void Entity::LoopSound(str sound_name, float volume, float min_dist, float max_dist, float pitch)
|
|
{
|
|
str name;
|
|
str random_alias;
|
|
AliasListNode_t *ret = NULL;
|
|
|
|
// Get the real sound to be played
|
|
|
|
if (sound_name.length() > 0) {
|
|
// Get the real sound to play
|
|
|
|
name = GetRandomAlias(sound_name, &ret).c_str();
|
|
|
|
if (!name) {
|
|
name = sound_name.c_str();
|
|
}
|
|
|
|
if (name.length() && ret) {
|
|
int aliaschannel = ret->channel;
|
|
float aliasvolume = G_Random() * ret->volumeMod + ret->volume;
|
|
float aliasmin_dist = ret->dist;
|
|
float aliasmax_dist = ret->maxDist;
|
|
float aliaspitch = G_Random() * ret->pitchMod + ret->pitch;
|
|
int aliasstreamed = ret->streamed;
|
|
// Add the looping sound to the entity
|
|
|
|
edict->s.loopSound = gi.soundindex(name, aliasstreamed);
|
|
edict->s.loopSoundVolume = volume < 0 ? (aliasvolume) : (aliasvolume * volume);
|
|
edict->s.loopSoundMinDist = min_dist < 0 ? (aliasmin_dist) : (min_dist);
|
|
edict->s.loopSoundMaxDist = max_dist < 0 ? (aliasmax_dist) : (max_dist);
|
|
edict->s.loopSoundPitch = pitch < 0 ? (aliaspitch) : (aliaspitch * pitch);
|
|
|
|
// Local sound will always be heard
|
|
edict->s.loopSoundFlags = aliaschannel == CHAN_LOCAL;
|
|
} else {
|
|
gi.DPrintf(
|
|
"ERROR: Entity::LoopSound: %s needs an alias in ubersound.scr or uberdialog.scr - Please fix.\n",
|
|
sound_name.c_str()
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Entity::StopLoopSound(Event *ev)
|
|
{
|
|
StopLoopSound();
|
|
}
|
|
|
|
void Entity::StopLoopSound(void)
|
|
{
|
|
edict->s.loopSound = 0;
|
|
}
|
|
|
|
void Entity::ProcessSoundEvent(Event *ev, qboolean checkSubtitle)
|
|
{
|
|
str sound_name;
|
|
str wait;
|
|
bool waitTillDone = false;
|
|
float volume;
|
|
int channel = 0;
|
|
|
|
// Set defaults
|
|
volume = DEFAULT_VOL;
|
|
|
|
if (ev->NumArgs() > 3) {
|
|
Com_Printf("ERROR Sound. Format is playsound soundname <wait>\n");
|
|
return;
|
|
}
|
|
|
|
// Get sound parameters
|
|
|
|
sound_name = ev->GetString(1);
|
|
|
|
if (ev->NumArgs() > 1) {
|
|
wait = ev->GetString(2);
|
|
}
|
|
|
|
if (!Q_stricmp(wait.c_str(), "wait")) {
|
|
waitTillDone = true;
|
|
|
|
if (ev->NumArgs() == 3) {
|
|
volume = ev->GetFloat(3);
|
|
}
|
|
} else if (!Q_stricmp(wait.c_str(), "volume")) {
|
|
if (ev->NumArgs() != 3) {
|
|
ScriptError("Entity::Sound: volume not followed by a float");
|
|
}
|
|
|
|
volume = ev->GetFloat(3);
|
|
}
|
|
|
|
if (volume == DEFAULT_VOL) {
|
|
Sound(sound_name, CHAN_AUTO, volume, 0, NULL, 0, 0, waitTillDone, checkSubtitle, -1.0f);
|
|
} else {
|
|
Sound(sound_name, -1, volume, -1.0f, NULL, -1.0f, 1, waitTillDone, checkSubtitle, -1.0f);
|
|
}
|
|
}
|
|
|
|
void Entity::Sound(Event *ev)
|
|
{
|
|
if (level.spawning) {
|
|
Event *event = new Event(EV_Sound);
|
|
|
|
for (int i = 1; i <= ev->NumArgs(); i++) {
|
|
event->AddValue(ev->GetValue(i));
|
|
}
|
|
|
|
PostEvent(event, level.frametime);
|
|
} else {
|
|
ProcessSoundEvent(ev, true);
|
|
}
|
|
}
|
|
|
|
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::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->AddVector(mins);
|
|
}
|
|
|
|
void Entity::GetMaxs(Event *ev)
|
|
{
|
|
ev->AddVector(maxs);
|
|
}
|
|
|
|
void Entity::SetScale(Event *ev)
|
|
{
|
|
setScale(ev->GetFloat(1));
|
|
}
|
|
|
|
void Entity::GetScale(Event *ev)
|
|
{
|
|
ev->AddFloat(edict->s.scale);
|
|
}
|
|
|
|
void Entity::SetAlpha(Event *ev)
|
|
|
|
{
|
|
setAlpha(ev->GetFloat(1));
|
|
}
|
|
|
|
void Entity::SetKillTarget(Event *ev)
|
|
|
|
{
|
|
SetKillTarget(ev->GetString(1));
|
|
}
|
|
|
|
Vector Entity::GetControllerAngles(int num)
|
|
{
|
|
Vector controller_angles;
|
|
|
|
assert((num >= 0) && (num < NUM_BONE_CONTROLLERS));
|
|
|
|
if ((num < 0) || (num >= NUM_BONE_CONTROLLERS)) {
|
|
gi.Printf("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)) {
|
|
gi.Printf("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;
|
|
|
|
num = ev->GetInteger(1);
|
|
angles = ev->GetVector(2);
|
|
|
|
// this check is missing in mohaa
|
|
if ((num < 0) || (num >= NUM_BONE_CONTROLLERS)) {
|
|
ScriptError("Bone controller index out of range. Index must be between 0-" STRING(NUM_BONE_CONTROLLERS - 1) "."
|
|
);
|
|
}
|
|
|
|
VectorCopy(angles, edict->s.bone_angles[num]);
|
|
EulerToQuat(edict->s.bone_angles[num], edict->s.bone_quat[num]);
|
|
}
|
|
|
|
void Entity::GetControllerAngles(Event *ev)
|
|
{
|
|
int num = ev->GetInteger(1);
|
|
|
|
// this check is missing in mohaa
|
|
if ((num < 0) || (num >= NUM_BONE_CONTROLLERS)) {
|
|
ScriptError("Bone controller index out of range. Index must be between 0-" STRING(NUM_BONE_CONTROLLERS - 1) "."
|
|
);
|
|
}
|
|
|
|
ev->AddVector(edict->s.bone_angles[num]);
|
|
}
|
|
|
|
void Entity::SetControllerTag(int num, int tag_num)
|
|
{
|
|
assert((num >= 0) && (num < NUM_BONE_CONTROLLERS));
|
|
|
|
if ((num < 0) || (num >= NUM_BONE_CONTROLLERS)) {
|
|
gi.Printf("SetControllerTag", "Bone controller index out of range (%d)\n", num);
|
|
return;
|
|
}
|
|
|
|
edict->s.bone_tag[num] = tag_num;
|
|
}
|
|
|
|
void Entity::Sound(
|
|
str sound_name,
|
|
int channel,
|
|
float volume,
|
|
float min_dist,
|
|
Vector *sound_origin,
|
|
float pitch,
|
|
int argstype,
|
|
int doCallback,
|
|
int checkSubtitle,
|
|
float max_dist
|
|
)
|
|
{
|
|
const char *name = NULL;
|
|
vec3_t org;
|
|
str random_alias;
|
|
AliasListNode_t *ret = NULL;
|
|
static cvar_t *g_subtitle = NULL;
|
|
static bool _tmp = false;
|
|
int num;
|
|
int aliaschannel;
|
|
float aliasvolume;
|
|
float aliasmin_dist;
|
|
float aliasmax_dist;
|
|
float aliaspitch;
|
|
|
|
if (!_tmp) {
|
|
g_subtitle = gi.Cvar_Get("g_subtitle", "0", CVAR_ARCHIVE);
|
|
_tmp = true;
|
|
}
|
|
|
|
if (doCallback && g_gametype->integer != GT_SINGLE_PLAYER) {
|
|
Com_Printf("^~^~^ Callback of sound '%s' ignored.\n", sound_name.c_str());
|
|
doCallback = 0;
|
|
}
|
|
|
|
if (sound_name.length() > 0) {
|
|
// Get the real sound to play
|
|
|
|
random_alias = GetRandomAlias(sound_name, &ret).c_str();
|
|
|
|
if (random_alias.length() > 0) {
|
|
name = random_alias.c_str();
|
|
}
|
|
|
|
if (!name) {
|
|
name = gi.GlobalAlias_FindRandom(sound_name.c_str(), &ret);
|
|
}
|
|
|
|
// Play the sound
|
|
|
|
if (name && ret) {
|
|
aliaschannel = ret->channel;
|
|
aliasvolume = G_Random() * ret->volumeMod + ret->volume;
|
|
aliaspitch = G_Random() * ret->pitchMod + ret->pitch;
|
|
aliasmin_dist = ret->dist;
|
|
aliasmax_dist = ret->maxDist;
|
|
|
|
if (channel < 0) {
|
|
channel = ret->channel;
|
|
}
|
|
|
|
num = entnum;
|
|
if (sound_origin) {
|
|
VectorCopy(*sound_origin, org);
|
|
num = ENTITYNUM_NONE;
|
|
} else {
|
|
VectorCopy(edict->s.origin, org);
|
|
|
|
if (doCallback) {
|
|
num |= S_FLAG_DO_CALLBACK;
|
|
}
|
|
}
|
|
|
|
switch (argstype) {
|
|
case 0:
|
|
volume = aliasvolume;
|
|
pitch = aliaspitch;
|
|
min_dist = aliasmin_dist;
|
|
max_dist = aliasmax_dist;
|
|
channel = aliaschannel;
|
|
break;
|
|
case 1:
|
|
if (volume >= 0.0f) {
|
|
volume = volume * aliasvolume;
|
|
} else {
|
|
volume = aliasvolume;
|
|
}
|
|
|
|
if (pitch >= 0.0f) {
|
|
pitch = pitch * aliaspitch;
|
|
} else {
|
|
pitch = aliaspitch;
|
|
}
|
|
|
|
if (min_dist < 0.0f) {
|
|
min_dist = aliasmin_dist;
|
|
}
|
|
|
|
if (max_dist < 0.0f) {
|
|
max_dist = aliasmax_dist;
|
|
}
|
|
|
|
if (channel < 0) {
|
|
channel = aliaschannel;
|
|
}
|
|
break;
|
|
default:
|
|
if (volume < 0.0) {
|
|
volume = aliasvolume;
|
|
}
|
|
if (pitch < 0.0) {
|
|
pitch = aliaspitch;
|
|
}
|
|
if (min_dist < 0.0) {
|
|
min_dist = aliasmin_dist;
|
|
}
|
|
if (max_dist < 0.0) {
|
|
max_dist = aliasmax_dist;
|
|
}
|
|
if (channel < 0) {
|
|
channel = aliaschannel;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (g_gametype->integer == GT_SINGLE_PLAYER && (ret->forcesubtitle || !checkSubtitle || g_subtitle->integer) && ret->subtitle) {
|
|
Entity *p = G_GetEntity(0);
|
|
|
|
if (p && (g_subtitle->integer == 2 || Square(max_dist) > DistanceSquared(org, p->edict->s.origin))) {
|
|
cvar_t *curSubtitle = gi.Cvar_Get("curSubtitle", "0", 0);
|
|
int curSub;
|
|
|
|
gi.cvar_set(va("subtitle%d", curSubtitle->integer), va("%s", ret->subtitle));
|
|
curSub = curSubtitle->integer + 1;
|
|
if (curSubtitle->integer + 1 < 0) {
|
|
curSub = curSubtitle->integer + MAX_SUBTITLES;
|
|
}
|
|
|
|
gi.cvar_set("curSubtitle", va("%d", (curSubtitle->integer + 1) - MAX_SUBTITLES * (curSub >> 2)));
|
|
}
|
|
}
|
|
|
|
gi.Sound(&org, num, channel, name, volume, min_dist, pitch, max_dist, ret->streamed);
|
|
} else {
|
|
gi.DPrintf(
|
|
"ERROR: Entity::Sound: %s needs an alias in ubersound.scr or uberdialog.scr - Please fix.\n",
|
|
sound_name.c_str()
|
|
);
|
|
}
|
|
} else {
|
|
warning("Sound", "Null sample pointer");
|
|
}
|
|
}
|
|
|
|
qboolean Entity::attach(int parent_entity_num, int tag_num, qboolean use_angles, Vector 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->numchildren < MAX_MODEL_CHILDREN) {
|
|
//
|
|
// find a free spot in the parent
|
|
//
|
|
for (i = 0; i < MAX_MODEL_CHILDREN; i++) {
|
|
if (parent->children[i] == ENTITYNUM_NONE) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
edict->s.parent = parent_entity_num;
|
|
setSolidType(SOLID_NOT);
|
|
parent->children[i] = entnum;
|
|
parent->numchildren++;
|
|
edict->s.tag_num = tag_num;
|
|
edict->s.attach_use_angles = use_angles;
|
|
offset.copyTo(edict->s.attach_offset);
|
|
setOrigin();
|
|
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void Entity::KillAttach(Event *ev)
|
|
|
|
{
|
|
int i;
|
|
Entity *child = NULL;
|
|
|
|
// Kill all of this entities children
|
|
|
|
for (i = 0; i < MAX_MODEL_CHILDREN; i++) {
|
|
if (children[i] != ENTITYNUM_NONE) {
|
|
// Remove child
|
|
child = (Entity *)G_GetEntity(children[i]);
|
|
|
|
if (child) {
|
|
child->ProcessEvent(EV_Remove);
|
|
}
|
|
|
|
// Remove child from this entity
|
|
children[i] = ENTITYNUM_NONE;
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
for (i = 0, num = parent->numchildren; i < MAX_MODEL_CHILDREN; i++) {
|
|
if (parent->children[i] == ENTITYNUM_NONE) {
|
|
continue;
|
|
}
|
|
if (parent->children[i] == entnum) {
|
|
parent->children[i] = ENTITYNUM_NONE;
|
|
parent->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:
|
|
gi.Printf("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 (!Q_stricmp(flag, "blood")) {
|
|
mask = FL_BLOOD;
|
|
} else if (!Q_stricmp(flag, "explode")) {
|
|
mask = FL_DIE_EXPLODE;
|
|
} else if (!Q_stricmp(flag, "die_gibs")) {
|
|
mask = FL_DIE_GIBS;
|
|
} else if (!Q_stricmp(flag, "autoaim")) {
|
|
mask = FL_AUTOAIM;
|
|
} else if (!Q_stricmp(flag, "god")) {
|
|
mask = FL_GODMODE;
|
|
} else {
|
|
mask = 0;
|
|
action = FLAG_IGNORE;
|
|
gi.Printf("Unknown flag '%s'", flag);
|
|
}
|
|
switch (action) {
|
|
case FLAG_ADD:
|
|
flags |= mask;
|
|
break;
|
|
case FLAG_CLEAR:
|
|
flags &= ~mask;
|
|
break;
|
|
case FLAG_IGNORE:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
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:
|
|
gi.Printf("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 (!Q_stricmp(flag, "everyframe")) {
|
|
mask = EF_EVERYFRAME;
|
|
}
|
|
if (!Q_stricmp(flag, "antisbjuice")) {
|
|
mask = EF_ANTISBJUICE;
|
|
} else {
|
|
action = FLAG_IGNORE;
|
|
gi.Printf("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:
|
|
gi.Printf("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 (!Q_stricmp(flag, "dontdraw")) {
|
|
mask = RF_DONTDRAW;
|
|
} else if (!Q_stricmp(flag, "betterlighting")) {
|
|
mask = RF_EXTRALIGHT;
|
|
} else if (!Q_stricmp(flag, "lensflare")) {
|
|
mask = RF_LENSFLARE;
|
|
} else if (!Q_stricmp(flag, "viewlensflare")) {
|
|
mask = RF_VIEWLENSFLARE;
|
|
} else if (!Q_stricmp(flag, "lightoffset")) {
|
|
mask = RF_LIGHTOFFSET;
|
|
} else if (!Q_stricmp(flag, "skyorigin")) {
|
|
mask = RF_SKYORIGIN;
|
|
} else if (!Q_stricmp(flag, "fullbright")) {
|
|
mask = RF_FULLBRIGHT;
|
|
} else if (!Q_stricmp(flag, "minlight")) {
|
|
mask = RF_MINLIGHT;
|
|
} else if (!Q_stricmp(flag, "additivedynamiclight")) {
|
|
mask = RF_ADDITIVE_DLIGHT;
|
|
} else if (!Q_stricmp(flag, "lightstyledynamiclight")) {
|
|
mask = RF_LIGHTSTYLE_DLIGHT;
|
|
} else if (!Q_stricmp(flag, "shadow")) {
|
|
mask = RF_SHADOW;
|
|
} else if (!Q_stricmp(flag, "preciseshadow")) {
|
|
mask = RF_SHADOW_PRECISE;
|
|
} else if (!Q_stricmp(flag, "invisible")) {
|
|
mask = RF_INVISIBLE;
|
|
} else {
|
|
action = FLAG_IGNORE;
|
|
gi.Printf("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;
|
|
Entity *ent = NULL;
|
|
int singleClient = 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:
|
|
gi.Printf("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 (!Q_stricmp(flag, "broadcast")) {
|
|
mask = SVF_BROADCAST;
|
|
} else if (!Q_stricmp(flag, "sendonce")) {
|
|
mask = SVF_SENDONCE;
|
|
} else if (!Q_stricmp(flag, "singleclient")) {
|
|
mask = SVF_SINGLECLIENT;
|
|
if (action == FLAG_ADD) {
|
|
ent = ev->GetEntity(i + 1);
|
|
|
|
if (!ent) {
|
|
ScriptError("No player specified for +singleclient!");
|
|
}
|
|
}
|
|
|
|
i++;
|
|
} else if (!Q_stricmp(flag, "notsingleclient")) {
|
|
mask = SVF_NOTSINGLECLIENT;
|
|
if (action == FLAG_ADD) {
|
|
ent = ev->GetEntity(i + 1);
|
|
|
|
if (!ent) {
|
|
ScriptError("No player specified for +notsingleclient!");
|
|
}
|
|
}
|
|
|
|
i++;
|
|
} else {
|
|
action = FLAG_IGNORE;
|
|
gi.Printf("Entity::SVFlags: Unknown token %s.\n", flag);
|
|
}
|
|
|
|
switch (action) {
|
|
case FLAG_ADD:
|
|
edict->r.svFlags |= mask;
|
|
|
|
if ((mask & SVF_SINGLECLIENT) || (mask & SVF_NOTSINGLECLIENT)) {
|
|
edict->r.singleClient = singleClient;
|
|
}
|
|
|
|
break;
|
|
case FLAG_CLEAR:
|
|
edict->r.svFlags &= ~mask;
|
|
|
|
if ((mask & SVF_SINGLECLIENT) || (mask & SVF_NOTSINGLECLIENT)) {
|
|
edict->r.singleClient = 0;
|
|
}
|
|
|
|
break;
|
|
case FLAG_IGNORE:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (edict->r.svFlags & SVF_SENDONCE) {
|
|
// Turn this entity into an event if the SENDONCE flag is sent
|
|
edict->s.eType = ET_EVENTS;
|
|
}
|
|
}
|
|
|
|
void Entity::BroadcastAIEvent(int iType, float rad)
|
|
{
|
|
if (flags & FL_NOTARGET) {
|
|
return;
|
|
}
|
|
|
|
G_BroadcastAIEvent(this, centroid, iType, rad);
|
|
}
|
|
|
|
void Entity::BroadcastAIEvent(Event *ev)
|
|
{
|
|
int iType;
|
|
float rad;
|
|
str sTypeName;
|
|
|
|
if (!(this->flags & FL_NOTARGET)) {
|
|
iType = AI_EVENT_MISC;
|
|
|
|
if (ev->NumArgs() > 0) {
|
|
sTypeName = ev->GetString(1);
|
|
iType = G_AIEventTypeFromString(sTypeName);
|
|
|
|
rad = ev->NumArgs() <= 1 ? G_AIEventRadius(iType) : ev->GetFloat(2);
|
|
}
|
|
|
|
BroadcastAIEvent(iType, rad);
|
|
}
|
|
}
|
|
|
|
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;
|
|
Damage(this, this, 10, origin, vec_zero, vec_zero, 0, 0, MOD_SUICIDE);
|
|
}
|
|
|
|
void Entity::Killed(Event *ev)
|
|
{
|
|
deadflag = DEAD_DEAD;
|
|
if (health > 0) {
|
|
health = 0;
|
|
}
|
|
}
|
|
|
|
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->tiki, surf_name);
|
|
|
|
if (surface_num < 0) {
|
|
warning("SurfaceCommand", "group %s not found.", surf_name);
|
|
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 '+'\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 (!Q_stricmp(token, "skin1")) {
|
|
mask = MDL_SURFACE_SKINOFFSET_BIT0;
|
|
} else if (!Q_stricmp(token, "skin2")) {
|
|
mask = MDL_SURFACE_SKINOFFSET_BIT1;
|
|
} else if (!Q_stricmp(token, "nodraw")) {
|
|
mask = MDL_SURFACE_NODRAW;
|
|
} else if (!Q_stricmp(token, "crossfade")) {
|
|
mask = MDL_SURFACE_CROSSFADE_SKINS;
|
|
} else {
|
|
mask = 0;
|
|
warning("SurfaceCommand", "Unknown token %s.", token);
|
|
action = FLAG_IGNORE;
|
|
}
|
|
|
|
int numsurfaces;
|
|
if (edict->tiki) {
|
|
numsurfaces = gi.TIKI_NumSurfaces(edict->tiki);
|
|
} else {
|
|
numsurfaces = 0;
|
|
}
|
|
|
|
for (; surface_num < numsurfaces; surface_num++) {
|
|
if (mult) {
|
|
current_surface_name = gi.Surface_NumToName(edict->tiki, 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)
|
|
{
|
|
str surf_name;
|
|
str 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;
|
|
str bone;
|
|
int tagnum;
|
|
qboolean use_angles = qtrue;
|
|
Vector 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 (!parent) {
|
|
return;
|
|
}
|
|
|
|
tagnum = gi.Tag_NumForName(parent->edict->tiki, bone);
|
|
if (tagnum >= 0) {
|
|
attach(parent->entnum, tagnum, use_angles, offset);
|
|
} else {
|
|
warning("AttachEvent", "Tag %s not found", bone.c_str());
|
|
}
|
|
}
|
|
|
|
void Entity::AttachModelEvent(Event *ev)
|
|
{
|
|
Animate *obj;
|
|
str bone;
|
|
str modelname;
|
|
int tagnum;
|
|
float remove_time, fade_time, fade_delay;
|
|
Vector offset;
|
|
|
|
obj = new Animate;
|
|
|
|
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->detach_at_death = ev->GetInteger(5);
|
|
}
|
|
|
|
if (ev->NumArgs() > 5) {
|
|
remove_time = ev->GetFloat(6);
|
|
|
|
if (remove_time != -1) {
|
|
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 != -1) {
|
|
obj->setAlpha(0);
|
|
|
|
fade_event = new Event(EV_FadeIn);
|
|
fade_event->AddFloat(fade_time);
|
|
obj->PostEvent(fade_event, 0);
|
|
}
|
|
}
|
|
|
|
if (ev->NumArgs() > 7) {
|
|
Event *fade_event;
|
|
|
|
fade_delay = ev->GetFloat(8);
|
|
|
|
if (fade_delay != -1) {
|
|
if (ev->NumArgs() > 8) {
|
|
fade_time = ev->GetFloat(9);
|
|
} else {
|
|
fade_time = 0;
|
|
}
|
|
|
|
fade_event = new Event(EV_Fade);
|
|
|
|
if (fade_time) {
|
|
fade_event->AddFloat(fade_time);
|
|
}
|
|
|
|
obj->PostEvent(fade_event, fade_delay);
|
|
}
|
|
}
|
|
|
|
if (ev->NumArgs() > 9) {
|
|
offset = ev->GetVector(10);
|
|
}
|
|
|
|
obj->setModel(modelname);
|
|
|
|
tagnum = gi.Tag_NumForName(edict->tiki, bone);
|
|
if (tagnum >= 0) {
|
|
if (obj->attach(this->entnum, tagnum, true, offset)) {
|
|
obj->NewAnim("idle");
|
|
} else {
|
|
warning("AttachModelEvent", "Could not attach model %s to tag \"%s\" on entnum #%d (targetname = %s)", modelname.c_str(), bone.c_str(), entnum, targetname.c_str());
|
|
if (g_iInThinks) {
|
|
PostEvent(EV_Remove, 0);
|
|
} else {
|
|
delete obj;
|
|
}
|
|
return;
|
|
}
|
|
} else {
|
|
warning("AttachModelEvent", "Tag %s not found", bone.c_str());
|
|
obj->NewAnim("idle");
|
|
}
|
|
}
|
|
|
|
void Entity::RemoveAttachedModelEvent(Event *ev)
|
|
{
|
|
str tag_name;
|
|
int tag_num;
|
|
int num;
|
|
int i;
|
|
Entity *ent;
|
|
float fade_rate = 0;
|
|
Event *fade_event;
|
|
str model_name;
|
|
|
|
tag_name = ev->GetString(1);
|
|
tag_num = gi.Tag_NumForName(edict->tiki, 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 = numchildren;
|
|
|
|
for (i = 0; (i < MAX_MODEL_CHILDREN) && num; i++) {
|
|
if (children[i] == ENTITYNUM_NONE) {
|
|
continue;
|
|
}
|
|
|
|
ent = (Entity *)G_GetEntity(children[i]);
|
|
|
|
if (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);
|
|
ent->PostEvent(fade_event, 0);
|
|
}
|
|
|
|
ent->PostEvent(EV_Remove, fade_rate);
|
|
}
|
|
}
|
|
|
|
num--;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Entity::AttachedModelAnimEvent(Event *ev)
|
|
{
|
|
int tag_num;
|
|
int num;
|
|
int i;
|
|
Entity *ent;
|
|
str model_name;
|
|
str anim_name;
|
|
float crossblend_time;
|
|
|
|
tag_num = gi.Tag_NumForName(edict->tiki, ev->GetString(1).c_str());
|
|
anim_name = ev->GetString(2);
|
|
if (ev->NumArgs() >= 3) {
|
|
crossblend_time = ev->GetFloat(3);
|
|
}
|
|
|
|
if (ev->NumArgs() >= 4) {
|
|
model_name = ev->GetString(4);
|
|
}
|
|
|
|
if (tag_num >= 0) {
|
|
num = numchildren;
|
|
|
|
// find the children that is glued to the specified tagnum
|
|
for (i = 0; i < MAX_MODEL_CHILDREN && num; i++) {
|
|
if (children[i] == ENTITYNUM_NONE) {
|
|
continue;
|
|
}
|
|
|
|
ent = G_GetEntity(children[i]);
|
|
if (ent->edict->s.tag_num != tag_num) {
|
|
// not matching the requested tag num
|
|
continue;
|
|
}
|
|
|
|
if (model_name.length() && str::icmp(model_name.c_str(), ent->model.c_str())) {
|
|
// different model
|
|
continue;
|
|
}
|
|
|
|
Event *newev = new Event(EV_SetAnim);
|
|
newev->AddString(anim_name);
|
|
ent->PostEvent(newev, 0.f);
|
|
}
|
|
}
|
|
}
|
|
|
|
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->r.svFlags |= SVF_USEBBOX;
|
|
}
|
|
|
|
void Entity::HurtEvent(Event *ev)
|
|
{
|
|
Vector normal;
|
|
float dmg;
|
|
int means_of_death;
|
|
Vector direction;
|
|
|
|
if (ev->NumArgs() < 1) {
|
|
dmg = 50;
|
|
} else {
|
|
dmg = ev->GetFloat(1);
|
|
}
|
|
|
|
if (ev->NumArgs() > 1) {
|
|
means_of_death = MOD_string_to_int(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, dmg, 0, means_of_death);
|
|
}
|
|
|
|
void Entity::HealEvent(Event *ev)
|
|
{
|
|
if (IsDead()) {
|
|
ScriptError("cannot heal dead entities");
|
|
}
|
|
|
|
health += ev->GetFloat(1) * max_health;
|
|
if (health > max_health) {
|
|
health = max_health;
|
|
}
|
|
}
|
|
|
|
void Entity::Censor(Event *ev)
|
|
|
|
{
|
|
Vector delta;
|
|
float oldsize;
|
|
float newsize;
|
|
|
|
oldsize = size.length();
|
|
setSolidType(SOLID_NOT);
|
|
setModel("censored.tik");
|
|
gi.TIKI_CalculateBounds(edict->tiki, 1, 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::TossEvent(Event *ev)
|
|
{
|
|
movetype = MOVETYPE_TOSS;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
void Entity::Shader(Event *ev)
|
|
|
|
{
|
|
str token;
|
|
|
|
if (gi.modeltiki(model)) {
|
|
gi.Printf("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 (!Q_stricmp(token, "translation")) {
|
|
float x, y;
|
|
|
|
x = ev->GetFloat(2);
|
|
y = ev->GetFloat(3);
|
|
TRANSLATION_TO_PKT(x, edict->s.tag_num);
|
|
TRANSLATION_TO_PKT(y, edict->s.skinNum);
|
|
} else if (!Q_stricmp(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 (!Q_stricmp(token, "rotation")) {
|
|
float rot;
|
|
|
|
rot = ev->GetFloat(2);
|
|
ROTATE_TO_PKT(rot, edict->s.tag_num);
|
|
} else if (!Q_stricmp(token, "wavebase")) {
|
|
float base;
|
|
|
|
base = ev->GetFloat(2);
|
|
BASE_TO_PKT(base, edict->s.surfaces[0]);
|
|
} else if (!Q_stricmp(token, "waveamp")) {
|
|
float amp;
|
|
|
|
amp = ev->GetFloat(2);
|
|
AMPLITUDE_TO_PKT(amp, edict->s.surfaces[1]);
|
|
} else if (!Q_stricmp(token, "wavephase")) {
|
|
float phase;
|
|
|
|
phase = ev->GetFloat(2);
|
|
PHASE_TO_PKT(phase, edict->s.surfaces[2]);
|
|
} else if (!Q_stricmp(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 = MAP_SIZE;
|
|
}
|
|
if (!droptofloor(range)) {}
|
|
}
|
|
|
|
qboolean Entity::isBoundTo(Entity *master)
|
|
|
|
{
|
|
Entity *ent;
|
|
|
|
for (ent = bindmaster; ent != NULL; ent = ent->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;
|
|
}
|
|
|
|
// unbind myself from my master
|
|
unbind();
|
|
|
|
bindmaster = master;
|
|
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(bindmaster->orientation, mat);
|
|
R_ConcatRotations(mat, orientation, local);
|
|
MatrixToEulerAngles(local, ang);
|
|
setAngles(ang);
|
|
|
|
setLocalOrigin(getParentVector(localorigin - bindmaster->origin));
|
|
|
|
return;
|
|
}
|
|
|
|
void Entity::unbind(void)
|
|
{
|
|
Entity *prev;
|
|
Entity *next;
|
|
Entity *last;
|
|
Entity *ent;
|
|
|
|
if (!bindmaster) {
|
|
return;
|
|
}
|
|
|
|
//bindmaster = NULL;
|
|
|
|
// Check this GAMEFIX - should it be origin?
|
|
localorigin = Vector(edict->s.origin);
|
|
localangles = Vector(edict->s.angles);
|
|
|
|
if (!teammaster) {
|
|
bindmaster = NULL;
|
|
//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 = teammaster;
|
|
|
|
for (ent = teammaster->teamchain; ent && (ent != this); ent = ent->teamchain) {
|
|
prev = ent;
|
|
}
|
|
|
|
// If ent is not pointing to me, then something is very wrong.
|
|
assert(ent);
|
|
if (!ent) {
|
|
gi.Printf("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 = teamchain; next != NULL; next = next->teamchain) {
|
|
if (!next->isBoundTo(this)) {
|
|
break;
|
|
}
|
|
|
|
// Tell them I'm now the teammaster
|
|
next->teammaster = this;
|
|
last = next;
|
|
}
|
|
|
|
// disconnect the last member of our team from the old team
|
|
last->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 (teammaster != this) {
|
|
prev->teamchain = next;
|
|
if (!next && (teammaster == prev)) {
|
|
prev->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->teamchain != NULL; ent = ent->teamchain) {
|
|
ent->teammaster = next;
|
|
}
|
|
next->teammaster = next;
|
|
next->flags &= ~FL_TEAMSLAVE;
|
|
}
|
|
|
|
// If we don't have anyone on our team, then clear the team variables.
|
|
if (teamchain) {
|
|
// make myself my own team
|
|
teammaster = this;
|
|
} else {
|
|
// no longer a team
|
|
teammaster = NULL;
|
|
}
|
|
|
|
flags &= ~FL_TEAMSLAVE;
|
|
bindmaster = NULL;
|
|
}
|
|
|
|
void Entity::EventUnglue(Event *ev)
|
|
{
|
|
unglue();
|
|
}
|
|
|
|
void Entity::GlueEvent(Event *ev)
|
|
{
|
|
Entity *ent;
|
|
qboolean glueAngles;
|
|
|
|
if (ev->NumArgs() > 1) {
|
|
glueAngles = ev->GetInteger(2);
|
|
} else {
|
|
glueAngles = qtrue;
|
|
}
|
|
|
|
ent = ev->GetEntity(1);
|
|
if (ent) {
|
|
glue(ent, glueAngles, qfalse);
|
|
}
|
|
}
|
|
|
|
void Entity::DuckableGlueEvent(Event *ev)
|
|
{
|
|
Entity *ent;
|
|
qboolean glueAngles;
|
|
|
|
if (ev->NumArgs() > 1) {
|
|
glueAngles = ev->GetInteger(2);
|
|
} else {
|
|
glueAngles = qtrue;
|
|
}
|
|
|
|
ent = ev->GetEntity(1);
|
|
if (ent) {
|
|
glue(ent, glueAngles, qtrue);
|
|
}
|
|
}
|
|
|
|
void Entity::MakeClayPidgeon(Event *ev)
|
|
{
|
|
edict->r.contents = CONTENTS_CLAYPIDGEON;
|
|
setSolidType(SOLID_BBOX);
|
|
}
|
|
|
|
void Entity::EventUnbind(Event *ev)
|
|
|
|
{
|
|
unbind();
|
|
}
|
|
|
|
void Entity::BindEvent(Event *ev)
|
|
{
|
|
Entity *ent;
|
|
|
|
ent = ev->GetEntity(1);
|
|
if (ent) {
|
|
bind(ent, false);
|
|
}
|
|
}
|
|
|
|
Vector Entity::getParentVector(Vector vec)
|
|
{
|
|
Vector pos;
|
|
|
|
if (!bindmaster) {
|
|
return vec;
|
|
}
|
|
|
|
pos[0] = vec * bindmaster->orientation[0];
|
|
pos[1] = vec * bindmaster->orientation[1];
|
|
pos[2] = vec * bindmaster->orientation[2];
|
|
|
|
return pos;
|
|
}
|
|
|
|
//
|
|
// Team methods
|
|
//
|
|
|
|
void Entity::joinTeam(Entity *teammember)
|
|
{
|
|
Entity *ent;
|
|
Entity *master;
|
|
Entity *prev;
|
|
Entity *next;
|
|
|
|
if (teammaster && (teammaster != this)) {
|
|
quitTeam();
|
|
}
|
|
|
|
assert(teammember);
|
|
if (!teammember) {
|
|
warning("joinTeam", "Null entity");
|
|
return;
|
|
}
|
|
|
|
master = teammember->teammaster;
|
|
if (!master) {
|
|
master = teammember;
|
|
teammember->teammaster = teammember;
|
|
teammember->teamchain = this;
|
|
|
|
// make anyone who's bound to me part of the new team
|
|
for (ent = teamchain; ent != NULL; ent = ent->teamchain) {
|
|
ent->teammaster = master;
|
|
}
|
|
} else {
|
|
// skip past the chain members bound to the entity we're teaming up with
|
|
prev = teammember;
|
|
next = teammember->teamchain;
|
|
if (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->teamchain;
|
|
}
|
|
} else {
|
|
// if we're not bound to someone, then put us at the end of the team
|
|
while (next) {
|
|
prev = next;
|
|
next = next->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->teamchain != NULL; ent = ent->teamchain) {
|
|
ent->teamchain->teammaster = master;
|
|
}
|
|
|
|
prev->teamchain = this;
|
|
ent->teamchain = next;
|
|
}
|
|
|
|
teammaster = master;
|
|
flags |= FL_TEAMSLAVE;
|
|
}
|
|
|
|
void Entity::quitTeam(void)
|
|
{
|
|
Entity *ent;
|
|
|
|
if (!teammaster) {
|
|
return;
|
|
}
|
|
|
|
if (teammaster == this) {
|
|
if (!teamchain->teamchain) {
|
|
teamchain->teammaster = NULL;
|
|
} else {
|
|
// make next teammate the teammaster
|
|
for (ent = teamchain; ent; ent = ent->teamchain) {
|
|
ent->teammaster = teamchain;
|
|
}
|
|
}
|
|
|
|
teamchain->flags &= ~FL_TEAMSLAVE;
|
|
} else {
|
|
assert(flags & FL_TEAMSLAVE);
|
|
assert(teammaster->teamchain);
|
|
|
|
ent = teammaster;
|
|
while (ent->teamchain != this) {
|
|
// this should never happen
|
|
assert(ent->teamchain);
|
|
|
|
ent = ent->teamchain;
|
|
}
|
|
|
|
ent->teamchain = teamchain;
|
|
|
|
if (!teammaster->teamchain) {
|
|
teammaster->teammaster = NULL;
|
|
}
|
|
}
|
|
|
|
teammaster = NULL;
|
|
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(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::NoLerpThisFrame(void)
|
|
{
|
|
edict->s.eFlags ^= EF_TELEPORT_BIT;
|
|
}
|
|
|
|
void Entity::Postthink(void) {}
|
|
|
|
void Entity::TouchTriggersEvent(Event *ev)
|
|
{
|
|
flags |= FL_TOUCH_TRIGGERS;
|
|
}
|
|
|
|
void Entity::DeathSinkStart(Event *ev)
|
|
{
|
|
if (g_gametype->integer != GT_SINGLE_PLAYER) {
|
|
PostEvent(EV_Remove, 5.0f);
|
|
} else {
|
|
// Start the sinking
|
|
ProcessEvent(EV_DeathSink);
|
|
}
|
|
}
|
|
|
|
void Entity::DeathSink(Event *ev)
|
|
{
|
|
// Sink just a little
|
|
|
|
if (g_gametype->integer != GT_SINGLE_PLAYER) {
|
|
origin[2] -= 0.2f;
|
|
setOrigin(origin);
|
|
|
|
// Make sure the sink happens again next frame
|
|
PostEvent(EV_DeathSink, FRAMETIME);
|
|
} else {
|
|
Entity *player = G_GetEntity(0);
|
|
|
|
// Remove the entity when the player isn't supposed to see it
|
|
if (player && gi.AreasConnected(this->edict->r.areanum, player->edict->r.areanum)) {
|
|
PostEvent(EV_DeathSink, 1.0f);
|
|
} else {
|
|
PostEvent(EV_Remove, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Entity::VelocityModified(void) {}
|
|
|
|
void Entity::DetachAllChildren(Event *ev)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < MAX_MODEL_CHILDREN; i++) {
|
|
Entity *ent;
|
|
if (children[i] == ENTITYNUM_NONE) {
|
|
continue;
|
|
}
|
|
|
|
ent = (Entity *)G_GetEntity(children[i]);
|
|
if (ent) {
|
|
ent->PostEvent(EV_Remove, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool monkeycheck = false;
|
|
cvar_t *thereisnomonkey;
|
|
|
|
qboolean Entity::CheckEventFlags(Event *event)
|
|
{
|
|
int flags = GetFlags(event);
|
|
|
|
// Special handling of console events
|
|
if (!(flags & (EV_CONSOLE | EV_CHEAT))) {
|
|
if (isSubclassOf(Entity)) {
|
|
Entity *ent;
|
|
|
|
ent = (Entity *)this;
|
|
gi.SendServerCommand(
|
|
ent->edict - g_entities, "print \"Command '%s' not available from console\n\"", event->getName()
|
|
);
|
|
}
|
|
|
|
// don't process
|
|
return false;
|
|
}
|
|
|
|
// don't allow console cheats unless the server says it's ok.
|
|
|
|
if ((flags & EV_CHEAT)) {
|
|
if (!monkeycheck) {
|
|
thereisnomonkey = gi.Cvar_Get("thereisnomonkey", "0", CVAR_TEMP);
|
|
monkeycheck = true;
|
|
}
|
|
|
|
if (!thereisnomonkey->integer) {
|
|
gi.cvar_set("cheats", "0");
|
|
}
|
|
|
|
if (!sv_cheats->integer) {
|
|
if (isSubclassOf(Entity)) {
|
|
Entity *ent;
|
|
const char *szConv =
|
|
gi.LV_ConvertString("You must run the server with '+set cheats 1' to enable this command.");
|
|
|
|
ent = (Entity *)this;
|
|
gi.SendServerCommand(ent->edict - g_entities, "print \"%s\n\"", szConv);
|
|
}
|
|
|
|
// don't process
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// ok to process
|
|
return true;
|
|
}
|
|
|
|
void Entity::SetMovementStealth(float fStealthScale)
|
|
{
|
|
if (fStealthScale < 0.0f) {
|
|
stealthMovementScale = 0.0f;
|
|
} else {
|
|
stealthMovementScale = fStealthScale;
|
|
}
|
|
}
|
|
|
|
void Entity::EventMovementStealth(Event *ev)
|
|
{
|
|
SetMovementStealth(ev->GetFloat(1));
|
|
}
|
|
|
|
void Entity::PusherEvent(Event *ev)
|
|
{
|
|
Entity *inflictor;
|
|
Entity *attacker;
|
|
Vector dir;
|
|
Vector momentum;
|
|
float m;
|
|
float force;
|
|
|
|
if (movetype <= MOVETYPE_NOCLIP || movetype == MOVETYPE_FLYMISSILE || movetype == MOVETYPE_STOP
|
|
|| movetype == MOVETYPE_PUSH) {
|
|
return;
|
|
}
|
|
|
|
inflictor = ev->GetEntity(1);
|
|
attacker = ev->GetEntity(2);
|
|
dir = ev->GetVector(3);
|
|
force = ev->GetFloat(4);
|
|
|
|
m = mass;
|
|
if (m < 20) {
|
|
m = 20.0f;
|
|
}
|
|
|
|
m = 1000.0f / m;
|
|
momentum = dir * force * m;
|
|
|
|
velocity += momentum;
|
|
}
|
|
|
|
void Entity::AlwaysDraw(Event *ev)
|
|
{
|
|
edict->s.renderfx |= RF_ALWAYSDRAW;
|
|
}
|
|
|
|
void Entity::NormalDraw(Event *ev)
|
|
{
|
|
edict->s.renderfx &= RF_DONTDRAW | RF_ALWAYSDRAW;
|
|
}
|
|
|
|
void Entity::NeverDraw(Event *ev)
|
|
{
|
|
edict->s.renderfx |= RF_DONTDRAW;
|
|
}
|
|
|
|
//*************************************************************************
|
|
//
|
|
// BIND code
|
|
//
|
|
//*************************************************************************
|
|
|
|
void Entity::glue(Entity *master, qboolean use_my_angles, qboolean can_duck)
|
|
{
|
|
int iNumGlues;
|
|
|
|
if (m_pGlueMaster) {
|
|
unglue();
|
|
}
|
|
|
|
if (!master) {
|
|
warning("glue", "Cannot glue to master because of: Master is NULL\n");
|
|
return;
|
|
}
|
|
|
|
if (master == this) {
|
|
warning("glue", "Cannot glue to master because of: Binding an entity to itself\n");
|
|
return;
|
|
}
|
|
|
|
iNumGlues = master->m_iNumGlues;
|
|
if (iNumGlues + 1 > MAX_GLUE_CHILDREN) {
|
|
warning("glue", "Cannot glue to master because of: MAX_GLUE_CHILDREN reached\n");
|
|
return;
|
|
}
|
|
|
|
for (int i = 0; i < iNumGlues; i++) {
|
|
if (master->m_pGlues[i] == this) {
|
|
warning("glue", "Cannot glue to master because of: entity is already glued\n");
|
|
return;
|
|
}
|
|
}
|
|
|
|
master->m_pGlues[iNumGlues] = this;
|
|
|
|
if (use_my_angles) {
|
|
m_bGlueAngles = true;
|
|
master->m_pGluesFlags[iNumGlues] = GL_USEANGLES;
|
|
} else {
|
|
m_bGlueAngles = false;
|
|
master->m_pGluesFlags[iNumGlues] = 0;
|
|
}
|
|
|
|
m_pGlueMaster = master;
|
|
m_bGlueDuckable = can_duck == qtrue;
|
|
master->m_iNumGlues++;
|
|
}
|
|
|
|
void Entity::unglue(void)
|
|
{
|
|
int iNumGlues;
|
|
int i;
|
|
Entity *master = m_pGlueMaster;
|
|
|
|
if (!master) {
|
|
return;
|
|
}
|
|
|
|
iNumGlues = master->m_iNumGlues;
|
|
|
|
for (i = 0; i < iNumGlues; i++) {
|
|
if (master->m_pGlues[i] == this) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i != iNumGlues) {
|
|
for (; i < iNumGlues - 1; i++) {
|
|
master->m_pGlues[i] = master->m_pGlues[i + 1];
|
|
master->m_pGluesFlags[i] = master->m_pGluesFlags[i + 1];
|
|
}
|
|
|
|
master->m_iNumGlues--;
|
|
|
|
m_pGlueMaster = NULL;
|
|
m_bGlueAngles = false;
|
|
}
|
|
}
|
|
|
|
void Entity::GetTagPositionAndOrientation(int tagnum, orientation_t *new_or)
|
|
{
|
|
int i;
|
|
orientation_t tag_or;
|
|
float axis[3][3];
|
|
|
|
GetRawTag(tagnum, &tag_or);
|
|
|
|
AnglesToAxis(angles, axis);
|
|
|
|
VectorCopy(origin, new_or->origin);
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
VectorMA(new_or->origin, tag_or.origin[i], axis[i], new_or->origin);
|
|
}
|
|
|
|
MatrixMultiply(tag_or.axis, axis, new_or->axis);
|
|
}
|
|
|
|
void Entity::GetTagPosition(Event *ev)
|
|
{
|
|
Vector pos;
|
|
str tagname;
|
|
int tagnum;
|
|
int i;
|
|
orientation_t tag_or;
|
|
float axis[3][3];
|
|
|
|
if (!edict->tiki) {
|
|
ScriptError("Entity '%s' at %.2f %.2f %.2f has no model.", targetname.c_str(), origin[0], origin[1], origin[2]);
|
|
}
|
|
|
|
tagname = ev->GetString(1);
|
|
tagnum = gi.Tag_NumForName(edict->tiki, tagname);
|
|
if (tagnum < 0) {
|
|
ScriptError("Could not find tag '%s' in '%s'", tagname.c_str(), edict->tiki->name);
|
|
}
|
|
|
|
tag_or = G_TIKI_Orientation(edict, tagnum & TAG_MASK);
|
|
AnglesToAxis(angles, axis);
|
|
|
|
pos = origin;
|
|
for (i = 0; i < 3; i++) {
|
|
VectorMA(pos, tag_or.origin[i], axis[i], pos);
|
|
}
|
|
|
|
ev->AddVector(pos);
|
|
}
|
|
|
|
void Entity::GetTagAngles(Event *ev)
|
|
{
|
|
orientation_t orient;
|
|
Vector ang;
|
|
str tagname;
|
|
int tagnum;
|
|
|
|
if (!edict->tiki) {
|
|
ScriptError("Entity '%s' at %.2f %.2f %.2f has no model.", targetname.c_str(), origin[0], origin[1], origin[2]);
|
|
}
|
|
|
|
tagname = ev->GetString(1);
|
|
tagnum = gi.Tag_NumForName(edict->tiki, tagname);
|
|
if (tagnum < 0) {
|
|
ScriptError("Could not find tag '%s' in '%s'", tagname.c_str(), edict->tiki->name);
|
|
}
|
|
|
|
GetTagPositionAndOrientation(tagnum, &orient);
|
|
MatrixToEulerAngles(orient.axis, ang);
|
|
|
|
ev->AddVector(ang);
|
|
}
|
|
|
|
void Entity::IsTouching(Event *ev)
|
|
{
|
|
Entity *ent = ev->GetEntity(1);
|
|
|
|
if (!ent) {
|
|
ScriptError("IsTouching used with a NULL entity.");
|
|
}
|
|
|
|
ev->AddInteger(IsTouching(ent));
|
|
}
|
|
|
|
void Entity::IsInside(Event *ev)
|
|
{
|
|
Entity *ent = ev->GetEntity(1);
|
|
|
|
if (!ent) {
|
|
ScriptError("IsInside used with a NULL entity.");
|
|
}
|
|
|
|
ev->AddInteger(IsInside(ent));
|
|
}
|
|
|
|
void Entity::CanSeeInternal(Event *ev, bool bNoEnts)
|
|
{
|
|
float fov = 0.0f;
|
|
float distance = 0.0f;
|
|
|
|
if (ev->NumArgs() <= 0 || ev->NumArgs() > 3) {
|
|
ScriptError("cansee should have 1, 2 or 3 arguments");
|
|
}
|
|
|
|
if (ev->NumArgs() > 2) {
|
|
distance = ev->GetFloat(3);
|
|
if (distance < 0) {
|
|
ScriptError("distance must be >= 0");
|
|
}
|
|
}
|
|
|
|
if (ev->NumArgs() > 1) {
|
|
fov = ev->GetFloat(2);
|
|
if (fov < 0.0f || fov > 360.0f) {
|
|
ScriptError("fov must be in range 0 <= fov <= 360");
|
|
}
|
|
}
|
|
|
|
if (ev->IsVectorAt(1)) {
|
|
Vector org = ev->GetVector(1);
|
|
|
|
ev->AddInteger(CanSee(org, fov, distance, bNoEnts));
|
|
} else {
|
|
Entity *ent = ev->GetEntity(1);
|
|
if (!ent) {
|
|
ScriptError("NULL entity in parameter 1");
|
|
}
|
|
|
|
ev->AddInteger(CanSee(ent, fov, distance, bNoEnts));
|
|
}
|
|
}
|
|
|
|
void Entity::CanSee(Event *ev)
|
|
{
|
|
CanSeeInternal(ev, false);
|
|
}
|
|
|
|
void Entity::CanSeeNoEnts(Event *ev)
|
|
{
|
|
CanSeeInternal(ev, true);
|
|
}
|
|
|
|
void Entity::EventInPVS(Event *ev)
|
|
{
|
|
ev->AddInteger(gi.AreasConnected(edict->r.areanum, ev->GetEntity(1)->edict->r.areanum) != 0);
|
|
}
|
|
|
|
void Entity::SetShaderData(Event *ev)
|
|
{
|
|
edict->s.shader_data[0] = ev->GetFloat(1);
|
|
edict->s.shader_data[1] = ev->GetFloat(2);
|
|
}
|
|
|
|
void Entity::MPrintf(const char *fmt, ...)
|
|
{
|
|
va_list argptr;
|
|
char msg[MAXPRINTMSG];
|
|
|
|
if (g_monitorNum->integer == entnum || g_monitor->string[0] && g_monitor->string == targetname) {
|
|
va_start(argptr, fmt);
|
|
Q_vsnprintf(msg, sizeof(msg), fmt, argptr);
|
|
va_end(argptr);
|
|
Com_Printf("%s", msg);
|
|
}
|
|
}
|
|
|
|
void Entity::GetVelocity(Event *ev)
|
|
{
|
|
ev->AddVector(velocity);
|
|
}
|
|
|
|
void Entity::SetVelocity(Event *ev)
|
|
{
|
|
velocity = ev->GetVector(1);
|
|
}
|
|
|
|
void Entity::GetAVelocity(Event *ev)
|
|
{
|
|
ev->AddVector(avelocity);
|
|
}
|
|
|
|
void Entity::DoForceActivate(void)
|
|
{
|
|
edict->r.lastNetTime = level.inttime;
|
|
}
|
|
|
|
void Entity::ForceActivate(Event *ev)
|
|
{
|
|
DoForceActivate();
|
|
}
|
|
|
|
void Entity::EventTrace(Event *ev)
|
|
{
|
|
Vector start, end, mins, maxs;
|
|
int mask = MASK_SOLID;
|
|
Entity *ent;
|
|
trace_t trace;
|
|
|
|
mins = vec_zero;
|
|
maxs = vec_zero;
|
|
ent = NULL;
|
|
|
|
start = ev->GetVector(1);
|
|
end = ev->GetVector(2);
|
|
|
|
if (ev->NumArgs() > 2) {
|
|
if (ev->GetInteger(3)) {
|
|
mask = 1;
|
|
}
|
|
}
|
|
|
|
if (ev->NumArgs() > 3) {
|
|
mins = ev->GetVector(4);
|
|
}
|
|
|
|
if (ev->NumArgs() > 4) {
|
|
maxs = ev->GetVector(5);
|
|
}
|
|
|
|
if (ev->NumArgs() > 5) {
|
|
ent = G_GetEntity(ev->GetInteger(6));
|
|
}
|
|
|
|
// call trace
|
|
trace = G_Trace(start, mins, maxs, end, ent, mask, qfalse, "Entity::EventTrace");
|
|
|
|
ev->AddVector(trace.endpos);
|
|
}
|
|
|
|
void Entity::EventSightTrace(Event *ev)
|
|
{
|
|
Vector start, end, mins, maxs;
|
|
int mask = MASK_SOLID;
|
|
Entity *ent = nullptr;
|
|
qboolean hit = qfalse;
|
|
|
|
mins = vec_zero;
|
|
maxs = vec_zero;
|
|
|
|
start = ev->GetVector(1);
|
|
end = ev->GetVector(2);
|
|
|
|
if (ev->NumArgs() > 2) {
|
|
if (ev->GetInteger(3)) {
|
|
mask = 1;
|
|
}
|
|
}
|
|
|
|
if (ev->NumArgs() > 3) {
|
|
mins = ev->GetVector(4);
|
|
}
|
|
|
|
if (ev->NumArgs() > 4) {
|
|
maxs = ev->GetVector(5);
|
|
}
|
|
|
|
if (ev->NumArgs() > 5) {
|
|
ent = G_GetEntity(ev->GetInteger(6));
|
|
}
|
|
|
|
hit = G_SightTrace(start, mins, maxs, end, ent, NULL, mask, qfalse, "Entity::EventSightTrace");
|
|
|
|
ev->AddInteger(hit);
|
|
}
|
|
|
|
void Entity::ClientThink(void) {}
|
|
|
|
void Entity::EndFrame(void) {}
|
|
|
|
void Entity::CalcBlend(void) {}
|
|
|
|
void Entity::ShowInfo(float fDot, float fDist)
|
|
{
|
|
int i;
|
|
char szText[512];
|
|
if (fDot > 0.94999999 && fDist < 1024.0 && fDist > 64.0) {
|
|
i = Com_sprintf(szText, sizeof(szText), "%i:%i", entnum, radnum);
|
|
if (targetname.length()) {
|
|
i = Com_sprintf(szText + i, sizeof(szText) - i, ":%s", targetname.c_str());
|
|
}
|
|
|
|
if (health != 0) {
|
|
Com_sprintf(szText + i, sizeof(szText) - i, ":%.1f", health);
|
|
}
|
|
|
|
G_DebugString(Vector(origin.x + 0, origin.y + 0, origin.z + maxs.z + 65), 1.0, 1.0, 1.0, 1.0, szText);
|
|
}
|
|
}
|
|
|
|
bool Entity::AutoArchiveModel(void)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
void Entity::ConnectPaths(void)
|
|
{
|
|
PathNode *node;
|
|
int to;
|
|
int j;
|
|
|
|
for (int i = 0; i < m_iNumBlockedPaths; i++) {
|
|
to = m_BlockedPaths[i].to;
|
|
node = PathManager.pathnodes[m_BlockedPaths[i].from];
|
|
|
|
j = node->virtualNumChildren;
|
|
pathway_t *pathway = &node->Child[j];
|
|
|
|
do {
|
|
j--;
|
|
pathway--;
|
|
} while (pathway->node != to);
|
|
|
|
pathway->numBlockers--;
|
|
|
|
if (!pathway->numBlockers) {
|
|
node->ConnectChild(j);
|
|
}
|
|
}
|
|
|
|
m_iNumBlockedPaths = 0;
|
|
if (m_BlockedPaths) {
|
|
delete[] m_BlockedPaths;
|
|
m_BlockedPaths = NULL;
|
|
}
|
|
}
|
|
|
|
void Entity::DisconnectPaths(void)
|
|
{
|
|
int i;
|
|
int j;
|
|
PathNode *node;
|
|
pathway_t *pathway;
|
|
pathway_ref paths[1024];
|
|
vec3_t mins;
|
|
vec3_t maxs;
|
|
gentity_t *collision_ent;
|
|
|
|
if (m_BlockedPaths) {
|
|
ConnectPaths();
|
|
}
|
|
|
|
if (IsSubclassOfVehicle()) {
|
|
VehicleCollisionEntity *ent = ((Vehicle *)this)->GetCollisionEntity();
|
|
|
|
if (!ent) {
|
|
return;
|
|
}
|
|
|
|
collision_ent = ent->edict;
|
|
} else {
|
|
collision_ent = edict;
|
|
}
|
|
|
|
VectorSet(mins, -15.0f, -15.0f, 0);
|
|
VectorSet(maxs, 15.0f, 15.0f, 94.0f);
|
|
|
|
for (i = 0; i < PathManager.nodecount; i++) {
|
|
node = PathManager.pathnodes[i];
|
|
|
|
for (j = node->virtualNumChildren - 1; j >= 0; j--) {
|
|
pathway = &node->Child[j];
|
|
|
|
if (!gi.SightTraceEntity(collision_ent, pathway->pos1, mins, maxs, pathway->pos2, MASK_PATHSOLID, qtrue)) {
|
|
paths[m_iNumBlockedPaths].from = node->nodenum;
|
|
paths[m_iNumBlockedPaths].to = pathway->node;
|
|
m_iNumBlockedPaths++;
|
|
|
|
pathway->numBlockers++;
|
|
|
|
if (pathway->numBlockers == 1) {
|
|
node->DisconnectChild(j);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (m_iNumBlockedPaths) {
|
|
m_BlockedPaths = new pathway_ref[m_iNumBlockedPaths];
|
|
|
|
for (i = 0; i < m_iNumBlockedPaths; i++) {
|
|
m_BlockedPaths[i] = paths[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
void Entity::EventConnectPaths(Event *ev)
|
|
{
|
|
ConnectPaths();
|
|
}
|
|
|
|
void Entity::EventDisconnectPaths(Event *ev)
|
|
{
|
|
DisconnectPaths();
|
|
}
|
|
|
|
void Entity::Delete(void)
|
|
{
|
|
// Delete the entity the next frame
|
|
|
|
if (g_iInThinks) {
|
|
if (ProcessingEvents) {
|
|
// Fixed in OPM
|
|
// Add a slight delay because otherwise this would cause an infinite loop
|
|
// when processing pending events
|
|
PostEvent(EV_Remove, 0.001);
|
|
return;
|
|
}
|
|
|
|
PostEvent(EV_Remove, 0);
|
|
} else {
|
|
delete this;
|
|
}
|
|
}
|
|
|
|
void Entity::Remove(Event *ev)
|
|
{
|
|
if (ev->NumArgs()) {
|
|
ScriptError("Arguments not allowed.");
|
|
}
|
|
|
|
Delete();
|
|
}
|
|
|
|
void Entity::EventSoundDone(Event *ev)
|
|
{
|
|
int channelNum = ev->GetInteger(1);
|
|
str sfxName = ev->GetString(2);
|
|
|
|
if (gi.S_IsSoundPlaying(channelNum, sfxName)) {
|
|
// Repost the event
|
|
Event *event = new Event(EV_SoundDone);
|
|
event->AddInteger(channelNum);
|
|
event->AddString(sfxName);
|
|
PostEvent(event, level.frametime);
|
|
} else {
|
|
Unregister(STRING_SOUNDDONE);
|
|
}
|
|
}
|
|
|
|
void Entity::VolumeDamage(float damage)
|
|
{
|
|
int i;
|
|
int numtouch;
|
|
Entity *ent;
|
|
int touch[MAX_GENTITIES];
|
|
|
|
numtouch = gi.AreaEntities(absmin, absmax, touch, MAX_GENTITIES);
|
|
|
|
for (i = 0; i < numtouch; i++) {
|
|
ent = G_GetEntity(touch[i]);
|
|
ent->Damage(world, world, damage, origin, vec_zero, vec_zero, 0, DAMAGE_RADIUS, MOD_EXPLOSION);
|
|
}
|
|
}
|
|
|
|
void Entity::EventVolumeDamage(Event *ev)
|
|
{
|
|
VolumeDamage(ev->GetFloat(1));
|
|
}
|
|
|
|
void Entity::Archive(Archiver& arc)
|
|
{
|
|
int tempInt;
|
|
int i;
|
|
|
|
SimpleEntity::Archive(arc);
|
|
|
|
G_ArchiveEdict(arc, edict);
|
|
|
|
arc.ArchiveInteger(&entnum);
|
|
arc.ArchiveInteger(&radnum);
|
|
arc.ArchiveInteger(&spawnflags);
|
|
|
|
arc.ArchiveString(&model);
|
|
if (arc.Loading() && AutoArchiveModel()) {
|
|
if (model.length() && model[0] == '*') {
|
|
// set the brush model
|
|
edict->s.modelindex = atoi(model.c_str() + 1);
|
|
} else {
|
|
setModel();
|
|
}
|
|
}
|
|
|
|
arc.ArchiveVector(&mins);
|
|
arc.ArchiveVector(&maxs);
|
|
arc.ArchiveVector(&absmin);
|
|
arc.ArchiveVector(&absmax);
|
|
arc.ArchiveVector(&velocity);
|
|
arc.ArchiveVector(&accel);
|
|
arc.ArchiveVector(&avelocity);
|
|
arc.ArchiveVector(&aaccel);
|
|
arc.ArchiveVector(&size);
|
|
arc.ArchiveInteger(&movetype);
|
|
arc.ArchiveInteger(&mass);
|
|
arc.ArchiveFloat(&gravity);
|
|
arc.ArchiveRaw(orientation, sizeof(orientation));
|
|
|
|
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(&numchildren);
|
|
arc.ArchiveRaw(children, sizeof(children));
|
|
|
|
arc.ArchiveFloat(&lightRadius);
|
|
|
|
arc.ArchiveString(&moveteam);
|
|
arc.ArchiveObjectPointer((Class **)&teamchain);
|
|
arc.ArchiveObjectPointer((Class **)&teammaster);
|
|
|
|
arc.ArchiveObjectPointer((Class **)&bindmaster);
|
|
arc.ArchiveBoolean(&bind_use_my_angles);
|
|
arc.ArchiveVector(&localorigin);
|
|
arc.ArchiveVector(&localangles);
|
|
|
|
// add to target list to rebuild targetlists
|
|
arc.ArchiveString(&killtarget);
|
|
|
|
arc.ArchiveFloat(&health);
|
|
arc.ArchiveFloat(&max_health);
|
|
arc.ArchiveInteger(&deadflag);
|
|
arc.ArchiveInteger(&flags);
|
|
|
|
arc.ArchiveInteger(&watertype);
|
|
arc.ArchiveInteger(&waterlevel);
|
|
|
|
ArchiveEnum(takedamage, damage_t);
|
|
|
|
arc.ArchiveSafePointer(&enemy);
|
|
arc.ArchiveFloat(&pain_finished);
|
|
arc.ArchiveFloat(&damage_debounce_time);
|
|
arc.ArchiveInteger(&damage_type);
|
|
|
|
arc.ArchiveInteger(&m_iNumGlues);
|
|
|
|
for (i = 0; i < MAX_GLUE_CHILDREN; i++) {
|
|
arc.ArchiveSafePointer(&m_pGlues[i]);
|
|
arc.ArchiveInteger(&m_pGluesFlags[i]);
|
|
}
|
|
|
|
arc.ArchiveObjectPointer((Class **)&m_pGlueMaster);
|
|
arc.ArchiveBool(&m_bGlueAngles);
|
|
arc.ArchiveBool(&m_bGlueDuckable);
|
|
arc.ArchiveBoolean(&detach_at_death);
|
|
|
|
arc.ArchiveFloat(&stealthMovementScale);
|
|
arc.ArchiveInteger(&m_iNumBlockedPaths);
|
|
|
|
if (arc.Loading()) {
|
|
m_BlockedPaths = new pathway_ref[m_iNumBlockedPaths];
|
|
}
|
|
|
|
for (i = 0; i < m_iNumBlockedPaths; i++) {
|
|
arc.ArchiveShort(&m_BlockedPaths[i].from);
|
|
arc.ArchiveShort(&m_BlockedPaths[i].to);
|
|
}
|
|
|
|
immunities.Archive(arc);
|
|
}
|
|
|
|
void Entity::EventGetNormalHealth(Event *ev)
|
|
{
|
|
ev->AddFloat(health / max_health * 100);
|
|
}
|
|
|
|
void Entity::EventNormalDamage(Event *ev)
|
|
{
|
|
Vector position = vec_zero, direction, normal;
|
|
|
|
Entity *attacker = world;
|
|
float damage;
|
|
|
|
normal = direction = Vector(0, 1, 0);
|
|
|
|
int numArgs = ev->NumArgs();
|
|
if (numArgs == 1) {
|
|
damage = ev->GetFloat(1) / 100 * max_health;
|
|
} else if (numArgs == 2) {
|
|
damage = ev->GetFloat(1) / 100 * max_health;
|
|
direction = ev->GetVector(2);
|
|
} else {
|
|
ScriptError("Wrong number of arguments for normal_damage.\n");
|
|
}
|
|
|
|
Event e1(EV_Damage);
|
|
|
|
e1.AddEntity(attacker); //attacker
|
|
|
|
e1.AddFloat(damage); //damage
|
|
e1.AddEntity(attacker); //inflictor
|
|
e1.AddVector(position); //position
|
|
e1.AddVector(direction); //direction
|
|
e1.AddVector(normal); //normal
|
|
e1.AddInteger(0); //knockback
|
|
e1.AddInteger(DAMAGE_NONE); //damageflags
|
|
e1.AddInteger(MOD_NONE); //meansofdeath
|
|
e1.AddInteger(HITLOC_GENERAL); //location
|
|
|
|
ProcessEvent(e1);
|
|
}
|
|
|
|
void Entity::DrawBoundingBox(int showbboxes)
|
|
{
|
|
if (showbboxes >= 100) {
|
|
if (g_monitorNum->integer != entnum) {
|
|
if (!*g_monitor->string || g_monitor->string != targetname) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
showbboxes -= 100;
|
|
}
|
|
|
|
switch (showbboxes) {
|
|
case 1:
|
|
if (edict->s.solid) {
|
|
G_DebugRotatedBBox(origin, angles, mins, maxs, 1, 1, 0, 1);
|
|
}
|
|
break;
|
|
case 2:
|
|
if (edict->s.solid) {
|
|
G_DebugBBox(vec_zero, edict->r.absmin, edict->r.absmax, 1, 0, 1, 1);
|
|
}
|
|
break;
|
|
case 3:
|
|
if (edict->tiki && !(edict->s.renderfx & RF_DONTDRAW)) {
|
|
G_DebugRotatedBBox(origin, angles, mins, maxs, 1, 1, 0, 1);
|
|
}
|
|
break;
|
|
case 4:
|
|
G_DebugRotatedBBox(origin, angles, mins, maxs, 1, 1, 0, 1);
|
|
break;
|
|
case 7:
|
|
if (edict->tiki && !(edict->s.renderfx & RF_DONTDRAW)) {
|
|
Vector vMins, vMaxs;
|
|
|
|
gi.TIKI_CalculateBounds(edict->tiki, edict->s.scale, vMins, vMaxs);
|
|
if (!VectorCompare(vMins, vec_zero) || !VectorCompare(vMaxs, vec_zero)) {
|
|
if (edict->s.parent != ENTITYNUM_NONE && edict->s.tag_num >= 0) {
|
|
int i;
|
|
Vector vAng;
|
|
orientation_t oTag, oBox;
|
|
Entity* pParent;
|
|
|
|
pParent = G_GetEntity(edict->s.parent);
|
|
AnglesToAxis(pParent->angles, pParent->orientation);
|
|
oTag = G_TIKI_Orientation(pParent->edict, edict->s.tag_num & TAG_MASK);
|
|
|
|
VectorCopy(pParent->origin, oBox.origin);
|
|
for (i = 0; i < 3; i++) {
|
|
VectorMA(oBox.origin, oTag.origin[i], pParent->orientation[i], oBox.origin);
|
|
}
|
|
|
|
MatrixMultiply(oTag.axis, pParent->orientation, oBox.axis);
|
|
MatrixToEulerAngles(oBox.axis, vAng);
|
|
G_DebugRotatedBBox(oBox.origin, vAng, vMins, vMaxs, 0, 1, 1, 1);
|
|
} else {
|
|
G_DebugRotatedBBox(origin, angles, vMins, vMaxs, 0, 1, 1, 1);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case 8:
|
|
if (edict->tiki && !(edict->s.renderfx & RF_DONTDRAW)) {
|
|
G_DebugRotatedBBox(origin, angles, mins, maxs, 1, 1, 0, 1);
|
|
G_DebugBBox(origin, Vector(-4, -4, -4), Vector(4, 4, 4), 0, 1, 1, 1);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if ((showbboxes >= 9 && showbboxes <= 28) || (showbboxes >= -28 && showbboxes <= -9)) {
|
|
qboolean bDoPlayer = qfalse;
|
|
|
|
if (showbboxes >= 0) {
|
|
bDoPlayer = qfalse;
|
|
} else {
|
|
showbboxes = -showbboxes;
|
|
bDoPlayer = qtrue;
|
|
}
|
|
|
|
if (edict->s.solid == SOLID_NOT) {
|
|
return;
|
|
}
|
|
|
|
if ((edict != g_entities && !bDoPlayer) || (edict == g_entities && bDoPlayer)) {
|
|
int i, j;
|
|
int iTagNum;
|
|
int iCap;
|
|
float fRadius;
|
|
const char *pszTagName;
|
|
vec3_t vOffset;
|
|
vec3_t vPos;
|
|
vec3_t vColor;
|
|
orientation_t orTag;
|
|
orientation_t orPosition;
|
|
|
|
if (!IsSubclassOfSentient() || !edict->tiki) {
|
|
G_DebugBBox(origin, mins, maxs, 1.0, 1.0, 0.0, 1.0);
|
|
return;
|
|
}
|
|
|
|
AnglesToAxis(angles, orientation);
|
|
|
|
if (showbboxes <= 9 && showbboxes >= -9) {
|
|
i = 0;
|
|
iCap = 19;
|
|
} else {
|
|
i = showbboxes - 10;
|
|
if (i >= 18) {
|
|
return;
|
|
}
|
|
|
|
iCap = i + 1;
|
|
}
|
|
|
|
for (; i < iCap; i++) {
|
|
pszTagName = gi.CM_GetHitLocationInfo(i, &fRadius, vOffset);
|
|
|
|
if (sv_testloc_num->integer - 1 == i || (sv_testloc_num->integer && (showbboxes > 9 || showbboxes < -9))) {
|
|
fRadius = sv_testloc_radius->value;
|
|
vOffset[0] = sv_testloc_offset_x->value;
|
|
vOffset[1] = sv_testloc_offset_y->value;
|
|
vOffset[2] = sv_testloc_offset_z->value;
|
|
vColor[0] = 1.0;
|
|
vColor[1] = 0.5;
|
|
vColor[2] = 0.0;
|
|
} else {
|
|
vColor[0] = 0.0;
|
|
vColor[1] = 0.5;
|
|
vColor[2] = 1.0;
|
|
}
|
|
|
|
iTagNum = gi.Tag_NumForName(edict->tiki, pszTagName);
|
|
if (iTagNum < 0) {
|
|
return;
|
|
}
|
|
|
|
orTag = G_TIKI_Orientation(edict, iTagNum & TAG_MASK);
|
|
VectorCopy(origin, orPosition.origin);
|
|
|
|
for (j = 0; j < 3; j++) {
|
|
VectorMA(orPosition.origin, orTag.origin[j], orientation[j], orPosition.origin);
|
|
}
|
|
|
|
MatrixMultiply(orTag.axis, orientation, orPosition.axis);
|
|
MatrixTransformVector(vOffset, orPosition.axis, vPos);
|
|
VectorAdd(orPosition.origin, vPos, vPos);
|
|
|
|
G_DebugCircle(vPos, fRadius, vColor[0], vColor[1], vColor[2], 1.0);
|
|
|
|
pszTagName = gi.CM_GetHitLocationInfoSecondary(i, &fRadius, vOffset);
|
|
vColor[0] = 0.5;
|
|
vColor[1] = 0.0;
|
|
vColor[2] = 1.0;
|
|
|
|
if (!pszTagName || sv_testloc_num->integer - 1 == i || (sv_testloc_num->integer && (showbboxes > 9 || showbboxes < -9))) {
|
|
if (!sv_testloc_secondary->integer) {
|
|
continue;
|
|
}
|
|
|
|
if (sv_testloc_num->integer - 1 != i && (!sv_testloc_num->integer || (showbboxes <= 9 && showbboxes >= -9))) {
|
|
continue;
|
|
}
|
|
|
|
fRadius = sv_testloc_radius2->value;
|
|
vOffset[0] = sv_testloc_offset2_x->value;
|
|
vOffset[1] = sv_testloc_offset2_y->value;
|
|
vOffset[2] = sv_testloc_offset2_z->value;
|
|
vColor[0] = 1.0;
|
|
vColor[1] = 0.0;
|
|
vColor[2] = 0.5;
|
|
}
|
|
|
|
MatrixTransformVector(vOffset, orPosition.axis, vPos);
|
|
VectorAdd(orPosition.origin, vPos, vPos);
|
|
|
|
G_DebugCircle(vPos, fRadius, vColor[0], vColor[1], vColor[2], 1.0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
qboolean Entity::IsDead(void) const
|
|
{
|
|
return deadflag != DEAD_NO;
|
|
}
|
|
|
|
void Entity::AddImmunity(Event *ev)
|
|
{
|
|
str immune_string;
|
|
int new_immunity;
|
|
int number_of_immunities;
|
|
int i;
|
|
|
|
number_of_immunities = ev->NumArgs();
|
|
|
|
for (i = 1; i <= number_of_immunities; i++) {
|
|
immune_string = ev->GetString(i);
|
|
|
|
new_immunity = MOD_string_to_int(immune_string);
|
|
|
|
if (new_immunity != -1) {
|
|
immunities.AddUniqueObject(new_immunity);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Entity::RemoveImmunity(Event *ev)
|
|
{
|
|
str immune_string;
|
|
int old_immunity;
|
|
int number_of_immunities;
|
|
int i;
|
|
|
|
number_of_immunities = ev->NumArgs();
|
|
|
|
for (i = 1; i <= number_of_immunities; i++) {
|
|
immune_string = ev->GetString(i);
|
|
|
|
old_immunity = MOD_string_to_int(immune_string);
|
|
|
|
if (old_immunity != -1 && immunities.ObjectInList(old_immunity)) {
|
|
immunities.RemoveObject(old_immunity);
|
|
}
|
|
}
|
|
}
|
|
|
|
qboolean Entity::Immune(int meansofdeath)
|
|
{
|
|
int number_of_immunities, i;
|
|
|
|
number_of_immunities = immunities.NumObjects();
|
|
|
|
for (i = 1; i <= number_of_immunities; i++) {
|
|
if (meansofdeath == immunities.ObjectAt(i)) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void Entity::PathnodeClaimRevoked(PathNode *node)
|
|
{
|
|
return node->Relinquish();
|
|
}
|
|
|
|
qboolean Entity::BlocksAIMovement() const
|
|
{
|
|
return true;
|
|
}
|
|
|
|
qboolean Entity::AIDontFace(void) const
|
|
{
|
|
return true;
|
|
}
|
|
|
|
void Entity::EventGetEntnum(Event *ev)
|
|
{
|
|
ev->AddInteger(entnum);
|
|
}
|
|
|
|
void Entity::EventGetClassname(Event *ev)
|
|
{
|
|
ev->AddString(getClassname());
|
|
}
|
|
|
|
void Entity::SetModelEvent(Event *ev)
|
|
{
|
|
setModel(ev->GetString(1));
|
|
}
|
|
|
|
void Entity::EventSetRadnum(Event *ev)
|
|
{
|
|
radnum = ev->GetInteger(1);
|
|
}
|
|
|
|
void Entity::EventGetRadnum(Event *ev)
|
|
{
|
|
ev->AddInteger(radnum);
|
|
}
|
|
|
|
void Entity::EventSetRotatedBbox(Event *ev)
|
|
{
|
|
if (ev->GetInteger(1)) {
|
|
edict->s.eFlags |= EF_LINKANGLES;
|
|
} else {
|
|
edict->s.eFlags &= ~EF_LINKANGLES;
|
|
}
|
|
|
|
setAngles(angles);
|
|
}
|
|
|
|
void Entity::EventGetRotatedBbox(Event *ev)
|
|
{
|
|
ev->AddInteger((edict->s.eFlags & EF_LINKANGLES) ? 1 : 0);
|
|
}
|
|
|
|
void Entity::EventSinglePlayerCommand(Event *ev)
|
|
{
|
|
if (g_gametype->integer == GT_SINGLE_PLAYER && !g_realismmode->integer) {
|
|
Event *newev = new Event(ev->GetToken(1), ev->NumArgs() - 1);
|
|
|
|
for (int i = 2; i <= ev->NumArgs(); i++) {
|
|
newev->AddToken(ev->GetToken(i));
|
|
}
|
|
|
|
ProcessEvent(newev);
|
|
}
|
|
}
|
|
|
|
void Entity::EventMultiPlayerCommand(Event *ev)
|
|
{
|
|
if (g_gametype->integer != GT_SINGLE_PLAYER && !g_realismmode->integer) {
|
|
Event *newev = new Event(ev->GetToken(1), ev->NumArgs() - 1);
|
|
|
|
for (int i = 2; i <= ev->NumArgs(); i++) {
|
|
newev->AddToken(ev->GetToken(i));
|
|
}
|
|
|
|
ProcessEvent(newev);
|
|
}
|
|
}
|
|
|
|
void Entity::EventRealismModeCommand(Event *ev)
|
|
{
|
|
if (g_realismmode->integer) {
|
|
Event *newev = new Event(ev->GetToken(1), ev->NumArgs() - 1);
|
|
|
|
for (int i = 2; i <= ev->NumArgs(); i++) {
|
|
newev->AddToken(ev->GetToken(i));
|
|
}
|
|
|
|
ProcessEvent(newev);
|
|
}
|
|
}
|
|
|
|
void Entity::EventSPRealismModeCommand(Event *ev)
|
|
{
|
|
if (g_gametype->integer == GT_SINGLE_PLAYER && g_realismmode->integer) {
|
|
Event *newev = new Event(ev->GetToken(1), ev->NumArgs() - 1);
|
|
|
|
for (int i = 2; i <= ev->NumArgs(); i++) {
|
|
newev->AddToken(ev->GetToken(i));
|
|
}
|
|
|
|
ProcessEvent(newev);
|
|
}
|
|
}
|
|
|
|
void Entity::EventDMRealismModeCommand(Event *ev)
|
|
{
|
|
if (g_gametype->integer != GT_SINGLE_PLAYER && g_realismmode->integer) {
|
|
Event *newev = new Event(ev->GetToken(1), ev->NumArgs() - 1);
|
|
|
|
for (int i = 2; i <= ev->NumArgs(); i++) {
|
|
newev->AddToken(ev->GetToken(i));
|
|
}
|
|
|
|
ProcessEvent(newev);
|
|
}
|
|
}
|
|
|
|
void Entity::GetLocalYawFromVector(Event *ev)
|
|
{
|
|
float yaw;
|
|
Vector vec;
|
|
|
|
vec = ev->GetVector(1);
|
|
yaw = vectoyaw(vec) + 180.0f;
|
|
yaw = AngleSubtract(yaw, angles[1]);
|
|
ev->AddFloat(yaw);
|
|
}
|
|
|
|
void Entity::EventShootableOnly(Event *ev)
|
|
{
|
|
edict->r.contents |= CONTENTS_SHOOTONLY | CONTENTS_WEAPONCLIP;
|
|
}
|
|
|
|
void Entity::SetShaderTime(Event *ev)
|
|
{
|
|
edict->s.shader_time = level.time;
|
|
if (ev->NumArgs() > 0) {
|
|
edict->s.shader_time += ev->GetFloat(1);
|
|
}
|
|
|
|
if (ev->NumArgs() > 1) {
|
|
edict->s.shader_time += ev->GetFloat(1) * random() / 32768.0;
|
|
}
|
|
}
|
|
|
|
qboolean Entity::GetTagPositionAndOrientation(str tagname, orientation_t *new_or)
|
|
{
|
|
int tagnum;
|
|
|
|
tagnum = gi.Tag_NumForName(edict->tiki, tagname);
|
|
|
|
if (tagnum < 0) {
|
|
return false;
|
|
}
|
|
|
|
GetTagPositionAndOrientation(tagnum, new_or);
|
|
return true;
|
|
}
|
|
|
|
void Entity::NoTarget(Event *ev)
|
|
{
|
|
if (ev->GetBoolean(1)) {
|
|
flags |= FL_NOTARGET;
|
|
} else {
|
|
flags &= ~FL_NOTARGET;
|
|
}
|
|
}
|
|
|
|
void Entity::GetZone(Event *ev)
|
|
{
|
|
ev->AddInteger(edict->r.areanum);
|
|
|
|
// FIXME: Implement OPM feature
|
|
STUB();
|
|
|
|
#if 0
|
|
ZoneInfo *zone = getZone();
|
|
|
|
if( zone != NULL ) {
|
|
ev->AddListener( zone );
|
|
} else {
|
|
ev->AddNil();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void Entity::IsInZone(Event *ev)
|
|
{
|
|
if (edict->r.areanum == ev->GetInteger(1)) {
|
|
ev->AddInteger(1);
|
|
} else {
|
|
ev->AddInteger(0);
|
|
}
|
|
|
|
// FIXME: Implement OPM feature
|
|
STUB();
|
|
|
|
#if 0
|
|
ZoneInfo *zone = ( ZoneInfo * )ev->GetEntity( 1 );
|
|
ZoneInfo *currentZone = getZone();
|
|
|
|
if( zone == NULL ) {
|
|
ScriptError( "The zone must be a valid ZoneInfo!!!\n" );
|
|
}
|
|
|
|
if( !zone->inheritsFrom( "ZoneInfo" ) ) {
|
|
ScriptError( "The specified zone is not a ZoneInfo!!!\n" );
|
|
}
|
|
|
|
if( currentZone == zone || IsTouching( zone ) ) {
|
|
ev->AddInteger( 1 ); // The entity is in the specified zone
|
|
} else {
|
|
ev->AddInteger( 0 ); // The entity is not in the specified zone
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void Entity::SetDepthHack(Event *ev)
|
|
{
|
|
int bEnable;
|
|
Entity *entity = (Entity *)this;
|
|
|
|
bEnable = ev->GetInteger(1);
|
|
|
|
if (bEnable) {
|
|
entity->edict->s.renderfx |= RF_DEPTHHACK;
|
|
} else {
|
|
entity->edict->s.renderfx &= ~RF_DEPTHHACK;
|
|
}
|
|
}
|
|
|
|
#ifdef OPM_FEATURES
|
|
void Entity::ProcessHint(gentity_t *client, bool bShow)
|
|
{
|
|
Player *player = (Player *)client->entity;
|
|
|
|
if (client->client != NULL) {
|
|
qboolean is_hidden = bShow && (edict->s.renderfx & RF_INVISIBLE);
|
|
qboolean can_see = bShow
|
|
&& player->canUse(
|
|
this, m_bHintRequiresLookAt
|
|
); //( player->IsTouching( this ) || player->CanSee( this, string->fov, 94 ) == 1 );
|
|
|
|
if (bShow && can_see && !is_hidden && !player->IsDead() && !player->IsSpectator()) {
|
|
if (sv_specialgame->integer) {
|
|
// FIXME: delete
|
|
/*
|
|
gi.MSG_SetClient(client - g_entities);
|
|
|
|
// Send the hint string once
|
|
gi.MSG_StartCGM(CGM_HINTSTRING);
|
|
gi.MSG_WriteString(m_HintString);
|
|
gi.MSG_EndCGM();
|
|
*/
|
|
}
|
|
} else {
|
|
}
|
|
}
|
|
}
|
|
|
|
void Entity::SetHintRequireLookAt(Event *ev)
|
|
{
|
|
m_bHintRequiresLookAt = ev->GetBoolean(1);
|
|
}
|
|
|
|
void Entity::SetHintString(Event *ev)
|
|
{
|
|
m_HintString = ev->GetString(1);
|
|
}
|
|
|
|
void Entity::SetShader(Event* ev)
|
|
{
|
|
str shadername = ev->GetString(1);
|
|
qboolean fReset = false;
|
|
|
|
if (!shadername.length()) {
|
|
shadername = "default";
|
|
fReset = true;
|
|
}
|
|
|
|
gi.SendServerCommand(-1, "setshader %d %s %d", entnum, shadername.c_str(), fReset);
|
|
}
|
|
#endif
|
|
|
|
void Entity::PlayNonPvsSound(const str& soundName, float volume)
|
|
{
|
|
AliasListNode_t* ret;
|
|
str name;
|
|
|
|
if (g_gametype->integer == GT_SINGLE_PLAYER) {
|
|
// Ignore in single-player
|
|
return;
|
|
}
|
|
|
|
if (!sv_netoptimize->integer) {
|
|
// don't use sound indexes for nothing
|
|
return;
|
|
}
|
|
|
|
if (edict->r.num_nonpvs_sounds >= MAX_NONPVS_SOUNDS) {
|
|
return;
|
|
}
|
|
|
|
name = GetRandomAlias(soundName, &ret);
|
|
if (name.length() && ret) {
|
|
nonpvs_sound_t* npvs = &edict->r.nonpvs_sounds[edict->r.num_nonpvs_sounds];
|
|
|
|
npvs->index = gi.pvssoundindex(name.c_str(), ret->streamed);
|
|
npvs->volume = G_Random() * ret->volumeMod + ret->volume * volume;
|
|
npvs->minDist = ret->dist;
|
|
npvs->maxDist = ret->maxDist;
|
|
npvs->pitch = G_Random() * ret->pitchMod + ret->pitch;
|
|
edict->r.num_nonpvs_sounds++;
|
|
}
|
|
}
|