thirtyflightsofloving/missionpack/g_newfnc.c
Knightmare66 8083f057cf Added V_Shutdown() function.
Added better error message for clientnum underflow in HUD layout string.
Changed cvar ion_ripper_extra_sounds to default to 0 in missionpack DLL.
2021-03-01 21:49:38 -05:00

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;
}
}