heretic2-sdk/Toolkit/Programming/GameCode/game/g_field.c
1998-11-24 00:00:00 +00:00

635 lines
16 KiB
C

#include "g_local.h"
#include "p_types.h"
#include "Vector.h"
#include "p_actions2.h"
#include "g_DefaultMessageHandler.h"
#include "p_main2.h"
#include "buoy.h"
#include "m_stats.h"
#include "g_teleport.h"
extern void SP_misc_teleporter (edict_t *self);
void InitTrigger(edict_t *self);
void InitField(edict_t *self)
{
if(!Vec3IsZero(self->s.angles))
{
G_SetMovedir(self->s.angles, self->movedir);
}
self->classID = CID_TRIGGER; // fields are basically triggers
self->solid = SOLID_TRIGGER;
self->movetype = PHYSICSTYPE_NONE;
gi.setmodel (self, self->model);
self->svflags = SVF_NOCLIENT;
gi.linkentity(self);
}
void FogDensity_touch(edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
{
player_state_t *ps;
// Only players can know about fog density changes
if(other->client)
{
ps = &other->client->ps;
if (!self->target)
{
ps->fog_density = 0.0;
return;
}
ps->fog_density = strtod(self->target, NULL);
}
}
/*QUAKED trigger_fogdensity (.5 .5 .5) ?
Sets the value of r_fog_density
and the fog color
---------KEY----------------
target - fog density (.01 - .0001)
color - red green blue values (0 0 0)
range of 1.0 - 0
----------------------------
*/
void SP_trigger_fogdensity(edict_t *self)
{
InitField(self);
self->touch = FogDensity_touch;
self->solid = SOLID_TRIGGER;
}
//----------------------------------------------------------------------
// Force Field
//----------------------------------------------------------------------
#define FIELD_FORCE_ONCE 1
void push_touch(edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
{
vec3_t forward,up;
if(other->health > 0)
{
if (other->client) // A player???
{
// don't take falling damage immediately from this
VectorCopy(other->velocity, other->client->playerinfo.oldvelocity);
other->client->playerinfo.flags |= PLAYER_FLAG_USE_ENT_POS;
other->groundentity = NULL;
}
AngleVectors(self->s.angles,forward,NULL,up);
VectorMA(other->velocity,self->speed,forward,other->velocity);
VectorMA(other->velocity,self->speed,up,other->velocity);
}
G_UseTargets(self, self);
if(self->spawnflags & FIELD_FORCE_ONCE)
{
G_FreeEdict (self);
}
}
void push_touch_trigger(edict_t *self, edict_t *activator)
{
push_touch(self,activator,NULL,NULL);
}
void TrigPush_Deactivate(edict_t *self, G_Message_t *msg)
{
self->solid = SOLID_NOT;
self->touch = NULL;
}
void TrigPush_Activate(edict_t *self, G_Message_t *msg)
{
self->solid = SOLID_TRIGGER;
self->touch = push_touch;
gi.linkentity (self);
}
void TrigPushStaticsInit()
{
classStatics[CID_TRIG_PUSH].msgReceivers[G_MSG_SUSPEND] = TrigPush_Deactivate;
classStatics[CID_TRIG_PUSH].msgReceivers[G_MSG_UNSUSPEND] = TrigPush_Activate;
}
/*QUAKED trigger_push (.5 .5 .5) ? FORCE_ONCE
Pushes the player
---------KEYS----------
speed - how fast the player is pushed (default 500)
angle - the angle to push the player along the X,Y
zangle - the up direction to push the player (0 is straight up, 180 is straight down)
------------------------------------
-------FLAGS---------------
FORCE_ONCE - pushes once and then goes away
------------------------------------
*/
void SP_trigger_push(edict_t *self)
{
InitTrigger(self);
self->solid = SOLID_TRIGGER;
self->msgHandler = DefaultMsgHandler;
self->classID = CID_TRIG_PUSH;
if (!self->speed)
{
self->speed = 500;
}
self->s.angles[2] = st.zangle;
// Can't really use the normal trigger setup cause it doesn't update velocity often enough
self->touch = push_touch;
self->TriggerActivated = push_touch_trigger;
}
//----------------------------------------------------------------------
// Damage Field
//----------------------------------------------------------------------
void DamageField_Use(edict_t *self, edict_t *other, edict_t *activator);
void DamageField_Touch(edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf);
void TrigDamage_Deactivate(edict_t *self, G_Message_t *msg)
{
self->solid = SOLID_NOT;
self->use = NULL;
}
void TrigDamage_Activate(edict_t *self, G_Message_t *msg)
{
self->solid = SOLID_TRIGGER;
self->use = DamageField_Use;
gi.linkentity (self);
}
void TrigDamageStaticsInit()
{
classStatics[CID_TRIG_DAMAGE].msgReceivers[G_MSG_SUSPEND] = TrigDamage_Deactivate;
classStatics[CID_TRIG_DAMAGE].msgReceivers[G_MSG_UNSUSPEND] = TrigDamage_Activate;
}
/*QUAKED trigger_Damage (.5 .5 .5) ? START_OFF TOGGLE SILENT NO_PROTECTION SLOW
Any entity that Touches this will be Damage.
It does dmg points of Damage each server frame
SILENT supresses playing the sound
SLOW changes the Damage rate to once per second
NO_PROTECTION *nothing* stops the Damage
"dmg" default 5 (whole numbers only)
*/
void SP_trigger_Damage(edict_t *self)
{
if(deathmatch->value && self->dmg > 100)
{
self->spawnflags = DEATHMATCH_RANDOM;
SP_misc_teleporter(self);
return;
}
InitField(self);
self->msgHandler = DefaultMsgHandler;
self->classID = CID_TRIG_DAMAGE;
self->touch = DamageField_Touch;
if (!self->dmg)
self->dmg = 5;
if (self->spawnflags & 1)
self->solid = SOLID_NOT;
else
self->solid = SOLID_TRIGGER;
if (self->spawnflags & 2)
self->use = DamageField_Use;
self->movetype = PHYSICSTYPE_NONE;
gi.linkentity (self);
}
void DamageField_Use(edict_t *self, edict_t *other, edict_t *activator)
{
if (self->solid == SOLID_NOT)
self->solid = SOLID_TRIGGER;
else
self->solid = SOLID_NOT;
if (!(self->spawnflags & 2))
self->use = NULL;
}
void DamageField_Touch(edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
{
int dflags;
if (!other->takedamage)
return;
if (self->timestamp > level.time)
return;
if (self->spawnflags & 16)
self->timestamp = level.time + 1;
else
self->timestamp = level.time + FRAMETIME;
if (!(self->spawnflags & 4))
{
if ((level.framenum % 10) == 0)
gi.sound (other, CHAN_AUTO, self->noise_index, 1, ATTN_NORM, 0);
}
if (self->spawnflags & 8)
dflags = DAMAGE_NO_PROTECTION;
else
dflags = 0;
T_Damage (other, self, self, vec3_origin, other->s.origin, vec3_origin, self->dmg, self->dmg, dflags | DAMAGE_SPELL|DAMAGE_AVOID_ARMOR,MOD_DIED);
G_UseTargets(self, self);
}
//----------------------------------------------------------------------
// Gravity Field
//----------------------------------------------------------------------
void GravityField_Touch(edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf);
/*QUAKED trigger_Gravity (.5 .5 .5) ?
Changes the Touching entites Gravity to
the value of "Gravity". 1.0 is standard
Gravity for the level.
*/
void SP_trigger_Gravity(edict_t *self)
{
if (st.gravity == 0)
{
gi.dprintf("trigger_Gravity without gravity set at %s\n", vtos(self->s.origin));
G_FreeEdict (self);
return;
}
InitField(self);
self->gravity = atoi(st.gravity);
self->touch = GravityField_Touch;
}
void GravityField_Touch(edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
{
other->gravity = self->gravity;
G_UseTargets(self, self);
}
//----------------------------------------------------------------------
// Monster Jump Field
//----------------------------------------------------------------------
void MonsterJumpField_Touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
{
if (other->flags & (FL_FLY | FL_SWIM) )
return;
if (other->svflags & SVF_DEADMONSTER)
return;
if ( !(other->svflags & SVF_MONSTER))
return;
VectorMA(other->velocity, self->speed, self->movedir, other->velocity);
other->velocity[2] += self->accel;
other->groundentity = NULL;
}
/*QUAKED trigger_MonsterJump (.5 .5 .5) ?
Walking monsters that Touch this will jump in the direction of the trigger's angle
"speed" default to 200, the speed thrown forward
"height" default to 200, the speed thrown upwards
*/
void SP_trigger_MonsterJump(edict_t *self)
{
if (self->s.angles[YAW] == 0)
self->s.angles[YAW] = 360;
InitField(self);
if (!self->speed)
self->speed = 200;
if (!st.height)
self->accel = 200;
else
self->accel = st.height;
self->touch = MonsterJumpField_Touch;
}
//----------------------------------------------------------------------
// Monster Go to Buoy Trigger
//----------------------------------------------------------------------
#define TSF_BUOY_TOUCH 1
#define TSF_BUOY_IGNORE_ENEMY 2
#define TSF_BUOY_TELEPORT_SAFE 4
#define TSF_BUOY_TELEPORT_UNSAFE 8
#define TSF_BUOY_FIXED 16
#define TSF_BUOY_STAND 32
#define TSF_BUOY_WANDER 64
extern qboolean MG_MakeConnection(edict_t *self, buoy_t *first_buoy, qboolean skipjump);
qboolean MG_MonsterAttemptTeleport(edict_t *self, vec3_t destination, qboolean ignoreLOS);
void trigger_goto_buoy_execute (edict_t *self, edict_t *monster, edict_t *activator)
{
qboolean found = false;
buoy_t *found_buoy = NULL;
int i;
for(i = 0; i < level.active_buoys; i++)
{
found_buoy = &level.buoy_list[i];
if(found_buoy->targetname)
{
if(!strcmp(found_buoy->targetname, self->pathtarget))
{
found = true;
break;
}
}
}
if(!found)
{
vec3_t org;
VectorMA(self->mins, 0.5, self->maxs, org);
gi.dprintf("G.D.E. FAULT: trigger_goto_buoy at %s can't find it's pathtargeted buoy %s\n", vtos(org), self->pathtarget);
return;
}
if(self->spawnflags & TSF_BUOY_TELEPORT_SAFE)
{
if(MG_MonsterAttemptTeleport(monster, found_buoy->origin, false))
{
if(BUOY_DEBUG)
gi.dprintf("%s was teleported(safely) to %s by trigger_goto_buoy\n", monster->classname, found_buoy->targetname);
}
return;
}
else if(self->spawnflags & TSF_BUOY_TELEPORT_UNSAFE)
{
if(MG_MonsterAttemptTeleport(monster, found_buoy->origin, true))
{
if(BUOY_DEBUG)
gi.dprintf("%s was teleported(unsafely) to %s by trigger_goto_buoy\n", monster->classname, found_buoy->targetname);
}
return;
}
if(BUOY_DEBUG)
gi.dprintf("%s forced to go to buoy %s by trigger_goto_buoy\n", monster->classname, self->pathtarget);
if(self->spawnflags&TSF_BUOY_IGNORE_ENEMY)//make him ignore enemy until gets to dest buoy
{
monster->ai_mood_flags|=AI_MOOD_FLAG_IGNORE_ENEMY;
if(BUOY_DEBUG)
gi.dprintf("%s forced to ignore enemy by trigger_goto_buoy\n", monster->classname, self->pathtarget);
}
monster->spawnflags &= ~MSF_FIXED;
monster->ai_mood_flags|=AI_MOOD_FLAG_FORCED_BUOY;
monster->forced_buoy = found_buoy->id;
monster->ai_mood = AI_MOOD_NAVIGATE;
if(!monster->enemy)
monster->enemy = activator;
MG_RemoveBuoyEffects(monster);
MG_MakeConnection(monster, NULL, false);
if(self->spawnflags&TSF_BUOY_FIXED)
monster->ai_mood_flags|=AI_MOOD_FLAG_GOTO_FIXED;
if(self->spawnflags&TSF_BUOY_STAND)
monster->ai_mood_flags|=AI_MOOD_FLAG_GOTO_STAND;
if(self->spawnflags&TSF_BUOY_WANDER)
monster->ai_mood_flags|=AI_MOOD_FLAG_GOTO_WANDER;
//make him check mood NOW and get going! Don't wait for current anim to finish!
if(classStatics[monster->classID].msgReceivers[MSG_CHECK_MOOD])
QPostMessage(monster, MSG_CHECK_MOOD,PRI_DIRECTIVE, "i", monster->ai_mood);
else
{//no check mood message handler, just send a run and let him wait, i guess!
monster->mood_nextthink = 0;
QPostMessage(monster, MSG_RUN,PRI_DIRECTIVE, NULL);
}
}
void trigger_goto_buoy_touch_go (edict_t *self)
{
if(!self->enemy)
return;
if(!(self->enemy->svflags&SVF_MONSTER))
return;
if(self->enemy->health<=0)
return;
if(!(self->enemy->monsterinfo.aiflags&AI_USING_BUOYS))
return;
trigger_goto_buoy_execute(self, self->enemy, self->activator);
}
void trigger_goto_buoy_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
{
if(level.time < self->air_finished)
return;
if(!(other->svflags & SVF_MONSTER))
return;
if(!(other->monsterinfo.aiflags&AI_USING_BUOYS))
return;
if(other->health<=0)
return;
self->activator = other->enemy;
if(self->delay)
{
self->enemy = other;
self->think = trigger_goto_buoy_touch_go;
self->nextthink = level.time + self->delay;
return;
}
trigger_goto_buoy_execute(self, other, self->activator);
if(self->wait == -1)
{
self->touch = NULL;
self->use = NULL;
}
else
self->air_finished = level.time + self->wait;
}
void trigger_goto_buoy_use_go (edict_t *self)
{
edict_t *monster = NULL;
monster = G_Find(monster, FOFS(targetname), self->target);
if(!monster)
{
if(BUOY_DEBUG)
gi.dprintf("ERROR: trigger_goto_buoy can't find it's target monster %s\n", self->pathtarget);
return;
}
if(!(monster->svflags&SVF_MONSTER))
return;
if(monster->health<=0)
return;
if(!(monster->monsterinfo.aiflags&AI_USING_BUOYS))
return;
trigger_goto_buoy_execute(self, monster, self->activator);
}
void trigger_goto_buoy_use (edict_t *self, edict_t *other, edict_t *activator)
{
if(level.time < self->air_finished)
return;
self->activator = activator;
if(self->delay)
{
self->think = trigger_goto_buoy_use_go;
self->nextthink = level.time + self->delay;
return;
}
trigger_goto_buoy_use_go(self);
self->air_finished = level.time + self->wait;
}
void trigger_goto_buoy_find_target(edict_t *self)
{
qboolean found = false;
buoy_t *found_buoy = NULL;
int i;
self->think = NULL;
self->nextthink = -1;
for(i = 0; i < level.active_buoys; i++)
{
if(!stricmp(level.buoy_list[i].targetname, self->pathtarget))
{
found = true;
break;
}
}
if(!found)
{
vec3_t org;
VectorMA(self->mins, 0.5, self->maxs, org);
gi.dprintf("G.D.E. FAULT: trigger_goto_buoy at %s can't find it's pathtargeted buoy %s\n", vtos(org), self->pathtarget);
return;
}
}
/*QUAKED trigger_goto_buoy (.5 .5 .5) ? Touch IgnoreEnemy TeleportSafe TeleportUnSafe FIXED STAND WANDER
A monster touching this trigger will find the buoy with the "pathtarget" targetname and head for it if it can.
This is NOT a touch trigger for a player, only monsters should ever touch it and only if the TOUCH spawnflag is on.
To have a player touch-trigger it, have the player touch a normal trigger that fires this trigger... (sorry!)
Otherwise, acts like a normal trigger.
"pathtarget" - targetname of buoy monster should head to
Touch - should be able to be touch-activated by monsters- NOTE: This will try to force the entity touching the trigger to it's buoy- should NOT be intended to be touched by anything but monsters
IgnoreEnemy - Monster will ignore his enemy until he gets to his target buoy (or until attacked or woken up some other way, working on preventing this if desired)
TeleportSafe - Make monster teleport to target buoy only if there is nothing there and the player cannot see the monster and/or desination buoy
TeleportUnSafe - Same as TeleportSafe, but ignores whether or not the player can see the monster and/or desination buoy
If you wish to make an assassin teleport to a buoy, use TeleportUnsafe since he doesn't need to hide the teleport from the player
FIXED - Upon arriving at the target buoy, the monster will become fixed and wait for an enemy (will not move from that spot no matter what)
STAND - Upon arriving at the target buoy, the monster will forget any aenemy it has and simply stand around there until it sees another enemy
WANDER - Upon arriving at the target buoy, the monster will forget any aenemy it has and begin to wander around that buoy's vicinity
"wait" how long to wait between firings
"delay" how long to wait after being activated to actually try to send the monster away
*/
void SP_trigger_goto_buoy(edict_t *self)
{
InitField(self);
if(!self->pathtarget)
{
gi.dprintf("G.D.E. FAULT: trigger_goto_buoy with no pathtarget!\n");
G_FreeEdict(self);
return;
}
else if(BUOY_DEBUG)
{
self->think = trigger_goto_buoy_find_target;
self->nextthink = level.time + 0.5;
}
if(self->spawnflags&TSF_BUOY_TOUCH)
self->touch = trigger_goto_buoy_touch;
if(self->targetname)
{
if(!self->target)
gi.dprintf("G.D.E. FAULT: targeted trigger_goto_buoy with no monster target!\n");
self->use = trigger_goto_buoy_use;
}
}