mirror of
https://github.com/blendogames/thirtyflightsofloving.git
synced 2024-11-14 16:40:57 +00:00
8083f057cf
Added better error message for clientnum underflow in HUD layout string. Changed cvar ion_ripper_extra_sounds to default to 0 in missionpack DLL.
550 lines
14 KiB
C
550 lines
14 KiB
C
#include "g_local.h"
|
|
|
|
//void plat_CalcMove (edict_t *ent, vec3_t dest, void(*func)(edict_t*));
|
|
|
|
void fd_secret_move1(edict_t *self);
|
|
void fd_secret_move2(edict_t *self);
|
|
void fd_secret_move3(edict_t *self);
|
|
void fd_secret_move4(edict_t *self);
|
|
void fd_secret_move5(edict_t *self);
|
|
void fd_secret_move6(edict_t *self);
|
|
void fd_secret_done(edict_t *self);
|
|
|
|
/*
|
|
=============================================================================
|
|
|
|
SECRET DOORS
|
|
|
|
=============================================================================
|
|
*/
|
|
|
|
#define SEC_OPEN_ONCE 1 // stays open
|
|
#define SEC_1ST_LEFT 2 // 1st move is left of arrow
|
|
#define SEC_1ST_DOWN 4 // 1st move is down from arrow
|
|
#define SEC_NO_SHOOT 8 // only opened by trigger
|
|
#define SEC_YES_SHOOT 16 // shootable even if targeted
|
|
#define SEC_MOVE_RIGHT 32
|
|
#define SEC_MOVE_FORWARD 64
|
|
|
|
#define STATE_TOP 0
|
|
#define STATE_BOTTOM 1
|
|
#define STATE_UP 2
|
|
#define STATE_DOWN 3
|
|
#define STATE_LOWEST 4
|
|
|
|
void fd_secret_use (edict_t *self, edict_t *other, edict_t *activator)
|
|
{
|
|
edict_t *ent;
|
|
|
|
// gi.dprintf("fd_secret_use\n");
|
|
if (self->flags & FL_TEAMSLAVE)
|
|
return;
|
|
|
|
// added sound
|
|
if (self->moveinfo.sound_start)
|
|
gi.sound (self, CHAN_NO_PHS_ADD+CHAN_VOICE, self->moveinfo.sound_start, 1, self->attenuation, 0); // was ATTN_STATIC
|
|
if (self->moveinfo.sound_middle) {
|
|
self->s.sound = self->moveinfo.sound_middle;
|
|
#ifdef LOOP_SOUND_ATTENUATION
|
|
self->s.attenuation = self->attenuation;
|
|
#endif
|
|
}
|
|
|
|
// trigger all paired doors
|
|
for (ent = self; ent; ent = ent->teamchain)
|
|
{
|
|
if (self->moveinfo.state == STATE_LOWEST)
|
|
{
|
|
ent->moveinfo.state = STATE_DOWN;
|
|
Move_Calc (ent, ent->pos1, fd_secret_move1);
|
|
door_use_areaportals (self, true);
|
|
}
|
|
else if (self->moveinfo.state == STATE_TOP) // Knightmare added
|
|
{
|
|
ent->moveinfo.state = STATE_UP;
|
|
Move_Calc (ent, ent->pos1, fd_secret_move5);
|
|
}
|
|
}
|
|
}
|
|
|
|
void fd_secret_killed (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
|
|
{
|
|
// gi.dprintf("fd_secret_killed\n");
|
|
// self->health = self->max_health;
|
|
self->takedamage = DAMAGE_NO;
|
|
|
|
if (self->flags & FL_TEAMSLAVE && self->teammaster && self->teammaster->takedamage != DAMAGE_NO)
|
|
fd_secret_killed (self->teammaster, inflictor, attacker, damage, point);
|
|
else
|
|
fd_secret_use (self, inflictor, attacker);
|
|
}
|
|
|
|
// Wait after first movement...
|
|
void fd_secret_move1 (edict_t *self)
|
|
{
|
|
// gi.dprintf("fd_secret_move1\n");
|
|
self->nextthink = level.time + 1.0;
|
|
self->think = fd_secret_move2;
|
|
self->moveinfo.state = STATE_BOTTOM;
|
|
|
|
// added sound
|
|
self->s.sound = 0;
|
|
if (self->moveinfo.sound_end)
|
|
gi.sound (self, CHAN_NO_PHS_ADD+CHAN_VOICE, self->moveinfo.sound_end, 1, self->attenuation, 0); // was ATTN_STATIC
|
|
}
|
|
|
|
// Start moving sideways w/sound...
|
|
void fd_secret_move2 (edict_t *self)
|
|
{
|
|
// gi.dprintf("fd_secret_move2\n");
|
|
// added sound
|
|
if (self->moveinfo.sound_start)
|
|
gi.sound (self, CHAN_NO_PHS_ADD+CHAN_VOICE, self->moveinfo.sound_start, 1, self->attenuation, 0); // was ATTN_STATIC
|
|
if (self->moveinfo.sound_middle) {
|
|
self->s.sound = self->moveinfo.sound_middle;
|
|
#ifdef LOOP_SOUND_ATTENUATION
|
|
self->s.attenuation = self->attenuation;
|
|
#endif
|
|
}
|
|
|
|
self->moveinfo.state = STATE_UP;
|
|
Move_Calc (self, self->pos2, fd_secret_move3);
|
|
}
|
|
|
|
// Wait here until time to go back...
|
|
void fd_secret_move3 (edict_t *self)
|
|
{
|
|
// gi.dprintf("fd_secret_move3\n");
|
|
if (!(self->spawnflags & SEC_OPEN_ONCE))
|
|
{
|
|
self->nextthink = level.time + self->wait;
|
|
self->think = fd_secret_move4;
|
|
}
|
|
self->moveinfo.state = STATE_TOP;
|
|
|
|
// added sound
|
|
self->s.sound = 0;
|
|
if (self->moveinfo.sound_end)
|
|
gi.sound (self, CHAN_NO_PHS_ADD+CHAN_VOICE, self->moveinfo.sound_end, 1, self->attenuation, 0); // was ATTN_STATIC
|
|
}
|
|
|
|
// Move backward...
|
|
void fd_secret_move4 (edict_t *self)
|
|
{
|
|
// gi.dprintf("fd_secret_move4\n");
|
|
// added sound
|
|
if (self->moveinfo.sound_start)
|
|
gi.sound (self, CHAN_NO_PHS_ADD+CHAN_VOICE, self->moveinfo.sound_start, 1, self->attenuation, 0); // was ATTN_STATIC
|
|
if (self->moveinfo.sound_middle) {
|
|
self->s.sound = self->moveinfo.sound_middle;
|
|
#ifdef LOOP_SOUND_ATTENUATION
|
|
self->s.attenuation = self->attenuation;
|
|
#endif
|
|
}
|
|
|
|
self->moveinfo.state = STATE_UP;
|
|
Move_Calc (self, self->pos1, fd_secret_move5);
|
|
}
|
|
|
|
// Wait 1 second...
|
|
void fd_secret_move5 (edict_t *self)
|
|
{
|
|
// gi.dprintf("fd_secret_move5\n");
|
|
self->nextthink = level.time + 1.0;
|
|
self->think = fd_secret_move6;
|
|
self->moveinfo.state = STATE_BOTTOM;
|
|
|
|
// added sound
|
|
self->s.sound = 0;
|
|
if (self->moveinfo.sound_end)
|
|
gi.sound (self, CHAN_NO_PHS_ADD+CHAN_VOICE, self->moveinfo.sound_end, 1, self->attenuation, 0); // was ATTN_STATIC
|
|
}
|
|
|
|
void fd_secret_move6 (edict_t *self)
|
|
{
|
|
// gi.dprintf("fd_secret_move6\n");
|
|
// added sound
|
|
if (self->moveinfo.sound_start)
|
|
gi.sound (self, CHAN_NO_PHS_ADD+CHAN_VOICE, self->moveinfo.sound_start, 1, self->attenuation, 0); // was ATTN_STATIC
|
|
if (self->moveinfo.sound_middle) {
|
|
self->s.sound = self->moveinfo.sound_middle;
|
|
#ifdef LOOP_SOUND_ATTENUATION
|
|
self->s.attenuation = self->attenuation;
|
|
#endif
|
|
}
|
|
|
|
self->moveinfo.state = STATE_DOWN;
|
|
Move_Calc (self, self->pos0, fd_secret_done);
|
|
}
|
|
|
|
void fd_secret_done (edict_t *self)
|
|
{
|
|
// gi.dprintf("fd_secret_done\n");
|
|
if (!self->targetname || self->spawnflags & SEC_YES_SHOOT)
|
|
{
|
|
self->health = 1;
|
|
self->takedamage = DAMAGE_YES;
|
|
self->die = fd_secret_killed;
|
|
}
|
|
self->moveinfo.state = STATE_LOWEST;
|
|
|
|
// added sound
|
|
self->s.sound = 0;
|
|
if (self->moveinfo.sound_end)
|
|
gi.sound (self, CHAN_NO_PHS_ADD+CHAN_VOICE, self->moveinfo.sound_end, 1, self->attenuation, 0); // was ATTN_STATIC
|
|
|
|
door_use_areaportals (self, false);
|
|
}
|
|
|
|
void secret_blocked (edict_t *self, edict_t *other)
|
|
{
|
|
// Remove dead Q1 monsters, as they can't be gibbed
|
|
if ( (other->svflags & SVF_DEADMONSTER) && (other->flags & FL_Q1_MONSTER) )
|
|
{
|
|
G_FreeEdict(other);
|
|
return;
|
|
}
|
|
|
|
if (!(self->flags & FL_TEAMSLAVE))
|
|
T_Damage (other, self, self, vec3_origin, other->s.origin, vec3_origin, self->dmg, 0, 0, MOD_CRUSH);
|
|
|
|
// if (time < self->attack_finished)
|
|
// return;
|
|
// self->attack_finished = time + 0.5;
|
|
// T_Damage (other, self, self, self->dmg);
|
|
}
|
|
|
|
/*
|
|
================
|
|
secret_touch
|
|
|
|
Prints messages
|
|
================
|
|
*/
|
|
void secret_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
|
|
{
|
|
if (other->health <= 0)
|
|
return;
|
|
|
|
// if (!(other->client))
|
|
// Lazarus: Allows robot usage
|
|
if (!other->client && !(other->flags & FL_ROBOT))
|
|
return;
|
|
|
|
if (self->monsterinfo.attack_finished > level.time)
|
|
return;
|
|
self->monsterinfo.attack_finished = level.time + 2;
|
|
|
|
if (self->message)
|
|
{
|
|
gi.centerprintf (other, self->message);
|
|
gi.sound (other, CHAN_AUTO, gi.soundindex ("misc/talk1.wav"), 1, ATTN_NORM, 0);
|
|
// fixme - put this sound back??
|
|
// gi.sound (other, CHAN_BODY, "misc/talk.wav", 1, ATTN_NORM);
|
|
}
|
|
}
|
|
|
|
|
|
/*QUAKED func_door_secret2 (0 .5 .8) ? OPEN_ONCE 1st_LEFT 1st_DOWN NO_SHOOT ALWAYS_SHOOT SLIDE_RIGHT SLIDE_FORWARD
|
|
Basic secret door. Slides back, then to the left. Angle determines direction.
|
|
|
|
FLAGS:
|
|
OPEN_ONCE = Stay open, never close
|
|
1st_LEFT = 1st move is left/right of arrow
|
|
1st_DOWN = 1st move is forwards/backwards
|
|
NO_SHOOT = not implemented yet
|
|
ALWAYS_SHOOT = even if targeted, keep shootable
|
|
SLIDE_RIGHT = the sideways move will be to right of arrow
|
|
SLIDE_FORWARD = the to/fro move will be forward
|
|
|
|
VALUES:
|
|
wait = # of seconds before coming back (5 default)
|
|
dmg = damage to inflict when blocked (2 default)
|
|
width- override how far the door pops out
|
|
length- override how far the door slides
|
|
*/
|
|
|
|
void SP_func_door_secret2 (edict_t *ent)
|
|
{
|
|
vec3_t forward, right, up;
|
|
// float lrSize, fbSize;
|
|
|
|
ent->class_id = ENTITY_FUNC_DOOR_SECRET2;
|
|
|
|
if ( (level.maptype == MAPTYPE_CUSTOM) && (ent->sounds > 4) && (ent->sounds < 100) ) // custom sounds
|
|
{
|
|
ent->moveinfo.sound_start = gi.soundindex (va("doors/dr%02i_strt.wav", ent->sounds));
|
|
ent->moveinfo.sound_middle = gi.soundindex (va("doors/dr%02i_mid.wav", ent->sounds));
|
|
ent->moveinfo.sound_end = gi.soundindex (va("doors/dr%02i_end.wav", ent->sounds));
|
|
}
|
|
else if (ent->sounds != 1)
|
|
{
|
|
ent->moveinfo.sound_start = gi.soundindex ("doors/dr1_strt.wav");
|
|
ent->moveinfo.sound_middle = gi.soundindex ("doors/dr1_mid.wav");
|
|
ent->moveinfo.sound_end = gi.soundindex ("doors/dr1_end.wav");
|
|
}
|
|
else
|
|
{
|
|
ent->moveinfo.sound_start = 0;
|
|
ent->moveinfo.sound_middle = 0;
|
|
ent->moveinfo.sound_end = 0;
|
|
}
|
|
|
|
if (ent->attenuation <= 0)
|
|
ent->attenuation = ATTN_STATIC;
|
|
|
|
if (!ent->dmg)
|
|
ent->dmg = 2;
|
|
|
|
AngleVectors (ent->s.angles, forward, right, up);
|
|
VectorCopy (ent->s.origin, ent->pos0);
|
|
VectorCopy (ent->s.angles, ent->move_angles);
|
|
G_SetMovedir (ent->s.angles, ent->movedir);
|
|
|
|
ent->movetype = MOVETYPE_PUSH;
|
|
ent->solid = SOLID_BSP;
|
|
gi.setmodel (ent, ent->model);
|
|
|
|
if (ent->move_angles[1] == 0 || ent->move_angles[1] == 180)
|
|
{
|
|
if (!ent->width)
|
|
ent->width = ent->size[1] - 2; // was lrSize
|
|
if (!ent->length)
|
|
ent->length = ent->size[0] - 2; // was fbSize
|
|
}
|
|
else if (ent->move_angles[1] == 90 || ent->move_angles[1] == 270)
|
|
{
|
|
if (!ent->width)
|
|
ent->width = ent->size[0] - 2; // was lrSize
|
|
if (!ent->length)
|
|
ent->length = ent->size[1] - 2; // was fbSize
|
|
}
|
|
else {
|
|
gi.dprintf("func_door_secret2 angles not set at 0, 90, 180, 270!\n");
|
|
}
|
|
|
|
if (ent->spawnflags & SEC_MOVE_FORWARD)
|
|
VectorScale (forward, ent->length, forward);
|
|
else
|
|
VectorScale (forward, ent->length * -1 , forward);
|
|
|
|
if (ent->spawnflags & SEC_MOVE_RIGHT)
|
|
VectorScale (right, ent->width, right);
|
|
else
|
|
VectorScale (right, ent->width * -1, right);
|
|
|
|
if (ent->spawnflags & SEC_1ST_DOWN)
|
|
{
|
|
VectorAdd (ent->s.origin, forward, ent->pos1); // was ent->moveinfo.start_origin
|
|
VectorAdd (ent->pos1, right, ent->pos2); // was ent->moveinfo.end_origin
|
|
}
|
|
else
|
|
{
|
|
VectorAdd (ent->s.origin, right, ent->pos1); // was ent->moveinfo.start_origin
|
|
VectorAdd (ent->pos1, forward, ent->pos2); // was ent->moveinfo.end_origin
|
|
}
|
|
|
|
ent->touch = secret_touch;
|
|
ent->blocked = secret_blocked;
|
|
ent->use = fd_secret_use;
|
|
|
|
ent->moveinfo.speed = 50;
|
|
ent->moveinfo.accel = 50;
|
|
ent->moveinfo.decel = 50;
|
|
ent->moveinfo.state = STATE_LOWEST;
|
|
|
|
if (!ent->targetname || ent->spawnflags & SEC_YES_SHOOT)
|
|
{
|
|
ent->health = 1;
|
|
ent->max_health = ent->health;
|
|
ent->takedamage = DAMAGE_YES;
|
|
ent->die = fd_secret_killed;
|
|
}
|
|
if (!ent->wait)
|
|
ent->wait = 5; // 5 seconds before closing
|
|
ent->postthink = train_move_children; // Knightmare- now supports movewith
|
|
|
|
gi.linkentity(ent);
|
|
}
|
|
|
|
// ==================================================
|
|
|
|
#define FWALL_START_ON 1
|
|
|
|
void force_wall_think(edict_t *self)
|
|
{
|
|
if (self->solid == SOLID_BSP)
|
|
{
|
|
gi.WriteByte (svc_temp_entity);
|
|
gi.WriteByte (TE_FORCEWALL);
|
|
gi.WritePosition (self->pos1);
|
|
gi.WritePosition (self->pos2);
|
|
gi.WriteByte (self->style);
|
|
gi.multicast (self->offset, MULTICAST_PVS);
|
|
}
|
|
|
|
self->think = force_wall_think;
|
|
self->nextthink = level.time + FRAMETIME;
|
|
}
|
|
|
|
void force_wall_use (edict_t *self, edict_t *other, edict_t *activator)
|
|
{
|
|
if (self->solid == SOLID_NOT)
|
|
{
|
|
self->wait = 0;
|
|
self->think = force_wall_think;
|
|
self->nextthink = level.time + FRAMETIME;
|
|
self->solid = SOLID_BSP;
|
|
KillBox(self); // Is this appropriate?
|
|
gi.linkentity (self);
|
|
}
|
|
else
|
|
{
|
|
self->wait = 1;
|
|
self->think = NULL;
|
|
self->nextthink = 0;
|
|
self->solid = SOLID_NOT;
|
|
gi.linkentity( self );
|
|
}
|
|
}
|
|
|
|
void force_wall_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
|
|
{
|
|
if (!self->dmg)
|
|
return;
|
|
|
|
if (!other->takedamage)
|
|
return;
|
|
|
|
if (self->timestamp > level.time)
|
|
return;
|
|
|
|
self->timestamp = level.time + 1;
|
|
|
|
if (!(self->spawnflags & 4))
|
|
{
|
|
if ((level.framenum % 10) == 0)
|
|
gi.sound (other, CHAN_AUTO, self->noise_index, 1, ATTN_NORM, 0);
|
|
}
|
|
|
|
T_Damage (other, self, self, vec3_origin, other->s.origin, vec3_origin, self->dmg, self->dmg, 0, MOD_TRIGGER_HURT);
|
|
}
|
|
|
|
/*QUAKED func_force_wall (1 0 1) ? START_ON
|
|
A vertical particle force wall. Turns on and solid when triggered.
|
|
If someone is in the force wall when it turns on, they're telefragged.
|
|
|
|
START_ON - forcewall begins activated. triggering will turn it off.
|
|
style - color of particles to use.
|
|
0: black
|
|
208: green
|
|
215: white
|
|
220: yellow
|
|
240: red
|
|
241: blue
|
|
224: orange
|
|
*/
|
|
void SP_func_force_wall(edict_t *ent)
|
|
{
|
|
ent->class_id = ENTITY_FUNC_FORCE_WALL;
|
|
|
|
gi.setmodel (ent, ent->model);
|
|
|
|
ent->offset[0] = (ent->absmax[0] + ent->absmin[0]) / 2;
|
|
ent->offset[1] = (ent->absmax[1] + ent->absmin[1]) / 2;
|
|
ent->offset[2] = (ent->absmax[2] + ent->absmin[2]) / 2;
|
|
|
|
ent->pos1[2] = ent->absmax[2];
|
|
ent->pos2[2] = ent->absmax[2];
|
|
if (ent->size[0] > ent->size[1])
|
|
{
|
|
ent->pos1[0] = ent->absmin[0];
|
|
ent->pos2[0] = ent->absmax[0];
|
|
ent->pos1[1] = ent->offset[1];
|
|
ent->pos2[1] = ent->offset[1];
|
|
}
|
|
else
|
|
{
|
|
ent->pos1[0] = ent->offset[0];
|
|
ent->pos2[0] = ent->offset[0];
|
|
ent->pos1[1] = ent->absmin[1];
|
|
ent->pos2[1] = ent->absmax[1];
|
|
}
|
|
|
|
if (!ent->style)
|
|
ent->style = 208;
|
|
|
|
ent->movetype = MOVETYPE_NONE;
|
|
ent->wait = 1;
|
|
|
|
if (ent->spawnflags & FWALL_START_ON)
|
|
{
|
|
ent->solid = SOLID_BSP;
|
|
ent->think = force_wall_think;
|
|
ent->nextthink = level.time + FRAMETIME;
|
|
}
|
|
else
|
|
ent->solid = SOLID_NOT;
|
|
|
|
ent->use = force_wall_use;
|
|
ent->touch = force_wall_touch;
|
|
|
|
ent->svflags = SVF_NOCLIENT;
|
|
|
|
gi.linkentity(ent);
|
|
}
|
|
|
|
/*======================================================================================*/
|
|
|
|
/*QUAKED func_dm_wall (1 0 1) ?
|
|
When the number of clients that connect to the server is less than
|
|
the value in maxclient, the wall will appear and cut off areas of
|
|
the map. This only applies when the level is first loaded, so if
|
|
2 players start, the map will be sized to 2 player. If another 16
|
|
connect then it'll be a bit frantic (haha)
|
|
|
|
count - number of clients before disappearing
|
|
*/
|
|
|
|
/*
|
|
FIXME : Make a spawnflag to check if the designer wants to use an effect.
|
|
actually make this a point entity so ANY entity can be set up like this.
|
|
i.e. one could target func_walls, weapons, ammo and health and have a different
|
|
setup depending on the number of clients conncted.
|
|
*/
|
|
|
|
void SP_func_dm_wall (edict_t *self)
|
|
{
|
|
qboolean spawn = false;
|
|
|
|
self->class_id = ENTITY_FUNC_DM_WALL;
|
|
|
|
// if it was lower than this something went very wrong.
|
|
if (self->count > 2)
|
|
{
|
|
if (self->count > game.maxclients)
|
|
{
|
|
spawn = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
gi.dprintf("func_dm_wall with a count less than 2 at %s");
|
|
|
|
G_FreeEdict(self);
|
|
return;
|
|
}
|
|
|
|
if (spawn)
|
|
{
|
|
self->movetype = MOVETYPE_PUSH;
|
|
gi.setmodel (self, self->model);
|
|
self->solid = SOLID_BSP;
|
|
gi.linkentity (self);
|
|
}
|
|
else
|
|
{
|
|
G_FreeEdict(self);
|
|
return;
|
|
}
|
|
}
|