as released 1998-11-29
This commit is contained in:
commit
e3ace8e28d
95 changed files with 72059 additions and 0 deletions
298
Makefile
Normal file
298
Makefile
Normal file
|
@ -0,0 +1,298 @@
|
|||
|
||||
BUILD_DEBUG_DIR=debug
|
||||
BUILD_RELEASE_DIR=release
|
||||
|
||||
CC=gcc
|
||||
BASE_CFLAGS=-Dstricmp=strcasecmp
|
||||
RELEASE_CFLAGS=$(BASE_CFLAGS) -ffast-math -funroll-loops \
|
||||
-fomit-frame-pointer -fexpensive-optimizations
|
||||
DEBUG_CFLAGS=$(BASE_CFLAGS) -g
|
||||
LDFLAGS=-ldl -lm
|
||||
|
||||
SHLIBEXT=so
|
||||
|
||||
SHLIBCFLAGS=-fPIC
|
||||
SHLIBLDFLAGS=-shared
|
||||
|
||||
DO_CC=$(CC) $(CFLAGS) -o $@ -c $<
|
||||
DO_SHLIB_CC=$(CC) $(CFLAGS) $(SHLIBCFLAGS) -o $@ -c $<
|
||||
|
||||
TARGETS=$(BUILDDIR)/game$(ARCH).$(SHLIBEXT) \
|
||||
|
||||
build_debug:
|
||||
@-mkdir $(BUILD_DEBUG_DIR)
|
||||
$(MAKE) targets BUILDDIR=$(BUILD_DEBUG_DIR) CFLAGS="$(DEBUG_CFLAGS)"
|
||||
|
||||
build_release:
|
||||
@-mkdir $(BUILD_RELEASE_DIR)
|
||||
$(MAKE) targets BUILDDIR=$(BUILD_RELEASE_DIR) CFLAGS="$(RELEASE_CFLAGS)"
|
||||
|
||||
all: build_debug build_release
|
||||
|
||||
targets: $(TARGETS)
|
||||
|
||||
ROGUE_OBJS = \
|
||||
$(BUILDDIR)/dm_ball.o \
|
||||
$(BUILDDIR)/dm_tag.o \
|
||||
$(BUILDDIR)/g_ai.o \
|
||||
$(BUILDDIR)/g_chase.o \
|
||||
$(BUILDDIR)/g_cmds.o \
|
||||
$(BUILDDIR)/g_combat.o \
|
||||
$(BUILDDIR)/g_func.o \
|
||||
$(BUILDDIR)/g_items.o \
|
||||
$(BUILDDIR)/g_main.o \
|
||||
$(BUILDDIR)/g_misc.o \
|
||||
$(BUILDDIR)/g_monster.o \
|
||||
$(BUILDDIR)/g_newai.o \
|
||||
$(BUILDDIR)/g_newdm.o \
|
||||
$(BUILDDIR)/g_newfnc.o \
|
||||
$(BUILDDIR)/g_newtarg.o \
|
||||
$(BUILDDIR)/g_newtrig.o \
|
||||
$(BUILDDIR)/g_newweap.o \
|
||||
$(BUILDDIR)/g_phys.o \
|
||||
$(BUILDDIR)/g_save.o \
|
||||
$(BUILDDIR)/g_spawn.o \
|
||||
$(BUILDDIR)/g_sphere.o \
|
||||
$(BUILDDIR)/g_svcmds.o \
|
||||
$(BUILDDIR)/g_target.o \
|
||||
$(BUILDDIR)/g_trigger.o \
|
||||
$(BUILDDIR)/g_turret.o \
|
||||
$(BUILDDIR)/g_utils.o \
|
||||
$(BUILDDIR)/g_weapon.o \
|
||||
$(BUILDDIR)/m_actor.o \
|
||||
$(BUILDDIR)/m_berserk.o \
|
||||
$(BUILDDIR)/m_boss2.o \
|
||||
$(BUILDDIR)/m_boss3.o \
|
||||
$(BUILDDIR)/m_boss31.o \
|
||||
$(BUILDDIR)/m_boss32.o \
|
||||
$(BUILDDIR)/m_brain.o \
|
||||
$(BUILDDIR)/m_carrier.o \
|
||||
$(BUILDDIR)/m_chick.o \
|
||||
$(BUILDDIR)/m_flash.o \
|
||||
$(BUILDDIR)/m_flipper.o \
|
||||
$(BUILDDIR)/m_float.o \
|
||||
$(BUILDDIR)/m_flyer.o \
|
||||
$(BUILDDIR)/m_gladiator.o \
|
||||
$(BUILDDIR)/m_gunner.o \
|
||||
$(BUILDDIR)/m_hover.o \
|
||||
$(BUILDDIR)/m_infantry.o \
|
||||
$(BUILDDIR)/m_insane.o \
|
||||
$(BUILDDIR)/m_medic.o \
|
||||
$(BUILDDIR)/m_move.o \
|
||||
$(BUILDDIR)/m_mutant.o \
|
||||
$(BUILDDIR)/m_parasite.o \
|
||||
$(BUILDDIR)/m_soldier.o \
|
||||
$(BUILDDIR)/m_stalker.o \
|
||||
$(BUILDDIR)/m_supertank.o \
|
||||
$(BUILDDIR)/m_tank.o \
|
||||
$(BUILDDIR)/m_turret.o \
|
||||
$(BUILDDIR)/m_widow.o \
|
||||
$(BUILDDIR)/m_widow2.o \
|
||||
$(BUILDDIR)/p_client.o \
|
||||
$(BUILDDIR)/p_hud.o \
|
||||
$(BUILDDIR)/p_trail.o \
|
||||
$(BUILDDIR)/p_view.o \
|
||||
$(BUILDDIR)/p_weapon.o \
|
||||
$(BUILDDIR)/q_shared.o
|
||||
|
||||
$(BUILDDIR)/game$(ARCH).$(SHLIBEXT) : $(ROGUE_OBJS)
|
||||
$(CC) $(CFLAGS) $(SHLIBLDFLAGS) -o $@ $(ROGUE_OBJS)
|
||||
|
||||
$(BUILDDIR)/dm_ball.o : dm_ball.c
|
||||
$(DO_SHLIB_CC)
|
||||
|
||||
$(BUILDDIR)/dm_tag.o : dm_tag.c
|
||||
$(DO_SHLIB_CC)
|
||||
|
||||
$(BUILDDIR)/g_ai.o : g_ai.c
|
||||
$(DO_SHLIB_CC)
|
||||
|
||||
$(BUILDDIR)/g_chase.o : g_chase.c
|
||||
$(DO_SHLIB_CC)
|
||||
|
||||
$(BUILDDIR)/g_cmds.o : g_cmds.c
|
||||
$(DO_SHLIB_CC)
|
||||
|
||||
$(BUILDDIR)/g_combat.o : g_combat.c
|
||||
$(DO_SHLIB_CC)
|
||||
|
||||
$(BUILDDIR)/g_func.o : g_func.c
|
||||
$(DO_SHLIB_CC)
|
||||
|
||||
$(BUILDDIR)/g_items.o : g_items.c
|
||||
$(DO_SHLIB_CC)
|
||||
|
||||
$(BUILDDIR)/g_main.o : g_main.c
|
||||
$(DO_SHLIB_CC)
|
||||
|
||||
$(BUILDDIR)/g_misc.o : g_misc.c
|
||||
$(DO_SHLIB_CC)
|
||||
|
||||
$(BUILDDIR)/g_monster.o : g_monster.c
|
||||
$(DO_SHLIB_CC)
|
||||
|
||||
$(BUILDDIR)/g_newai.o : g_newai.c
|
||||
$(DO_SHLIB_CC)
|
||||
|
||||
$(BUILDDIR)/g_newdm.o : g_newdm.c
|
||||
$(DO_SHLIB_CC)
|
||||
|
||||
$(BUILDDIR)/g_newfnc.o : g_newfnc.c
|
||||
$(DO_SHLIB_CC)
|
||||
|
||||
$(BUILDDIR)/g_newtarg.o : g_newtarg.c
|
||||
$(DO_SHLIB_CC)
|
||||
|
||||
$(BUILDDIR)/g_newtrig.o : g_newtrig.c
|
||||
$(DO_SHLIB_CC)
|
||||
|
||||
$(BUILDDIR)/g_newweap.o : g_newweap.c
|
||||
$(DO_SHLIB_CC)
|
||||
|
||||
$(BUILDDIR)/g_phys.o : g_phys.c
|
||||
$(DO_SHLIB_CC)
|
||||
|
||||
$(BUILDDIR)/g_save.o : g_save.c
|
||||
$(DO_SHLIB_CC)
|
||||
|
||||
$(BUILDDIR)/g_spawn.o : g_spawn.c
|
||||
$(DO_SHLIB_CC)
|
||||
|
||||
$(BUILDDIR)/g_sphere.o : g_sphere.c
|
||||
$(DO_SHLIB_CC)
|
||||
|
||||
$(BUILDDIR)/g_svcmds.o : g_svcmds.c
|
||||
$(DO_SHLIB_CC)
|
||||
|
||||
$(BUILDDIR)/g_target.o : g_target.c
|
||||
$(DO_SHLIB_CC)
|
||||
|
||||
$(BUILDDIR)/g_trigger.o : g_trigger.c
|
||||
$(DO_SHLIB_CC)
|
||||
|
||||
$(BUILDDIR)/g_turret.o : g_turret.c
|
||||
$(DO_SHLIB_CC)
|
||||
|
||||
$(BUILDDIR)/g_utils.o : g_utils.c
|
||||
$(DO_SHLIB_CC)
|
||||
|
||||
$(BUILDDIR)/g_weapon.o : g_weapon.c
|
||||
$(DO_SHLIB_CC)
|
||||
|
||||
$(BUILDDIR)/m_actor.o : m_actor.c
|
||||
$(DO_SHLIB_CC)
|
||||
|
||||
$(BUILDDIR)/m_berserk.o : m_berserk.c
|
||||
$(DO_SHLIB_CC)
|
||||
|
||||
$(BUILDDIR)/m_boss2.o : m_boss2.c
|
||||
$(DO_SHLIB_CC)
|
||||
|
||||
$(BUILDDIR)/m_boss3.o : m_boss3.c
|
||||
$(DO_SHLIB_CC)
|
||||
|
||||
$(BUILDDIR)/m_boss31.o : m_boss31.c
|
||||
$(DO_SHLIB_CC)
|
||||
|
||||
$(BUILDDIR)/m_boss32.o : m_boss32.c
|
||||
$(DO_SHLIB_CC)
|
||||
|
||||
$(BUILDDIR)/m_brain.o : m_brain.c
|
||||
$(DO_SHLIB_CC)
|
||||
|
||||
$(BUILDDIR)/m_carrier.o : m_carrier.c
|
||||
$(DO_SHLIB_CC)
|
||||
|
||||
$(BUILDDIR)/m_chick.o : m_chick.c
|
||||
$(DO_SHLIB_CC)
|
||||
|
||||
$(BUILDDIR)/m_flash.o : m_flash.c
|
||||
$(DO_SHLIB_CC)
|
||||
|
||||
$(BUILDDIR)/m_flipper.o : m_flipper.c
|
||||
$(DO_SHLIB_CC)
|
||||
|
||||
$(BUILDDIR)/m_float.o : m_float.c
|
||||
$(DO_SHLIB_CC)
|
||||
|
||||
$(BUILDDIR)/m_flyer.o : m_flyer.c
|
||||
$(DO_SHLIB_CC)
|
||||
|
||||
$(BUILDDIR)/m_gladiator.o : m_gladiator.c
|
||||
$(DO_SHLIB_CC)
|
||||
|
||||
$(BUILDDIR)/m_gunner.o : m_gunner.c
|
||||
$(DO_SHLIB_CC)
|
||||
|
||||
$(BUILDDIR)/m_hover.o : m_hover.c
|
||||
$(DO_SHLIB_CC)
|
||||
|
||||
$(BUILDDIR)/m_infantry.o : m_infantry.c
|
||||
$(DO_SHLIB_CC)
|
||||
|
||||
$(BUILDDIR)/m_insane.o : m_insane.c
|
||||
$(DO_SHLIB_CC)
|
||||
|
||||
$(BUILDDIR)/m_medic.o : m_medic.c
|
||||
$(DO_SHLIB_CC)
|
||||
|
||||
$(BUILDDIR)/m_move.o : m_move.c
|
||||
$(DO_SHLIB_CC)
|
||||
|
||||
$(BUILDDIR)/m_mutant.o : m_mutant.c
|
||||
$(DO_SHLIB_CC)
|
||||
|
||||
$(BUILDDIR)/m_parasite.o : m_parasite.c
|
||||
$(DO_SHLIB_CC)
|
||||
|
||||
$(BUILDDIR)/m_soldier.o : m_soldier.c
|
||||
$(DO_SHLIB_CC)
|
||||
|
||||
$(BUILDDIR)/m_stalker.o : m_stalker.c
|
||||
$(DO_SHLIB_CC)
|
||||
|
||||
$(BUILDDIR)/m_supertank.o : m_supertank.c
|
||||
$(DO_SHLIB_CC)
|
||||
|
||||
$(BUILDDIR)/m_tank.o : m_tank.c
|
||||
$(DO_SHLIB_CC)
|
||||
|
||||
$(BUILDDIR)/m_turret.o : m_turret.c
|
||||
$(DO_SHLIB_CC)
|
||||
|
||||
$(BUILDDIR)/m_widow.o : m_widow.c
|
||||
$(DO_SHLIB_CC)
|
||||
|
||||
$(BUILDDIR)/m_widow2.o : m_widow2.c
|
||||
$(DO_SHLIB_CC)
|
||||
|
||||
$(BUILDDIR)/p_client.o : p_client.c
|
||||
$(DO_SHLIB_CC)
|
||||
|
||||
$(BUILDDIR)/p_hud.o : p_hud.c
|
||||
$(DO_SHLIB_CC)
|
||||
|
||||
$(BUILDDIR)/p_trail.o : p_trail.c
|
||||
$(DO_SHLIB_CC)
|
||||
|
||||
$(BUILDDIR)/p_view.o : p_view.c
|
||||
$(DO_SHLIB_CC)
|
||||
|
||||
$(BUILDDIR)/p_weapon.o : p_weapon.c
|
||||
$(DO_SHLIB_CC)
|
||||
|
||||
$(BUILDDIR)/q_shared.o : q_shared.c
|
||||
$(DO_SHLIB_CC)
|
||||
|
||||
######################
|
||||
|
||||
clean: clean-debug clean-release
|
||||
|
||||
clean-debug:
|
||||
$(MAKE) clean2 BUILDDIR=$(BUILD_DEBUG_DIR) CFLAGS="$(DEBUG_CFLAGS)"
|
||||
|
||||
clean-release:
|
||||
$(MAKE) clean2 BUILDDIR=$(BUILD_RELEASE_DIR) CFLAGS="$(DEBUG_CFLAGS)"
|
||||
|
||||
clean2:
|
||||
-rm -f $(ROGUE_OBJS)
|
678
dm_ball.c
Normal file
678
dm_ball.c
Normal file
|
@ -0,0 +1,678 @@
|
|||
// dm_ball.c
|
||||
// pmack
|
||||
// june 98
|
||||
|
||||
#include "g_local.h"
|
||||
|
||||
// defines
|
||||
|
||||
#define DBALL_GOAL_TEAM1 0x0001
|
||||
#define DBALL_GOAL_TEAM2 0x0002
|
||||
|
||||
// globals
|
||||
|
||||
edict_t *dball_ball_entity = NULL;
|
||||
int dball_ball_startpt_count;
|
||||
int dball_team1_goalscore;
|
||||
int dball_team2_goalscore;
|
||||
|
||||
cvar_t *dball_team1_skin;
|
||||
cvar_t *dball_team2_skin;
|
||||
cvar_t *goallimit;
|
||||
|
||||
// prototypes
|
||||
|
||||
extern void EndDMLevel (void);
|
||||
extern void ClientUserinfoChanged (edict_t *ent, char *userinfo);
|
||||
extern void SelectSpawnPoint (edict_t *ent, vec3_t origin, vec3_t angles);
|
||||
extern float PlayersRangeFromSpot (edict_t *spot);
|
||||
|
||||
void DBall_BallDie (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point);
|
||||
void DBall_BallRespawn (edict_t *self);
|
||||
|
||||
// **************************
|
||||
// Game rules
|
||||
// **************************
|
||||
|
||||
int DBall_CheckDMRules (void)
|
||||
{
|
||||
if(goallimit && goallimit->value)
|
||||
{
|
||||
if(dball_team1_goalscore >= goallimit->value)
|
||||
gi.bprintf (PRINT_HIGH, "Team 1 Wins.\n");
|
||||
else if(dball_team2_goalscore >= goallimit->value)
|
||||
gi.bprintf (PRINT_HIGH, "Team 2 Wins.\n");
|
||||
else
|
||||
return 0;
|
||||
|
||||
EndDMLevel ();
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//==================
|
||||
//==================
|
||||
void DBall_ClientBegin (edict_t *ent)
|
||||
{
|
||||
int team1, team2, unassigned;
|
||||
edict_t *other;
|
||||
char *p;
|
||||
static char value[512];
|
||||
int j;
|
||||
|
||||
team1 = 0;
|
||||
team2 = 0;
|
||||
unassigned = 0;
|
||||
|
||||
for (j = 1; j <= game.maxclients; j++)
|
||||
{
|
||||
other = &g_edicts[j];
|
||||
if (!other->inuse)
|
||||
continue;
|
||||
if (!other->client)
|
||||
continue;
|
||||
if (other == ent) // don't count the new player
|
||||
continue;
|
||||
|
||||
strcpy(value, Info_ValueForKey (other->client->pers.userinfo, "skin"));
|
||||
p = strchr(value, '/');
|
||||
if (p)
|
||||
{
|
||||
if(!strcmp(dball_team1_skin->string, value))
|
||||
team1++;
|
||||
else if(!strcmp(dball_team2_skin->string, value))
|
||||
team2++;
|
||||
else
|
||||
unassigned++;
|
||||
}
|
||||
else
|
||||
unassigned++;
|
||||
}
|
||||
|
||||
if(team1 > team2)
|
||||
{
|
||||
gi.dprintf("assigned to team 2\n");
|
||||
Info_SetValueForKey(ent->client->pers.userinfo, "skin", dball_team2_skin->string);
|
||||
}
|
||||
else
|
||||
{
|
||||
gi.dprintf("assigned to team 1\n");
|
||||
Info_SetValueForKey(ent->client->pers.userinfo, "skin", dball_team1_skin->string);
|
||||
}
|
||||
|
||||
ClientUserinfoChanged(ent, ent->client->pers.userinfo);
|
||||
|
||||
if(unassigned)
|
||||
gi.dprintf("%d unassigned players present!\n", unassigned);
|
||||
}
|
||||
|
||||
//==================
|
||||
//==================
|
||||
void DBall_SelectSpawnPoint (edict_t *ent, vec3_t origin, vec3_t angles)
|
||||
{
|
||||
edict_t *bestspot;
|
||||
float bestdistance, bestplayerdistance;
|
||||
edict_t *spot;
|
||||
char *spottype;
|
||||
char skin[512];
|
||||
|
||||
strcpy(skin, Info_ValueForKey (ent->client->pers.userinfo, "skin"));
|
||||
if(!strcmp(dball_team1_skin->string, skin))
|
||||
spottype = "dm_dball_team1_start";
|
||||
else if(!strcmp(dball_team2_skin->string, skin))
|
||||
spottype = "dm_dball_team2_start";
|
||||
else
|
||||
spottype = "info_player_deathmatch";
|
||||
|
||||
spot = NULL;
|
||||
bestspot = NULL;
|
||||
bestdistance = 0;
|
||||
while ((spot = G_Find (spot, FOFS(classname), spottype)) != NULL)
|
||||
{
|
||||
bestplayerdistance = PlayersRangeFromSpot (spot);
|
||||
|
||||
if (bestplayerdistance > bestdistance)
|
||||
{
|
||||
bestspot = spot;
|
||||
bestdistance = bestplayerdistance;
|
||||
}
|
||||
}
|
||||
|
||||
if (bestspot)
|
||||
{
|
||||
VectorCopy (bestspot->s.origin, origin);
|
||||
origin[2] += 9;
|
||||
VectorCopy (bestspot->s.angles, angles);
|
||||
return;
|
||||
}
|
||||
|
||||
// if we didn't find an appropriate spawnpoint, just
|
||||
// call the standard one.
|
||||
SelectSpawnPoint(ent, origin, angles);
|
||||
}
|
||||
|
||||
//==================
|
||||
//==================
|
||||
void DBall_GameInit (void)
|
||||
{
|
||||
// we don't want a minimum speed for friction to take effect.
|
||||
// this will allow any knockback to move stuff.
|
||||
sv_stopspeed->value = 0;
|
||||
dball_team1_goalscore = 0;
|
||||
dball_team2_goalscore = 0;
|
||||
|
||||
dmflags->value = (int)dmflags->value | DF_NO_MINES | DF_NO_NUKES | DF_NO_STACK_DOUBLE |
|
||||
DF_NO_FRIENDLY_FIRE | DF_SKINTEAMS;
|
||||
|
||||
dball_team1_skin = gi.cvar ("dball_team1_skin", "male/ctf_r", 0);
|
||||
dball_team2_skin = gi.cvar ("dball_team2_skin", "male/ctf_b", 0);
|
||||
goallimit = gi.cvar ("goallimit", "0", 0);
|
||||
}
|
||||
|
||||
//==================
|
||||
//==================
|
||||
void DBall_PostInitSetup (void)
|
||||
{
|
||||
edict_t *e;
|
||||
|
||||
e=NULL;
|
||||
// turn teleporter destinations nonsolid.
|
||||
while(e = G_Find (e, FOFS(classname), "misc_teleporter_dest"))
|
||||
{
|
||||
e->solid = SOLID_NOT;
|
||||
gi.linkentity (e);
|
||||
}
|
||||
|
||||
// count the ball start points
|
||||
dball_ball_startpt_count = 0;
|
||||
e=NULL;
|
||||
while(e = G_Find (e, FOFS(classname), "dm_dball_ball_start"))
|
||||
{
|
||||
dball_ball_startpt_count++;
|
||||
}
|
||||
|
||||
if(dball_ball_startpt_count == 0)
|
||||
gi.dprintf("No Deathball start points!\n");
|
||||
}
|
||||
|
||||
//==================
|
||||
// DBall_ChangeDamage - half damage between players. full if it involves
|
||||
// the ball entity
|
||||
//==================
|
||||
int DBall_ChangeDamage (edict_t *targ, edict_t *attacker, int damage, int mod)
|
||||
{
|
||||
// cut player -> ball damage to 1
|
||||
if (targ == dball_ball_entity)
|
||||
return 1;
|
||||
|
||||
// damage player -> player is halved
|
||||
if (attacker != dball_ball_entity)
|
||||
return damage / 2;
|
||||
|
||||
return damage;
|
||||
}
|
||||
|
||||
//==================
|
||||
//==================
|
||||
int DBall_ChangeKnockback (edict_t *targ, edict_t *attacker, int knockback, int mod)
|
||||
{
|
||||
if(targ != dball_ball_entity)
|
||||
return knockback;
|
||||
|
||||
if(knockback < 1)
|
||||
{
|
||||
// FIXME - these don't account for quad/double
|
||||
if(mod == MOD_ROCKET) // rocket
|
||||
knockback = 70;
|
||||
else if(mod == MOD_BFG_EFFECT) // bfg
|
||||
knockback = 90;
|
||||
else
|
||||
gi.dprintf ("zero knockback, mod %d\n", mod);
|
||||
}
|
||||
else
|
||||
{
|
||||
// FIXME - change this to an array?
|
||||
switch(mod)
|
||||
{
|
||||
case MOD_BLASTER:
|
||||
knockback *= 3;
|
||||
break;
|
||||
case MOD_SHOTGUN:
|
||||
knockback = (knockback * 3) / 8;
|
||||
break;
|
||||
case MOD_SSHOTGUN:
|
||||
knockback = knockback / 3;
|
||||
break;
|
||||
case MOD_MACHINEGUN:
|
||||
knockback = (knockback * 3) / 2;
|
||||
break;
|
||||
case MOD_HYPERBLASTER:
|
||||
knockback *= 4;
|
||||
break;
|
||||
case MOD_GRENADE:
|
||||
case MOD_HANDGRENADE:
|
||||
case MOD_PROX:
|
||||
case MOD_G_SPLASH:
|
||||
case MOD_HG_SPLASH:
|
||||
case MOD_HELD_GRENADE:
|
||||
case MOD_TRACKER:
|
||||
case MOD_DISINTEGRATOR:
|
||||
knockback /= 2;
|
||||
break;
|
||||
case MOD_R_SPLASH:
|
||||
knockback = (knockback * 3) / 2;
|
||||
break;
|
||||
case MOD_RAILGUN:
|
||||
case MOD_HEATBEAM:
|
||||
knockback /= 3;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// gi.dprintf("mod: %d knockback: %d\n", mod, knockback);
|
||||
return knockback;
|
||||
}
|
||||
|
||||
// **************************
|
||||
// Goals
|
||||
// **************************
|
||||
|
||||
void DBall_GoalTouch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
|
||||
{
|
||||
int team_score;
|
||||
int scorechange;
|
||||
int j;
|
||||
char value[512];
|
||||
char *p;
|
||||
edict_t *ent;
|
||||
|
||||
if(other != dball_ball_entity)
|
||||
return;
|
||||
|
||||
self->health = self->max_health;
|
||||
|
||||
// determine which team scored, and bump the team score
|
||||
if(self->spawnflags & DBALL_GOAL_TEAM1)
|
||||
{
|
||||
dball_team1_goalscore += self->wait;
|
||||
team_score = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
dball_team2_goalscore += self->wait;
|
||||
team_score = 2;
|
||||
}
|
||||
|
||||
// bump the score for everyone on the correct team.
|
||||
for (j = 1; j <= game.maxclients; j++)
|
||||
{
|
||||
ent = &g_edicts[j];
|
||||
if (!ent->inuse)
|
||||
continue;
|
||||
if (!ent->client)
|
||||
continue;
|
||||
|
||||
if (ent == other->enemy)
|
||||
scorechange = self->wait + 5;
|
||||
else
|
||||
scorechange = self->wait;
|
||||
|
||||
strcpy(value, Info_ValueForKey (ent->client->pers.userinfo, "skin"));
|
||||
p = strchr(value, '/');
|
||||
if (p)
|
||||
{
|
||||
if(!strcmp(dball_team1_skin->string, value))
|
||||
{
|
||||
if(team_score == 1)
|
||||
ent->client->resp.score += scorechange;
|
||||
else if(other->enemy == ent)
|
||||
ent->client->resp.score -= scorechange;
|
||||
}
|
||||
else if(!strcmp(dball_team2_skin->string, value))
|
||||
{
|
||||
if(team_score == 2)
|
||||
ent->client->resp.score += scorechange;
|
||||
else if(other->enemy == ent)
|
||||
ent->client->resp.score -= scorechange;
|
||||
}
|
||||
else
|
||||
gi.dprintf("unassigned player!!!!\n");
|
||||
}
|
||||
}
|
||||
|
||||
if(other->enemy)
|
||||
gi.dprintf("score for team %d by %s\n", team_score, other->enemy->client->pers.netname);
|
||||
else
|
||||
gi.dprintf("score for team %d by someone\n", team_score);
|
||||
|
||||
DBall_BallDie (other, other->enemy, other->enemy, 0, vec3_origin);
|
||||
|
||||
G_UseTargets (self, other);
|
||||
}
|
||||
|
||||
// **************************
|
||||
// Ball
|
||||
// **************************
|
||||
|
||||
edict_t *PickBallStart (edict_t *ent)
|
||||
{
|
||||
int which, current;
|
||||
edict_t *e;
|
||||
|
||||
which = ceil(random() * dball_ball_startpt_count);
|
||||
e = NULL;
|
||||
current = 0;
|
||||
|
||||
while(e = G_Find (e, FOFS(classname), "dm_dball_ball_start"))
|
||||
{
|
||||
current++;
|
||||
if(current == which)
|
||||
return e;
|
||||
}
|
||||
|
||||
if(current == 0)
|
||||
gi.dprintf("No ball start points found!\n");
|
||||
|
||||
return G_Find(NULL, FOFS(classname), "dm_dball_ball_start");
|
||||
}
|
||||
|
||||
//==================
|
||||
// DBall_BallTouch - if the ball hit another player, hurt them
|
||||
//==================
|
||||
void DBall_BallTouch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
|
||||
{
|
||||
vec3_t dir;
|
||||
float dot;
|
||||
float speed;
|
||||
|
||||
if(other->takedamage == DAMAGE_NO)
|
||||
return;
|
||||
|
||||
// hit a player
|
||||
if(other->client)
|
||||
{
|
||||
if(ent->velocity[0] || ent->velocity[1] || ent->velocity[2])
|
||||
{
|
||||
speed = VectorLength(ent->velocity);
|
||||
|
||||
VectorSubtract(ent->s.origin, other->s.origin, dir);
|
||||
dot = DotProduct(dir, ent->velocity);
|
||||
|
||||
if(dot > 0.7)
|
||||
{
|
||||
T_Damage (other, ent, ent, vec3_origin, ent->s.origin, vec3_origin,
|
||||
speed/10, speed/10, 0, MOD_DBALL_CRUSH);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==================
|
||||
// DBall_BallPain
|
||||
//==================
|
||||
void DBall_BallPain (edict_t *self, edict_t *other, float kick, int damage)
|
||||
{
|
||||
self->enemy = other;
|
||||
self->health = self->max_health;
|
||||
// if(other->classname)
|
||||
// gi.dprintf("hurt by %s -- %d\n", other->classname, self->health);
|
||||
}
|
||||
|
||||
void DBall_BallDie (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
|
||||
{
|
||||
// do the splash effect
|
||||
gi.WriteByte (svc_temp_entity);
|
||||
gi.WriteByte (TE_DBALL_GOAL);
|
||||
gi.WritePosition (self->s.origin);
|
||||
gi.multicast (self->s.origin, MULTICAST_PVS);
|
||||
|
||||
VectorClear(self->s.angles);
|
||||
VectorClear(self->velocity);
|
||||
VectorClear(self->avelocity);
|
||||
|
||||
// make it invisible and desolid until respawn time
|
||||
self->solid = SOLID_NOT;
|
||||
// self->s.modelindex = 0;
|
||||
self->think = DBall_BallRespawn;
|
||||
self->nextthink = level.time + 2;
|
||||
gi.linkentity(self);
|
||||
|
||||
}
|
||||
|
||||
void DBall_BallRespawn (edict_t *self)
|
||||
{
|
||||
edict_t *start;
|
||||
|
||||
// do the splash effect
|
||||
gi.WriteByte (svc_temp_entity);
|
||||
gi.WriteByte (TE_DBALL_GOAL);
|
||||
gi.WritePosition (self->s.origin);
|
||||
gi.multicast (self->s.origin, MULTICAST_PVS);
|
||||
|
||||
// move the ball and stop it
|
||||
start = PickBallStart(self);
|
||||
if(start)
|
||||
{
|
||||
VectorCopy(start->s.origin, self->s.origin);
|
||||
VectorCopy(start->s.origin, self->s.old_origin);
|
||||
}
|
||||
|
||||
VectorClear(self->s.angles);
|
||||
VectorClear(self->velocity);
|
||||
VectorClear(self->avelocity);
|
||||
|
||||
self->solid = SOLID_BBOX;
|
||||
self->s.modelindex = gi.modelindex ("models/objects/dball/tris.md2");
|
||||
self->s.event = EV_PLAYER_TELEPORT;
|
||||
self->groundentity = NULL;
|
||||
|
||||
// kill anything at the destination
|
||||
KillBox (self);
|
||||
|
||||
gi.linkentity (self);
|
||||
}
|
||||
|
||||
// ************************
|
||||
// SPEED CHANGES
|
||||
// ************************
|
||||
|
||||
#define DBALL_SPEED_ONEWAY 1
|
||||
|
||||
void DBall_SpeedTouch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
|
||||
{
|
||||
float dot;
|
||||
vec3_t vel;
|
||||
|
||||
if(other != dball_ball_entity)
|
||||
return;
|
||||
|
||||
if(self->timestamp >= level.time)
|
||||
return;
|
||||
|
||||
if(VectorLength(other->velocity) < 1)
|
||||
return;
|
||||
|
||||
if(self->spawnflags & DBALL_SPEED_ONEWAY)
|
||||
{
|
||||
VectorCopy (other->velocity, vel);
|
||||
VectorNormalize (vel);
|
||||
dot = DotProduct (vel, self->movedir);
|
||||
if(dot < 0.8)
|
||||
return;
|
||||
}
|
||||
|
||||
self->timestamp = level.time + self->delay;
|
||||
VectorScale (other->velocity, self->speed, other->velocity);
|
||||
}
|
||||
|
||||
// ************************
|
||||
// SPAWN FUNCTIONS
|
||||
// ************************
|
||||
|
||||
/*QUAKED dm_dball_ball (1 .5 .5) (-48 -48 -48) (48 48 48)
|
||||
Deathball Ball
|
||||
*/
|
||||
void SP_dm_dball_ball (edict_t *self)
|
||||
{
|
||||
if(!(deathmatch->value))
|
||||
{
|
||||
G_FreeEdict(self);
|
||||
return;
|
||||
}
|
||||
|
||||
if(gamerules && (gamerules->value != RDM_DEATHBALL))
|
||||
{
|
||||
G_FreeEdict(self);
|
||||
return;
|
||||
}
|
||||
|
||||
dball_ball_entity = self;
|
||||
// VectorCopy (self->s.origin, dball_ball_startpt);
|
||||
|
||||
self->s.modelindex = gi.modelindex ("models/objects/dball/tris.md2");
|
||||
VectorSet (self->mins, -32, -32, -32);
|
||||
VectorSet (self->maxs, 32, 32, 32);
|
||||
self->solid = SOLID_BBOX;
|
||||
self->movetype = MOVETYPE_NEWTOSS;
|
||||
self->clipmask = MASK_MONSTERSOLID;
|
||||
|
||||
self->takedamage = DAMAGE_YES;
|
||||
self->mass = 50;
|
||||
self->health = 50000;
|
||||
self->max_health = 50000;
|
||||
self->pain = DBall_BallPain;
|
||||
self->die = DBall_BallDie;
|
||||
self->touch = DBall_BallTouch;
|
||||
|
||||
gi.linkentity (self);
|
||||
}
|
||||
|
||||
/*QUAKED dm_dball_team1_start (1 .5 .5) (-16 -16 -24) (16 16 32)
|
||||
Deathball team 1 start point
|
||||
*/
|
||||
void SP_dm_dball_team1_start (edict_t *self)
|
||||
{
|
||||
if (!deathmatch->value)
|
||||
{
|
||||
G_FreeEdict (self);
|
||||
return;
|
||||
}
|
||||
if(gamerules && (gamerules->value != RDM_DEATHBALL))
|
||||
{
|
||||
G_FreeEdict(self);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*QUAKED dm_dball_team2_start (1 .5 .5) (-16 -16 -24) (16 16 32)
|
||||
Deathball team 2 start point
|
||||
*/
|
||||
void SP_dm_dball_team2_start (edict_t *self)
|
||||
{
|
||||
if (!deathmatch->value)
|
||||
{
|
||||
G_FreeEdict (self);
|
||||
return;
|
||||
}
|
||||
if(gamerules && (gamerules->value != RDM_DEATHBALL))
|
||||
{
|
||||
G_FreeEdict(self);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*QUAKED dm_dball_ball_start (1 .5 .5) (-48 -48 -48) (48 48 48)
|
||||
Deathball ball start point
|
||||
*/
|
||||
void SP_dm_dball_ball_start (edict_t *self)
|
||||
{
|
||||
if (!deathmatch->value)
|
||||
{
|
||||
G_FreeEdict (self);
|
||||
return;
|
||||
}
|
||||
if(gamerules && (gamerules->value != RDM_DEATHBALL))
|
||||
{
|
||||
G_FreeEdict(self);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*QUAKED dm_dball_speed_change (1 .5 .5) ? ONEWAY
|
||||
Deathball ball speed changing field.
|
||||
|
||||
speed: multiplier for speed (.5 = half, 2 = double, etc) (default = double)
|
||||
angle: used with ONEWAY so speed change is only one way.
|
||||
delay: time between speed changes (default: 0.2 sec)
|
||||
*/
|
||||
void SP_dm_dball_speed_change (edict_t *self)
|
||||
{
|
||||
if (!deathmatch->value)
|
||||
{
|
||||
G_FreeEdict (self);
|
||||
return;
|
||||
}
|
||||
if(gamerules && (gamerules->value != RDM_DEATHBALL))
|
||||
{
|
||||
G_FreeEdict(self);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!self->speed)
|
||||
self->speed = 2;
|
||||
|
||||
if(!self->delay)
|
||||
self->delay = 0.2;
|
||||
|
||||
self->touch = DBall_SpeedTouch;
|
||||
self->solid = SOLID_TRIGGER;
|
||||
self->movetype = MOVETYPE_NONE;
|
||||
self->svflags |= SVF_NOCLIENT;
|
||||
|
||||
if (!VectorCompare(self->s.angles, vec3_origin))
|
||||
G_SetMovedir (self->s.angles, self->movedir);
|
||||
else
|
||||
VectorSet (self->movedir, 1, 0, 0);
|
||||
|
||||
gi.setmodel (self, self->model);
|
||||
gi.linkentity (self);
|
||||
}
|
||||
|
||||
/*QUAKED dm_dball_goal (1 .5 .5) ? TEAM1 TEAM2
|
||||
Deathball goal
|
||||
|
||||
Team1/Team2 - beneficiary of this goal. when the ball enters this goal, the beneficiary team will score.
|
||||
|
||||
"wait": score to be given for this goal (default 10) player gets score+5.
|
||||
*/
|
||||
void SP_dm_dball_goal (edict_t *self)
|
||||
{
|
||||
if(!(deathmatch->value))
|
||||
{
|
||||
G_FreeEdict(self);
|
||||
return;
|
||||
}
|
||||
|
||||
if(gamerules && (gamerules->value != RDM_DEATHBALL))
|
||||
{
|
||||
G_FreeEdict(self);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!self->wait)
|
||||
self->wait = 10;
|
||||
|
||||
self->touch = DBall_GoalTouch;
|
||||
self->solid = SOLID_TRIGGER;
|
||||
self->movetype = MOVETYPE_NONE;
|
||||
self->svflags |= SVF_NOCLIENT;
|
||||
|
||||
if (!VectorCompare(self->s.angles, vec3_origin))
|
||||
G_SetMovedir (self->s.angles, self->movedir);
|
||||
|
||||
gi.setmodel (self, self->model);
|
||||
gi.linkentity (self);
|
||||
|
||||
}
|
333
dm_tag.c
Normal file
333
dm_tag.c
Normal file
|
@ -0,0 +1,333 @@
|
|||
// dm_tag
|
||||
// pmack
|
||||
// june 1998
|
||||
|
||||
#include "g_local.h"
|
||||
|
||||
extern edict_t *SelectFarthestDeathmatchSpawnPoint (void);
|
||||
extern void SelectSpawnPoint (edict_t *ent, vec3_t origin, vec3_t angles);
|
||||
|
||||
void SP_dm_tag_token (edict_t *self);
|
||||
|
||||
// ***********************
|
||||
// Tag Specific Stuff
|
||||
// ***********************
|
||||
|
||||
edict_t *tag_token;
|
||||
edict_t *tag_owner;
|
||||
int tag_count;
|
||||
|
||||
//=================
|
||||
//=================
|
||||
void Tag_PlayerDeath(edict_t *targ, edict_t *inflictor, edict_t *attacker)
|
||||
{
|
||||
// gi.dprintf("%s killed %s\n", attacker->classname, targ->classname);
|
||||
// gi.dprintf("%x killed %x\n", attacker, targ);
|
||||
|
||||
if(tag_token && targ && (targ == tag_owner))
|
||||
{
|
||||
// gi.dprintf("owner died/suicided. dropping\n");
|
||||
Tag_DropToken(targ, FindItem( "Tag Token" ));
|
||||
tag_owner = NULL;
|
||||
tag_count = 0;
|
||||
}
|
||||
// else
|
||||
// gi.dprintf("unrelated slaying\n");
|
||||
}
|
||||
|
||||
//=================
|
||||
//=================
|
||||
void Tag_KillItBonus (edict_t *self)
|
||||
{
|
||||
edict_t *armor;
|
||||
|
||||
// if the player is hurt, boost them up to max.
|
||||
if(self->health < self->max_health)
|
||||
{
|
||||
self->health += 200;
|
||||
if(self->health > self->max_health)
|
||||
self->health = self->max_health;
|
||||
}
|
||||
|
||||
// give the player a body armor
|
||||
armor = G_Spawn();
|
||||
armor->spawnflags |= DROPPED_ITEM;
|
||||
armor->item = FindItem("Body Armor");
|
||||
Touch_Item(armor, self, NULL, NULL);
|
||||
if(armor->inuse)
|
||||
G_FreeEdict(armor);
|
||||
}
|
||||
|
||||
//=================
|
||||
//=================
|
||||
void Tag_PlayerDisconnect (edict_t *self)
|
||||
{
|
||||
if(tag_token && self && (self == tag_owner))
|
||||
{
|
||||
// gi.dprintf("owner died/suicided. dropping\n");
|
||||
Tag_DropToken(self, FindItem( "Tag Token" ));
|
||||
tag_owner = NULL;
|
||||
tag_count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//=================
|
||||
//=================
|
||||
void Tag_Score (edict_t *attacker, edict_t *victim, int scoreChange)
|
||||
{
|
||||
gitem_t *quad;
|
||||
int mod;
|
||||
|
||||
mod = meansOfDeath & ~MOD_FRIENDLY_FIRE;
|
||||
|
||||
// gi.dprintf("%s killed %s\n", attacker->classname, victim->classname);
|
||||
// gi.dprintf("%x killed %x\n", attacker, victim);
|
||||
if(tag_token && tag_owner)
|
||||
{
|
||||
// owner killed somone else
|
||||
if((scoreChange > 0) && tag_owner == attacker)
|
||||
{
|
||||
scoreChange = 3;
|
||||
tag_count++;
|
||||
// gi.dprintf("tag total: %d\n", tag_count);
|
||||
if(tag_count == 5)
|
||||
{
|
||||
// gi.dprintf("going to quad\n");
|
||||
quad = FindItem ("Quad Damage");
|
||||
attacker->client->pers.inventory[ITEM_INDEX(quad)]++;
|
||||
quad->use (attacker, quad);
|
||||
tag_count = 0;
|
||||
}
|
||||
}
|
||||
// owner got killed. 5 points and switch owners
|
||||
else if(tag_owner == victim && tag_owner != attacker)
|
||||
{
|
||||
// gi.dprintf("owner killed by another player.\n");
|
||||
scoreChange = 5;
|
||||
if ((mod == MOD_HUNTER_SPHERE) || (mod == MOD_DOPPLE_EXPLODE) ||
|
||||
(mod == MOD_DOPPLE_VENGEANCE) || (mod == MOD_DOPPLE_HUNTER) ||
|
||||
(attacker->health <= 0))
|
||||
{
|
||||
Tag_DropToken(tag_owner, FindItem( "Tag Token" ));
|
||||
tag_owner = NULL;
|
||||
tag_count = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
Tag_KillItBonus(attacker);
|
||||
tag_owner = attacker;
|
||||
tag_count = 0;
|
||||
}
|
||||
}
|
||||
// else
|
||||
// gi.dprintf("unaffected slaying\n");
|
||||
}
|
||||
// else
|
||||
// gi.dprintf("no tag token?\n");
|
||||
|
||||
attacker->client->resp.score += scoreChange;
|
||||
}
|
||||
|
||||
//=================
|
||||
//=================
|
||||
qboolean Tag_PickupToken (edict_t *ent, edict_t *other)
|
||||
{
|
||||
if(gamerules && (gamerules->value != 2))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// gi.dprintf("tag token picked up by %x\n", other);
|
||||
// sanity checking is good.
|
||||
if(tag_token != ent)
|
||||
tag_token = ent;
|
||||
|
||||
other->client->pers.inventory[ITEM_INDEX(ent->item)]++;
|
||||
|
||||
tag_owner = other;
|
||||
tag_count = 0;
|
||||
|
||||
Tag_KillItBonus (other);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//=================
|
||||
//=================
|
||||
void Tag_Respawn (edict_t *ent)
|
||||
{
|
||||
edict_t *spot;
|
||||
|
||||
spot = SelectFarthestDeathmatchSpawnPoint();
|
||||
if(spot == NULL)
|
||||
{
|
||||
// gi.dprintf("No open spawn point, waiting...\n");
|
||||
ent->nextthink = level.time + 1;
|
||||
return;
|
||||
}
|
||||
|
||||
// gi.dprintf("Relocating\n");
|
||||
|
||||
VectorCopy(spot->s.origin, ent->s.origin);
|
||||
gi.linkentity(ent);
|
||||
}
|
||||
|
||||
//=================
|
||||
//=================
|
||||
void Tag_MakeTouchable (edict_t *ent)
|
||||
{
|
||||
ent->touch = Touch_Item;
|
||||
|
||||
tag_token->think = Tag_Respawn;
|
||||
|
||||
// check here to see if it's in lava or slime. if so, do a respawn sooner
|
||||
if(gi.pointcontents(ent->s.origin) & (CONTENTS_LAVA|CONTENTS_SLIME))
|
||||
{
|
||||
// gi.dprintf("spawned in slime or lava. quick relocate\n");
|
||||
tag_token->nextthink = level.time + 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
// gi.dprintf("spawned in the clear. regular relocate\n");
|
||||
tag_token->nextthink = level.time + 30;
|
||||
}
|
||||
}
|
||||
|
||||
//=================
|
||||
//=================
|
||||
void Tag_DropToken (edict_t *ent, gitem_t *item)
|
||||
{
|
||||
trace_t trace;
|
||||
vec3_t forward, right;
|
||||
vec3_t offset;
|
||||
|
||||
// if(ent->client)
|
||||
// gi.dprintf("%s dropped the tag token\n", ent->client->pers.netname);
|
||||
// else
|
||||
// gi.dprintf("non-client dropped the tag token (%s)\n", ent->classname);
|
||||
|
||||
// reset the score count for next player
|
||||
tag_count = 0;
|
||||
tag_owner = NULL;
|
||||
|
||||
tag_token = G_Spawn();
|
||||
|
||||
tag_token->classname = item->classname;
|
||||
tag_token->item = item;
|
||||
tag_token->spawnflags = DROPPED_ITEM;
|
||||
tag_token->s.effects = EF_ROTATE | EF_TAGTRAIL;
|
||||
tag_token->s.renderfx = RF_GLOW;
|
||||
VectorSet (tag_token->mins, -15, -15, -15);
|
||||
VectorSet (tag_token->maxs, 15, 15, 15);
|
||||
gi.setmodel (tag_token, tag_token->item->world_model);
|
||||
tag_token->solid = SOLID_TRIGGER;
|
||||
tag_token->movetype = MOVETYPE_TOSS;
|
||||
tag_token->touch = NULL;
|
||||
tag_token->owner = ent;
|
||||
|
||||
AngleVectors (ent->client->v_angle, forward, right, NULL);
|
||||
VectorSet(offset, 24, 0, -16);
|
||||
G_ProjectSource (ent->s.origin, offset, forward, right, tag_token->s.origin);
|
||||
trace = gi.trace (ent->s.origin, tag_token->mins, tag_token->maxs,
|
||||
tag_token->s.origin, ent, CONTENTS_SOLID);
|
||||
VectorCopy (trace.endpos, tag_token->s.origin);
|
||||
|
||||
VectorScale (forward, 100, tag_token->velocity);
|
||||
tag_token->velocity[2] = 300;
|
||||
|
||||
tag_token->think = Tag_MakeTouchable;
|
||||
tag_token->nextthink = level.time + 1;
|
||||
|
||||
gi.linkentity (tag_token);
|
||||
|
||||
// tag_token = Drop_Item (ent, item);
|
||||
ent->client->pers.inventory[ITEM_INDEX(item)]--;
|
||||
ValidateSelectedItem (ent);
|
||||
}
|
||||
|
||||
//=================
|
||||
//=================
|
||||
void Tag_PlayerEffects (edict_t *ent)
|
||||
{
|
||||
if(ent == tag_owner)
|
||||
ent->s.effects |= EF_TAGTRAIL;
|
||||
}
|
||||
|
||||
//=================
|
||||
//=================
|
||||
void Tag_DogTag (edict_t *ent, edict_t *killer, char **pic)
|
||||
{
|
||||
if(ent == tag_owner)
|
||||
(*pic)="tag3";
|
||||
}
|
||||
|
||||
//=================
|
||||
// Tag_ChangeDamage - damage done that does not involve the tag owner
|
||||
// is at 75% original to encourage folks to go after the tag owner.
|
||||
//=================
|
||||
int Tag_ChangeDamage (edict_t *targ, edict_t *attacker, int damage, int mod)
|
||||
{
|
||||
if((targ != tag_owner) && (attacker != tag_owner))
|
||||
return (damage * 3 / 4);
|
||||
|
||||
return damage;
|
||||
}
|
||||
|
||||
//=================
|
||||
//=================
|
||||
void Tag_GameInit (void)
|
||||
{
|
||||
tag_token = NULL;
|
||||
tag_owner = NULL;
|
||||
tag_count = 0;
|
||||
}
|
||||
|
||||
//=================
|
||||
//=================
|
||||
void Tag_PostInitSetup (void)
|
||||
{
|
||||
edict_t *e;
|
||||
vec3_t origin, angles;
|
||||
|
||||
// automatic spawning of tag token if one is not present on map.
|
||||
e = G_Find (NULL, FOFS(classname), "dm_tag_token");
|
||||
if(e == NULL)
|
||||
{
|
||||
e = G_Spawn();
|
||||
e->classname = "dm_tag_token";
|
||||
|
||||
SelectSpawnPoint (e, origin, angles);
|
||||
VectorCopy(origin, e->s.origin);
|
||||
VectorCopy(origin, e->s.old_origin);
|
||||
VectorCopy(angles, e->s.angles);
|
||||
SP_dm_tag_token (e);
|
||||
}
|
||||
}
|
||||
|
||||
/*QUAKED dm_tag_token (.3 .3 1) (-16 -16 -16) (16 16 16)
|
||||
The tag token for deathmatch tag games.
|
||||
*/
|
||||
void SP_dm_tag_token (edict_t *self)
|
||||
{
|
||||
if(!(deathmatch->value))
|
||||
{
|
||||
G_FreeEdict(self);
|
||||
return;
|
||||
}
|
||||
|
||||
if(gamerules && (gamerules->value != 2))
|
||||
{
|
||||
G_FreeEdict(self);
|
||||
return;
|
||||
}
|
||||
|
||||
// store the tag token edict pointer for later use.
|
||||
tag_token = self;
|
||||
tag_count = 0;
|
||||
|
||||
self->classname = "dm_tag_token";
|
||||
self->model = "models/items/tagtoken/tris.md2";
|
||||
self->count = 1;
|
||||
SpawnItem (self, FindItem ("Tag Token"));
|
||||
}
|
||||
|
156
g_chase.c
Normal file
156
g_chase.c
Normal file
|
@ -0,0 +1,156 @@
|
|||
#include "g_local.h"
|
||||
|
||||
void UpdateChaseCam(edict_t *ent)
|
||||
{
|
||||
vec3_t o, ownerv, goal;
|
||||
edict_t *targ;
|
||||
vec3_t forward, right;
|
||||
trace_t trace;
|
||||
int i;
|
||||
vec3_t oldgoal;
|
||||
vec3_t angles;
|
||||
|
||||
// is our chase target gone?
|
||||
if (!ent->client->chase_target->inuse
|
||||
|| ent->client->chase_target->client->resp.spectator) {
|
||||
edict_t *old = ent->client->chase_target;
|
||||
ChaseNext(ent);
|
||||
if (ent->client->chase_target == old) {
|
||||
ent->client->chase_target = NULL;
|
||||
ent->client->ps.pmove.pm_flags &= ~PMF_NO_PREDICTION;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
targ = ent->client->chase_target;
|
||||
|
||||
VectorCopy(targ->s.origin, ownerv);
|
||||
VectorCopy(ent->s.origin, oldgoal);
|
||||
|
||||
ownerv[2] += targ->viewheight;
|
||||
|
||||
VectorCopy(targ->client->v_angle, angles);
|
||||
if (angles[PITCH] > 56)
|
||||
angles[PITCH] = 56;
|
||||
AngleVectors (angles, forward, right, NULL);
|
||||
VectorNormalize(forward);
|
||||
VectorMA(ownerv, -30, forward, o);
|
||||
|
||||
if (o[2] < targ->s.origin[2] + 20)
|
||||
o[2] = targ->s.origin[2] + 20;
|
||||
|
||||
// jump animation lifts
|
||||
if (!targ->groundentity)
|
||||
o[2] += 16;
|
||||
|
||||
trace = gi.trace(ownerv, vec3_origin, vec3_origin, o, targ, MASK_SOLID);
|
||||
|
||||
VectorCopy(trace.endpos, goal);
|
||||
|
||||
VectorMA(goal, 2, forward, goal);
|
||||
|
||||
// pad for floors and ceilings
|
||||
VectorCopy(goal, o);
|
||||
o[2] += 6;
|
||||
trace = gi.trace(goal, vec3_origin, vec3_origin, o, targ, MASK_SOLID);
|
||||
if (trace.fraction < 1) {
|
||||
VectorCopy(trace.endpos, goal);
|
||||
goal[2] -= 6;
|
||||
}
|
||||
|
||||
VectorCopy(goal, o);
|
||||
o[2] -= 6;
|
||||
trace = gi.trace(goal, vec3_origin, vec3_origin, o, targ, MASK_SOLID);
|
||||
if (trace.fraction < 1) {
|
||||
VectorCopy(trace.endpos, goal);
|
||||
goal[2] += 6;
|
||||
}
|
||||
|
||||
if (targ->deadflag)
|
||||
ent->client->ps.pmove.pm_type = PM_DEAD;
|
||||
else
|
||||
ent->client->ps.pmove.pm_type = PM_FREEZE;
|
||||
|
||||
VectorCopy(goal, ent->s.origin);
|
||||
for (i=0 ; i<3 ; i++)
|
||||
ent->client->ps.pmove.delta_angles[i] = ANGLE2SHORT(targ->client->v_angle[i] - ent->client->resp.cmd_angles[i]);
|
||||
|
||||
if (targ->deadflag) {
|
||||
ent->client->ps.viewangles[ROLL] = 40;
|
||||
ent->client->ps.viewangles[PITCH] = -15;
|
||||
ent->client->ps.viewangles[YAW] = targ->client->killer_yaw;
|
||||
} else {
|
||||
VectorCopy(targ->client->v_angle, ent->client->ps.viewangles);
|
||||
VectorCopy(targ->client->v_angle, ent->client->v_angle);
|
||||
}
|
||||
|
||||
ent->viewheight = 0;
|
||||
ent->client->ps.pmove.pm_flags |= PMF_NO_PREDICTION;
|
||||
gi.linkentity(ent);
|
||||
}
|
||||
|
||||
void ChaseNext(edict_t *ent)
|
||||
{
|
||||
int i;
|
||||
edict_t *e;
|
||||
|
||||
if (!ent->client->chase_target)
|
||||
return;
|
||||
|
||||
i = ent->client->chase_target - g_edicts;
|
||||
do {
|
||||
i++;
|
||||
if (i > maxclients->value)
|
||||
i = 1;
|
||||
e = g_edicts + i;
|
||||
if (!e->inuse)
|
||||
continue;
|
||||
if (!e->client->resp.spectator)
|
||||
break;
|
||||
} while (e != ent->client->chase_target);
|
||||
|
||||
ent->client->chase_target = e;
|
||||
ent->client->update_chase = true;
|
||||
}
|
||||
|
||||
void ChasePrev(edict_t *ent)
|
||||
{
|
||||
int i;
|
||||
edict_t *e;
|
||||
|
||||
if (!ent->client->chase_target)
|
||||
return;
|
||||
|
||||
i = ent->client->chase_target - g_edicts;
|
||||
do {
|
||||
i--;
|
||||
if (i < 1)
|
||||
i = maxclients->value;
|
||||
e = g_edicts + i;
|
||||
if (!e->inuse)
|
||||
continue;
|
||||
if (!e->client->resp.spectator)
|
||||
break;
|
||||
} while (e != ent->client->chase_target);
|
||||
|
||||
ent->client->chase_target = e;
|
||||
ent->client->update_chase = true;
|
||||
}
|
||||
|
||||
void GetChaseTarget(edict_t *ent)
|
||||
{
|
||||
int i;
|
||||
edict_t *other;
|
||||
|
||||
for (i = 1; i <= maxclients->value; i++) {
|
||||
other = g_edicts + i;
|
||||
if (other->inuse && !other->client->resp.spectator) {
|
||||
ent->client->chase_target = other;
|
||||
ent->client->update_chase = true;
|
||||
UpdateChaseCam(ent);
|
||||
return;
|
||||
}
|
||||
}
|
||||
gi.centerprintf(ent, "No other players to chase.");
|
||||
}
|
||||
|
947
g_combat.c
Normal file
947
g_combat.c
Normal file
|
@ -0,0 +1,947 @@
|
|||
// g_combat.c
|
||||
|
||||
#include "g_local.h"
|
||||
|
||||
void M_SetEffects (edict_t *self);
|
||||
|
||||
/*
|
||||
ROGUE
|
||||
clean up heal targets for medic
|
||||
*/
|
||||
void cleanupHealTarget (edict_t *ent)
|
||||
{
|
||||
ent->monsterinfo.healer = NULL;
|
||||
ent->takedamage = DAMAGE_YES;
|
||||
ent->monsterinfo.aiflags &= ~AI_RESURRECTING;
|
||||
M_SetEffects (ent);
|
||||
}
|
||||
/*
|
||||
============
|
||||
CanDamage
|
||||
|
||||
Returns true if the inflictor can directly damage the target. Used for
|
||||
explosions and melee attacks.
|
||||
============
|
||||
*/
|
||||
qboolean CanDamage (edict_t *targ, edict_t *inflictor)
|
||||
{
|
||||
vec3_t dest;
|
||||
trace_t trace;
|
||||
|
||||
// bmodels need special checking because their origin is 0,0,0
|
||||
if (targ->movetype == MOVETYPE_PUSH)
|
||||
{
|
||||
VectorAdd (targ->absmin, targ->absmax, dest);
|
||||
VectorScale (dest, 0.5, dest);
|
||||
trace = gi.trace (inflictor->s.origin, vec3_origin, vec3_origin, dest, inflictor, MASK_SOLID);
|
||||
if (trace.fraction == 1.0)
|
||||
return true;
|
||||
if (trace.ent == targ)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
trace = gi.trace (inflictor->s.origin, vec3_origin, vec3_origin, targ->s.origin, inflictor, MASK_SOLID);
|
||||
if (trace.fraction == 1.0)
|
||||
return true;
|
||||
|
||||
VectorCopy (targ->s.origin, dest);
|
||||
dest[0] += 15.0;
|
||||
dest[1] += 15.0;
|
||||
trace = gi.trace (inflictor->s.origin, vec3_origin, vec3_origin, dest, inflictor, MASK_SOLID);
|
||||
if (trace.fraction == 1.0)
|
||||
return true;
|
||||
|
||||
VectorCopy (targ->s.origin, dest);
|
||||
dest[0] += 15.0;
|
||||
dest[1] -= 15.0;
|
||||
trace = gi.trace (inflictor->s.origin, vec3_origin, vec3_origin, dest, inflictor, MASK_SOLID);
|
||||
if (trace.fraction == 1.0)
|
||||
return true;
|
||||
|
||||
VectorCopy (targ->s.origin, dest);
|
||||
dest[0] -= 15.0;
|
||||
dest[1] += 15.0;
|
||||
trace = gi.trace (inflictor->s.origin, vec3_origin, vec3_origin, dest, inflictor, MASK_SOLID);
|
||||
if (trace.fraction == 1.0)
|
||||
return true;
|
||||
|
||||
VectorCopy (targ->s.origin, dest);
|
||||
dest[0] -= 15.0;
|
||||
dest[1] -= 15.0;
|
||||
trace = gi.trace (inflictor->s.origin, vec3_origin, vec3_origin, dest, inflictor, MASK_SOLID);
|
||||
if (trace.fraction == 1.0)
|
||||
return true;
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
============
|
||||
Killed
|
||||
============
|
||||
*/
|
||||
void Killed (edict_t *targ, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
|
||||
{
|
||||
if (targ->health < -999)
|
||||
targ->health = -999;
|
||||
|
||||
if (targ->monsterinfo.aiflags & AI_MEDIC)
|
||||
{
|
||||
if (targ->enemy) // god, I hope so
|
||||
{
|
||||
cleanupHealTarget (targ->enemy);
|
||||
}
|
||||
|
||||
// clean up self
|
||||
targ->monsterinfo.aiflags &= ~AI_MEDIC;
|
||||
targ->enemy = attacker;
|
||||
}
|
||||
else
|
||||
{
|
||||
targ->enemy = attacker;
|
||||
}
|
||||
|
||||
if ((targ->svflags & SVF_MONSTER) && (targ->deadflag != DEAD_DEAD))
|
||||
{
|
||||
// targ->svflags |= SVF_DEADMONSTER; // now treat as a different content type
|
||||
//ROGUE - free up slot for spawned monster if it's spawned
|
||||
if (targ->monsterinfo.aiflags & AI_SPAWNED_CARRIER)
|
||||
{
|
||||
if (targ->monsterinfo.commander && targ->monsterinfo.commander->inuse &&
|
||||
!strcmp(targ->monsterinfo.commander->classname, "monster_carrier"))
|
||||
{
|
||||
targ->monsterinfo.commander->monsterinfo.monster_slots++;
|
||||
// if ((g_showlogic) && (g_showlogic->value))
|
||||
// gi.dprintf ("g_combat: freeing up carrier slot - %d left\n", targ->monsterinfo.commander->monsterinfo.monster_slots);
|
||||
}
|
||||
}
|
||||
if (targ->monsterinfo.aiflags & AI_SPAWNED_MEDIC_C)
|
||||
{
|
||||
if (targ->monsterinfo.commander)
|
||||
{
|
||||
if (targ->monsterinfo.commander->inuse && !strcmp(targ->monsterinfo.commander->classname, "monster_medic_commander"))
|
||||
{
|
||||
targ->monsterinfo.commander->monsterinfo.monster_slots++;
|
||||
// if ((g_showlogic) && (g_showlogic->value))
|
||||
// gi.dprintf ("g_combat: freeing up medic slot - %d left\n", targ->monsterinfo.commander->monsterinfo.monster_slots);
|
||||
}
|
||||
// else
|
||||
// if ((g_showlogic) && (g_showlogic->value))
|
||||
// gi.dprintf ("my commander is dead! he's a %s\n", targ->monsterinfo.commander->classname);
|
||||
}
|
||||
// else if ((g_showlogic) && (g_showlogic->value))
|
||||
// gi.dprintf ("My commander is GONE\n");
|
||||
|
||||
}
|
||||
if (targ->monsterinfo.aiflags & AI_SPAWNED_WIDOW)
|
||||
{
|
||||
// need to check this because we can have variable numbers of coop players
|
||||
if (targ->monsterinfo.commander && targ->monsterinfo.commander->inuse &&
|
||||
!strncmp(targ->monsterinfo.commander->classname, "monster_widow", 13))
|
||||
{
|
||||
if (targ->monsterinfo.commander->monsterinfo.monster_used > 0)
|
||||
targ->monsterinfo.commander->monsterinfo.monster_used--;
|
||||
// if ((g_showlogic) && (g_showlogic->value))
|
||||
// gi.dprintf ("g_combat: freeing up black widow slot - %d used\n", targ->monsterinfo.commander->monsterinfo.monster_used);
|
||||
}
|
||||
}
|
||||
//rogue
|
||||
if ((!(targ->monsterinfo.aiflags & AI_GOOD_GUY)) && (!(targ->monsterinfo.aiflags & AI_DO_NOT_COUNT)))
|
||||
{
|
||||
level.killed_monsters++;
|
||||
if (coop->value && attacker->client)
|
||||
attacker->client->resp.score++;
|
||||
// medics won't heal monsters that they kill themselves
|
||||
// PMM - now they will
|
||||
// if (strcmp(attacker->classname, "monster_medic") == 0)
|
||||
// targ->owner = attacker;
|
||||
}
|
||||
}
|
||||
|
||||
if (targ->movetype == MOVETYPE_PUSH || targ->movetype == MOVETYPE_STOP || targ->movetype == MOVETYPE_NONE)
|
||||
{ // doors, triggers, etc
|
||||
targ->die (targ, inflictor, attacker, damage, point);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((targ->svflags & SVF_MONSTER) && (targ->deadflag != DEAD_DEAD))
|
||||
{
|
||||
targ->touch = NULL;
|
||||
monster_death_use (targ);
|
||||
}
|
||||
|
||||
targ->die (targ, inflictor, attacker, damage, point);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
SpawnDamage
|
||||
================
|
||||
*/
|
||||
void SpawnDamage (int type, vec3_t origin, vec3_t normal, int damage)
|
||||
{
|
||||
if (damage > 255)
|
||||
damage = 255;
|
||||
gi.WriteByte (svc_temp_entity);
|
||||
gi.WriteByte (type);
|
||||
// gi.WriteByte (damage);
|
||||
gi.WritePosition (origin);
|
||||
gi.WriteDir (normal);
|
||||
gi.multicast (origin, MULTICAST_PVS);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
============
|
||||
T_Damage
|
||||
|
||||
targ entity that is being damaged
|
||||
inflictor entity that is causing the damage
|
||||
attacker entity that caused the inflictor to damage targ
|
||||
example: targ=monster, inflictor=rocket, attacker=player
|
||||
|
||||
dir direction of the attack
|
||||
point point at which the damage is being inflicted
|
||||
normal normal vector from that point
|
||||
damage amount of damage being inflicted
|
||||
knockback force to be applied against targ as a result of the damage
|
||||
|
||||
dflags these flags are used to control how T_Damage works
|
||||
DAMAGE_RADIUS damage was indirect (from a nearby explosion)
|
||||
DAMAGE_NO_ARMOR armor does not protect from this damage
|
||||
DAMAGE_ENERGY damage is from an energy based weapon
|
||||
DAMAGE_NO_KNOCKBACK do not affect velocity, just view angles
|
||||
DAMAGE_BULLET damage is from a bullet (used for ricochets)
|
||||
DAMAGE_NO_PROTECTION kills godmode, armor, everything
|
||||
============
|
||||
*/
|
||||
static int CheckPowerArmor (edict_t *ent, vec3_t point, vec3_t normal, int damage, int dflags)
|
||||
{
|
||||
gclient_t *client;
|
||||
int save;
|
||||
int power_armor_type;
|
||||
int index;
|
||||
int damagePerCell;
|
||||
int pa_te_type;
|
||||
int power;
|
||||
int power_used;
|
||||
|
||||
if (!damage)
|
||||
return 0;
|
||||
|
||||
client = ent->client;
|
||||
|
||||
if (dflags & (DAMAGE_NO_ARMOR | DAMAGE_NO_POWER_ARMOR)) // PGM
|
||||
return 0;
|
||||
|
||||
if (client)
|
||||
{
|
||||
power_armor_type = PowerArmorType (ent);
|
||||
if (power_armor_type != POWER_ARMOR_NONE)
|
||||
{
|
||||
index = ITEM_INDEX(FindItem("Cells"));
|
||||
power = client->pers.inventory[index];
|
||||
}
|
||||
}
|
||||
else if (ent->svflags & SVF_MONSTER)
|
||||
{
|
||||
power_armor_type = ent->monsterinfo.power_armor_type;
|
||||
power = ent->monsterinfo.power_armor_power;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
|
||||
if (power_armor_type == POWER_ARMOR_NONE)
|
||||
return 0;
|
||||
if (!power)
|
||||
return 0;
|
||||
|
||||
if (power_armor_type == POWER_ARMOR_SCREEN)
|
||||
{
|
||||
vec3_t vec;
|
||||
float dot;
|
||||
vec3_t forward;
|
||||
|
||||
// only works if damage point is in front
|
||||
AngleVectors (ent->s.angles, forward, NULL, NULL);
|
||||
VectorSubtract (point, ent->s.origin, vec);
|
||||
VectorNormalize (vec);
|
||||
dot = DotProduct (vec, forward);
|
||||
if (dot <= 0.3)
|
||||
return 0;
|
||||
|
||||
damagePerCell = 1;
|
||||
pa_te_type = TE_SCREEN_SPARKS;
|
||||
damage = damage / 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
damagePerCell = 2;
|
||||
pa_te_type = TE_SHIELD_SPARKS;
|
||||
damage = (2 * damage) / 3;
|
||||
}
|
||||
|
||||
// etf rifle
|
||||
if (dflags & DAMAGE_NO_REG_ARMOR)
|
||||
save = (power * damagePerCell) / 2;
|
||||
else
|
||||
save = power * damagePerCell;
|
||||
|
||||
if (!save)
|
||||
return 0;
|
||||
if (save > damage)
|
||||
save = damage;
|
||||
|
||||
SpawnDamage (pa_te_type, point, normal, save);
|
||||
ent->powerarmor_time = level.time + 0.2;
|
||||
|
||||
if (dflags & DAMAGE_NO_REG_ARMOR)
|
||||
power_used = (save / damagePerCell) * 2;
|
||||
else
|
||||
power_used = save / damagePerCell;
|
||||
|
||||
if (client)
|
||||
client->pers.inventory[index] -= power_used;
|
||||
else
|
||||
ent->monsterinfo.power_armor_power -= power_used;
|
||||
return save;
|
||||
}
|
||||
|
||||
static int CheckArmor (edict_t *ent, vec3_t point, vec3_t normal, int damage, int te_sparks, int dflags)
|
||||
{
|
||||
gclient_t *client;
|
||||
int save;
|
||||
int index;
|
||||
gitem_t *armor;
|
||||
|
||||
if (!damage)
|
||||
return 0;
|
||||
|
||||
client = ent->client;
|
||||
|
||||
if (!client)
|
||||
return 0;
|
||||
|
||||
// ROGUE - added DAMAGE_NO_REG_ARMOR for atf rifle
|
||||
if (dflags & (DAMAGE_NO_ARMOR | DAMAGE_NO_REG_ARMOR))
|
||||
return 0;
|
||||
|
||||
index = ArmorIndex (ent);
|
||||
if (!index)
|
||||
return 0;
|
||||
|
||||
armor = GetItemByIndex (index);
|
||||
|
||||
if (dflags & DAMAGE_ENERGY)
|
||||
save = ceil(((gitem_armor_t *)armor->info)->energy_protection*damage);
|
||||
else
|
||||
save = ceil(((gitem_armor_t *)armor->info)->normal_protection*damage);
|
||||
if (save >= client->pers.inventory[index])
|
||||
save = client->pers.inventory[index];
|
||||
|
||||
if (!save)
|
||||
return 0;
|
||||
|
||||
client->pers.inventory[index] -= save;
|
||||
SpawnDamage (te_sparks, point, normal, save);
|
||||
|
||||
return save;
|
||||
}
|
||||
|
||||
void M_ReactToDamage (edict_t *targ, edict_t *attacker, edict_t *inflictor)
|
||||
{
|
||||
// pmm
|
||||
qboolean new_tesla;
|
||||
|
||||
if (!(attacker->client) && !(attacker->svflags & SVF_MONSTER))
|
||||
return;
|
||||
|
||||
//=======
|
||||
//ROGUE
|
||||
// logic for tesla - if you are hit by a tesla, and can't see who you should be mad at (attacker)
|
||||
// attack the tesla
|
||||
// also, target the tesla if it's a "new" tesla
|
||||
if ((inflictor) && (!strcmp(inflictor->classname, "tesla")))
|
||||
{
|
||||
new_tesla = MarkTeslaArea(targ, inflictor);
|
||||
if (new_tesla)
|
||||
TargetTesla (targ, inflictor);
|
||||
return;
|
||||
// FIXME - just ignore teslas when you're TARGET_ANGER or MEDIC
|
||||
/* if (!(targ->enemy && (targ->monsterinfo.aiflags & (AI_TARGET_ANGER|AI_MEDIC))))
|
||||
{
|
||||
// FIXME - coop issues?
|
||||
if ((!targ->enemy) || (!visible(targ, targ->enemy)))
|
||||
{
|
||||
gi.dprintf ("can't see player, switching to tesla\n");
|
||||
TargetTesla (targ, inflictor);
|
||||
return;
|
||||
}
|
||||
gi.dprintf ("can see player, ignoring tesla\n");
|
||||
}
|
||||
else if ((g_showlogic) && (g_showlogic->value))
|
||||
gi.dprintf ("no enemy, or I'm doing other, more important things, than worrying about a damned tesla!\n");
|
||||
*/
|
||||
}
|
||||
//ROGUE
|
||||
//=======
|
||||
|
||||
if (attacker == targ || attacker == targ->enemy)
|
||||
return;
|
||||
|
||||
// if we are a good guy monster and our attacker is a player
|
||||
// or another good guy, do not get mad at them
|
||||
if (targ->monsterinfo.aiflags & AI_GOOD_GUY)
|
||||
{
|
||||
if (attacker->client || (attacker->monsterinfo.aiflags & AI_GOOD_GUY))
|
||||
return;
|
||||
}
|
||||
|
||||
//PGM
|
||||
// if we're currently mad at something a target_anger made us mad at, ignore
|
||||
// damage
|
||||
if (targ->enemy && targ->monsterinfo.aiflags & AI_TARGET_ANGER)
|
||||
{
|
||||
float percentHealth;
|
||||
|
||||
// make sure whatever we were pissed at is still around.
|
||||
if(targ->enemy->inuse)
|
||||
{
|
||||
percentHealth = (float)(targ->health) / (float)(targ->max_health);
|
||||
if( targ->enemy->inuse && percentHealth > 0.33)
|
||||
return;
|
||||
}
|
||||
|
||||
// remove the target anger flag
|
||||
targ->monsterinfo.aiflags &= ~AI_TARGET_ANGER;
|
||||
}
|
||||
//PGM
|
||||
|
||||
// PMM
|
||||
// if we're healing someone, do like above and try to stay with them
|
||||
if ((targ->enemy) && (targ->monsterinfo.aiflags & AI_MEDIC))
|
||||
{
|
||||
float percentHealth;
|
||||
|
||||
percentHealth = (float)(targ->health) / (float)(targ->max_health);
|
||||
// ignore it some of the time
|
||||
if( targ->enemy->inuse && percentHealth > 0.25)
|
||||
return;
|
||||
|
||||
// remove the medic flag
|
||||
targ->monsterinfo.aiflags &= ~AI_MEDIC;
|
||||
cleanupHealTarget (targ->enemy);
|
||||
}
|
||||
// PMM
|
||||
|
||||
// we now know that we are not both good guys
|
||||
|
||||
// if attacker is a client, get mad at them because he's good and we're not
|
||||
if (attacker->client)
|
||||
{
|
||||
targ->monsterinfo.aiflags &= ~AI_SOUND_TARGET;
|
||||
|
||||
// this can only happen in coop (both new and old enemies are clients)
|
||||
// only switch if can't see the current enemy
|
||||
if (targ->enemy && targ->enemy->client)
|
||||
{
|
||||
if (visible(targ, targ->enemy))
|
||||
{
|
||||
targ->oldenemy = attacker;
|
||||
return;
|
||||
}
|
||||
targ->oldenemy = targ->enemy;
|
||||
}
|
||||
targ->enemy = attacker;
|
||||
if (!(targ->monsterinfo.aiflags & AI_DUCKED))
|
||||
FoundTarget (targ);
|
||||
return;
|
||||
}
|
||||
|
||||
// it's the same base (walk/swim/fly) type and a different classname and it's not a tank
|
||||
// (they spray too much), get mad at them
|
||||
// PMM
|
||||
// added medics to this
|
||||
// FIXME -
|
||||
// this really should be turned into an AI flag marking appropriate monsters as "don't shoot me"
|
||||
// this also leads to the problem of tanks and medics being able to, at will, kill monsters with
|
||||
// no chance of retaliation. My vote is to make those monsters who are designed as "don't shoot me"
|
||||
// such that they also ignore being shot by monsters as well
|
||||
/*
|
||||
if (((targ->flags & (FL_FLY|FL_SWIM)) == (attacker->flags & (FL_FLY|FL_SWIM))) &&
|
||||
(strcmp (targ->classname, attacker->classname) != 0) &&
|
||||
(strcmp(attacker->classname, "monster_tank") != 0) &&
|
||||
(strcmp(attacker->classname, "monster_supertank") != 0) &&
|
||||
(strcmp(attacker->classname, "monster_makron") != 0) &&
|
||||
(strcmp(attacker->classname, "monster_jorg") != 0) &&
|
||||
(strcmp(attacker->classname, "monster_carrier") != 0) &&
|
||||
(strncmp(attacker->classname, "monster_medic", 12) != 0) ) // this should get medics & medic_commanders
|
||||
*/
|
||||
if (((targ->flags & (FL_FLY|FL_SWIM)) == (attacker->flags & (FL_FLY|FL_SWIM))) &&
|
||||
(strcmp (targ->classname, attacker->classname) != 0) &&
|
||||
!(attacker->monsterinfo.aiflags & AI_IGNORE_SHOTS) &&
|
||||
!(targ->monsterinfo.aiflags & AI_IGNORE_SHOTS) )
|
||||
{
|
||||
if (targ->enemy && targ->enemy->client)
|
||||
targ->oldenemy = targ->enemy;
|
||||
targ->enemy = attacker;
|
||||
if (!(targ->monsterinfo.aiflags & AI_DUCKED))
|
||||
FoundTarget (targ);
|
||||
}
|
||||
// if they *meant* to shoot us, then shoot back
|
||||
else if (attacker->enemy == targ)
|
||||
{
|
||||
if (targ->enemy && targ->enemy->client)
|
||||
targ->oldenemy = targ->enemy;
|
||||
targ->enemy = attacker;
|
||||
if (!(targ->monsterinfo.aiflags & AI_DUCKED))
|
||||
FoundTarget (targ);
|
||||
}
|
||||
// otherwise get mad at whoever they are mad at (help our buddy) unless it is us!
|
||||
else if (attacker->enemy && attacker->enemy != targ)
|
||||
{
|
||||
if (targ->enemy && targ->enemy->client)
|
||||
targ->oldenemy = targ->enemy;
|
||||
targ->enemy = attacker->enemy;
|
||||
if (!(targ->monsterinfo.aiflags & AI_DUCKED))
|
||||
FoundTarget (targ);
|
||||
}
|
||||
}
|
||||
|
||||
qboolean CheckTeamDamage (edict_t *targ, edict_t *attacker)
|
||||
{
|
||||
//FIXME make the next line real and uncomment this block
|
||||
// if ((ability to damage a teammate == OFF) && (targ's team == attacker's team))
|
||||
return false;
|
||||
}
|
||||
|
||||
void T_Damage (edict_t *targ, edict_t *inflictor, edict_t *attacker, vec3_t dir, vec3_t point, vec3_t normal, int damage, int knockback, int dflags, int mod)
|
||||
{
|
||||
gclient_t *client;
|
||||
int take;
|
||||
int save;
|
||||
int asave;
|
||||
int psave;
|
||||
int te_sparks;
|
||||
int sphere_notified; // PGM
|
||||
|
||||
if (!targ->takedamage)
|
||||
return;
|
||||
|
||||
sphere_notified = false; // PGM
|
||||
|
||||
// friendly fire avoidance
|
||||
// if enabled you can't hurt teammates (but you can hurt yourself)
|
||||
// knockback still occurs
|
||||
if ((targ != attacker) && ((deathmatch->value && ((int)(dmflags->value) & (DF_MODELTEAMS | DF_SKINTEAMS))) || coop->value))
|
||||
{
|
||||
if (OnSameTeam (targ, attacker))
|
||||
{
|
||||
// PMM - nukes kill everyone
|
||||
if (((int)(dmflags->value) & DF_NO_FRIENDLY_FIRE) && (mod != MOD_NUKE))
|
||||
damage = 0;
|
||||
else
|
||||
mod |= MOD_FRIENDLY_FIRE;
|
||||
}
|
||||
}
|
||||
meansOfDeath = mod;
|
||||
|
||||
//ROGUE
|
||||
// allow the deathmatch game to change values
|
||||
if (deathmatch->value && gamerules && gamerules->value)
|
||||
{
|
||||
if(DMGame.ChangeDamage)
|
||||
damage = DMGame.ChangeDamage(targ, attacker, damage, mod);
|
||||
if(DMGame.ChangeKnockback)
|
||||
knockback = DMGame.ChangeKnockback(targ, attacker, knockback, mod);
|
||||
|
||||
if(!damage)
|
||||
return;
|
||||
}
|
||||
//ROGUE
|
||||
|
||||
// easy mode takes half damage
|
||||
if (skill->value == 0 && deathmatch->value == 0 && targ->client)
|
||||
{
|
||||
damage *= 0.5;
|
||||
if (!damage)
|
||||
damage = 1;
|
||||
}
|
||||
|
||||
client = targ->client;
|
||||
|
||||
// PMM - defender sphere takes half damage
|
||||
if ((client) && (client->owned_sphere) && (client->owned_sphere->spawnflags == 1))
|
||||
{
|
||||
damage *= 0.5;
|
||||
if (!damage)
|
||||
damage = 1;
|
||||
}
|
||||
|
||||
if (dflags & DAMAGE_BULLET)
|
||||
te_sparks = TE_BULLET_SPARKS;
|
||||
else
|
||||
te_sparks = TE_SPARKS;
|
||||
|
||||
VectorNormalize(dir);
|
||||
|
||||
// bonus damage for suprising a monster
|
||||
if (!(dflags & DAMAGE_RADIUS) && (targ->svflags & SVF_MONSTER) && (attacker->client) && (!targ->enemy) && (targ->health > 0))
|
||||
damage *= 2;
|
||||
|
||||
if (targ->flags & FL_NO_KNOCKBACK)
|
||||
knockback = 0;
|
||||
|
||||
// figure momentum add
|
||||
if (!(dflags & DAMAGE_NO_KNOCKBACK))
|
||||
{
|
||||
if ((knockback) && (targ->movetype != MOVETYPE_NONE) && (targ->movetype != MOVETYPE_BOUNCE) && (targ->movetype != MOVETYPE_PUSH) && (targ->movetype != MOVETYPE_STOP))
|
||||
{
|
||||
vec3_t kvel;
|
||||
float mass;
|
||||
|
||||
if (targ->mass < 50)
|
||||
mass = 50;
|
||||
else
|
||||
mass = targ->mass;
|
||||
|
||||
if (targ->client && attacker == targ)
|
||||
VectorScale (dir, 1600.0 * (float)knockback / mass, kvel); // the rocket jump hack...
|
||||
else
|
||||
VectorScale (dir, 500.0 * (float)knockback / mass, kvel);
|
||||
|
||||
VectorAdd (targ->velocity, kvel, targ->velocity);
|
||||
}
|
||||
}
|
||||
|
||||
take = damage;
|
||||
save = 0;
|
||||
|
||||
// check for godmode
|
||||
if ( (targ->flags & FL_GODMODE) && !(dflags & DAMAGE_NO_PROTECTION) )
|
||||
{
|
||||
take = 0;
|
||||
save = damage;
|
||||
SpawnDamage (te_sparks, point, normal, save);
|
||||
}
|
||||
|
||||
// check for invincibility
|
||||
if ((client && client->invincible_framenum > level.framenum ) && !(dflags & DAMAGE_NO_PROTECTION))
|
||||
{
|
||||
if (targ->pain_debounce_time < level.time)
|
||||
{
|
||||
gi.sound(targ, CHAN_ITEM, gi.soundindex("items/protect4.wav"), 1, ATTN_NORM, 0);
|
||||
targ->pain_debounce_time = level.time + 2;
|
||||
}
|
||||
take = 0;
|
||||
save = damage;
|
||||
}
|
||||
// ROGUE
|
||||
// check for monster invincibility
|
||||
if (((targ->svflags & SVF_MONSTER) && targ->monsterinfo.invincible_framenum > level.framenum ) && !(dflags & DAMAGE_NO_PROTECTION))
|
||||
{
|
||||
if (targ->pain_debounce_time < level.time)
|
||||
{
|
||||
gi.sound(targ, CHAN_ITEM, gi.soundindex("items/protect4.wav"), 1, ATTN_NORM, 0);
|
||||
targ->pain_debounce_time = level.time + 2;
|
||||
}
|
||||
take = 0;
|
||||
save = damage;
|
||||
}
|
||||
// ROGUE
|
||||
|
||||
psave = CheckPowerArmor (targ, point, normal, take, dflags);
|
||||
take -= psave;
|
||||
|
||||
asave = CheckArmor (targ, point, normal, take, te_sparks, dflags);
|
||||
take -= asave;
|
||||
|
||||
//treat cheat/powerup savings the same as armor
|
||||
asave += save;
|
||||
|
||||
// team damage avoidance
|
||||
if (!(dflags & DAMAGE_NO_PROTECTION) && CheckTeamDamage (targ, attacker))
|
||||
return;
|
||||
|
||||
// ROGUE - this option will do damage both to the armor and person. originally for DPU rounds
|
||||
if (dflags & DAMAGE_DESTROY_ARMOR)
|
||||
{
|
||||
if(!(targ->flags & FL_GODMODE) && !(dflags & DAMAGE_NO_PROTECTION) &&
|
||||
!(client && client->invincible_framenum > level.framenum))
|
||||
{
|
||||
take = damage;
|
||||
}
|
||||
}
|
||||
// ROGUE
|
||||
|
||||
// do the damage
|
||||
if (take)
|
||||
{
|
||||
//PGM need more blood for chainfist.
|
||||
if(targ->flags & FL_MECHANICAL)
|
||||
{
|
||||
SpawnDamage ( TE_ELECTRIC_SPARKS, point, normal, take);
|
||||
}
|
||||
else if ((targ->svflags & SVF_MONSTER) || (client))
|
||||
{
|
||||
if(mod == MOD_CHAINFIST)
|
||||
SpawnDamage (TE_MOREBLOOD, point, normal, 255);
|
||||
else
|
||||
SpawnDamage (TE_BLOOD, point, normal, take);
|
||||
}
|
||||
else
|
||||
SpawnDamage (te_sparks, point, normal, take);
|
||||
//PGM
|
||||
|
||||
targ->health = targ->health - take;
|
||||
|
||||
//PGM - spheres need to know who to shoot at
|
||||
if(client && client->owned_sphere)
|
||||
{
|
||||
sphere_notified = true;
|
||||
if(client->owned_sphere->pain)
|
||||
client->owned_sphere->pain (client->owned_sphere, attacker, 0, 0);
|
||||
}
|
||||
//PGM
|
||||
|
||||
if (targ->health <= 0)
|
||||
{
|
||||
if ((targ->svflags & SVF_MONSTER) || (client))
|
||||
targ->flags |= FL_NO_KNOCKBACK;
|
||||
Killed (targ, inflictor, attacker, take, point);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//PGM - spheres need to know who to shoot at
|
||||
if (!sphere_notified)
|
||||
{
|
||||
if(client && client->owned_sphere)
|
||||
{
|
||||
sphere_notified = true;
|
||||
if(client->owned_sphere->pain)
|
||||
client->owned_sphere->pain (client->owned_sphere, attacker, 0, 0);
|
||||
}
|
||||
}
|
||||
//PGM
|
||||
|
||||
if (targ->svflags & SVF_MONSTER)
|
||||
{
|
||||
M_ReactToDamage (targ, attacker, inflictor);
|
||||
// PMM - fixme - if anyone else but the medic ever uses AI_MEDIC, check for it here instead
|
||||
// of in the medic's pain function
|
||||
if (!(targ->monsterinfo.aiflags & AI_DUCKED) && (take))
|
||||
{
|
||||
targ->pain (targ, attacker, knockback, take);
|
||||
// nightmare mode monsters don't go into pain frames often
|
||||
if (skill->value == 3)
|
||||
targ->pain_debounce_time = level.time + 5;
|
||||
}
|
||||
}
|
||||
else if (client)
|
||||
{
|
||||
if (!(targ->flags & FL_GODMODE) && (take))
|
||||
targ->pain (targ, attacker, knockback, take);
|
||||
}
|
||||
else if (take)
|
||||
{
|
||||
if (targ->pain)
|
||||
targ->pain (targ, attacker, knockback, take);
|
||||
}
|
||||
|
||||
// add to the damage inflicted on a player this frame
|
||||
// the total will be turned into screen blends and view angle kicks
|
||||
// at the end of the frame
|
||||
if (client)
|
||||
{
|
||||
client->damage_parmor += psave;
|
||||
client->damage_armor += asave;
|
||||
client->damage_blood += take;
|
||||
client->damage_knockback += knockback;
|
||||
VectorCopy (point, client->damage_from);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
============
|
||||
T_RadiusDamage
|
||||
============
|
||||
*/
|
||||
void T_RadiusDamage (edict_t *inflictor, edict_t *attacker, float damage, edict_t *ignore, float radius, int mod)
|
||||
{
|
||||
float points;
|
||||
edict_t *ent = NULL;
|
||||
vec3_t v;
|
||||
vec3_t dir;
|
||||
|
||||
while ((ent = findradius(ent, inflictor->s.origin, radius)) != NULL)
|
||||
{
|
||||
if (ent == ignore)
|
||||
continue;
|
||||
if (!ent->takedamage)
|
||||
continue;
|
||||
|
||||
VectorAdd (ent->mins, ent->maxs, v);
|
||||
VectorMA (ent->s.origin, 0.5, v, v);
|
||||
VectorSubtract (inflictor->s.origin, v, v);
|
||||
points = damage - 0.5 * VectorLength (v);
|
||||
if (ent == attacker)
|
||||
points = points * 0.5;
|
||||
if (points > 0)
|
||||
{
|
||||
if (CanDamage (ent, inflictor))
|
||||
{
|
||||
VectorSubtract (ent->s.origin, inflictor->s.origin, dir);
|
||||
T_Damage (ent, inflictor, attacker, dir, inflictor->s.origin, vec3_origin, (int)points, (int)points, DAMAGE_RADIUS, mod);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// **********************
|
||||
// ROGUE
|
||||
|
||||
/*
|
||||
============
|
||||
T_RadiusNukeDamage
|
||||
|
||||
Like T_RadiusDamage, but ignores walls (skips CanDamage check, among others)
|
||||
// up to KILLZONE radius, do 10,000 points
|
||||
// after that, do damage linearly out to KILLZONE2 radius
|
||||
============
|
||||
*/
|
||||
|
||||
void T_RadiusNukeDamage (edict_t *inflictor, edict_t *attacker, float damage, edict_t *ignore, float radius, int mod)
|
||||
{
|
||||
float points;
|
||||
edict_t *ent = NULL;
|
||||
vec3_t v;
|
||||
vec3_t dir;
|
||||
float len;
|
||||
float killzone, killzone2;
|
||||
trace_t tr;
|
||||
float dist;
|
||||
|
||||
killzone = radius;
|
||||
killzone2 = radius*2.0;
|
||||
|
||||
while ((ent = findradius(ent, inflictor->s.origin, killzone2)) != NULL)
|
||||
{
|
||||
// ignore nobody
|
||||
if (ent == ignore)
|
||||
continue;
|
||||
if (!ent->takedamage)
|
||||
continue;
|
||||
if (!ent->inuse)
|
||||
continue;
|
||||
if (!(ent->client || (ent->svflags & SVF_MONSTER) || (ent->svflags & SVF_DAMAGEABLE)))
|
||||
continue;
|
||||
|
||||
VectorAdd (ent->mins, ent->maxs, v);
|
||||
VectorMA (ent->s.origin, 0.5, v, v);
|
||||
VectorSubtract (inflictor->s.origin, v, v);
|
||||
len = VectorLength(v);
|
||||
if (len <= killzone)
|
||||
{
|
||||
if (ent->client)
|
||||
ent->flags |= FL_NOGIB;
|
||||
points = 10000;
|
||||
}
|
||||
else if (len <= killzone2)
|
||||
points = (damage/killzone)*(killzone2 - len);
|
||||
else
|
||||
points = 0;
|
||||
// points = damage - 0.005 * len*len;
|
||||
// if (ent == attacker)
|
||||
// points = points * 0.5;
|
||||
// if ((g_showlogic) && (g_showlogic->value))
|
||||
// {
|
||||
// if (!(strcmp(ent->classname, "player")))
|
||||
// gi.dprintf ("dist = %2.2f doing %6.0f damage to %s\n", len, points, inflictor->teammaster->client->pers.netname);
|
||||
// else
|
||||
// gi.dprintf ("dist = %2.2f doing %6.0f damage to %s\n", len, points, ent->classname);
|
||||
// }
|
||||
if (points > 0)
|
||||
{
|
||||
// if (CanDamage (ent, inflictor))
|
||||
// {
|
||||
if (ent->client)
|
||||
ent->client->nuke_framenum = level.framenum + 20;
|
||||
VectorSubtract (ent->s.origin, inflictor->s.origin, dir);
|
||||
T_Damage (ent, inflictor, attacker, dir, inflictor->s.origin, vec3_origin, (int)points, (int)points, DAMAGE_RADIUS, mod);
|
||||
// }
|
||||
}
|
||||
}
|
||||
ent = g_edicts+1; // skip the worldspawn
|
||||
// cycle through players
|
||||
while (ent)
|
||||
{
|
||||
if ((ent->client) && (ent->client->nuke_framenum != level.framenum+20) && (ent->inuse))
|
||||
{
|
||||
tr = gi.trace (inflictor->s.origin, NULL, NULL, ent->s.origin, inflictor, MASK_SOLID);
|
||||
if (tr.fraction == 1.0)
|
||||
{
|
||||
// if ((g_showlogic) && (g_showlogic->value))
|
||||
// gi.dprintf ("Undamaged player in LOS with nuke, flashing!\n");
|
||||
ent->client->nuke_framenum = level.framenum + 20;
|
||||
}
|
||||
else
|
||||
{
|
||||
dist = realrange (ent, inflictor);
|
||||
if (dist < 2048)
|
||||
ent->client->nuke_framenum = max(ent->client->nuke_framenum,level.framenum + 15);
|
||||
else
|
||||
ent->client->nuke_framenum = max(ent->client->nuke_framenum,level.framenum + 10);
|
||||
}
|
||||
ent++;
|
||||
}
|
||||
else
|
||||
ent = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
T_RadiusClassDamage
|
||||
|
||||
Like T_RadiusDamage, but ignores anything with classname=ignoreClass
|
||||
============
|
||||
*/
|
||||
void T_RadiusClassDamage (edict_t *inflictor, edict_t *attacker, float damage, char *ignoreClass, float radius, int mod)
|
||||
{
|
||||
float points;
|
||||
edict_t *ent = NULL;
|
||||
vec3_t v;
|
||||
vec3_t dir;
|
||||
|
||||
while ((ent = findradius(ent, inflictor->s.origin, radius)) != NULL)
|
||||
{
|
||||
if (ent->classname && !strcmp(ent->classname, ignoreClass))
|
||||
continue;
|
||||
if (!ent->takedamage)
|
||||
continue;
|
||||
|
||||
VectorAdd (ent->mins, ent->maxs, v);
|
||||
VectorMA (ent->s.origin, 0.5, v, v);
|
||||
VectorSubtract (inflictor->s.origin, v, v);
|
||||
points = damage - 0.5 * VectorLength (v);
|
||||
if (ent == attacker)
|
||||
points = points * 0.5;
|
||||
if (points > 0)
|
||||
{
|
||||
if (CanDamage (ent, inflictor))
|
||||
{
|
||||
VectorSubtract (ent->s.origin, inflictor->s.origin, dir);
|
||||
T_Damage (ent, inflictor, attacker, dir, inflictor->s.origin, vec3_origin, (int)points, (int)points, DAMAGE_RADIUS, mod);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ROGUE
|
||||
// ********************
|
412
g_main.c
Normal file
412
g_main.c
Normal file
|
@ -0,0 +1,412 @@
|
|||
|
||||
#include "g_local.h"
|
||||
|
||||
game_locals_t game;
|
||||
level_locals_t level;
|
||||
game_import_t gi;
|
||||
game_export_t globals;
|
||||
spawn_temp_t st;
|
||||
|
||||
int sm_meat_index;
|
||||
int snd_fry;
|
||||
int meansOfDeath;
|
||||
|
||||
edict_t *g_edicts;
|
||||
|
||||
cvar_t *deathmatch;
|
||||
cvar_t *coop;
|
||||
cvar_t *dmflags;
|
||||
cvar_t *skill;
|
||||
cvar_t *fraglimit;
|
||||
cvar_t *timelimit;
|
||||
cvar_t *password;
|
||||
cvar_t *spectator_password;
|
||||
cvar_t *maxclients;
|
||||
cvar_t *maxspectators;
|
||||
cvar_t *maxentities;
|
||||
cvar_t *g_select_empty;
|
||||
cvar_t *dedicated;
|
||||
|
||||
cvar_t *filterban;
|
||||
|
||||
cvar_t *sv_maxvelocity;
|
||||
cvar_t *sv_gravity;
|
||||
|
||||
cvar_t *sv_rollspeed;
|
||||
cvar_t *sv_rollangle;
|
||||
cvar_t *gun_x;
|
||||
cvar_t *gun_y;
|
||||
cvar_t *gun_z;
|
||||
|
||||
cvar_t *run_pitch;
|
||||
cvar_t *run_roll;
|
||||
cvar_t *bob_up;
|
||||
cvar_t *bob_pitch;
|
||||
cvar_t *bob_roll;
|
||||
|
||||
cvar_t *sv_cheats;
|
||||
|
||||
cvar_t *flood_msgs;
|
||||
cvar_t *flood_persecond;
|
||||
cvar_t *flood_waitdelay;
|
||||
|
||||
cvar_t *sv_maplist;
|
||||
|
||||
cvar_t *sv_stopspeed; //PGM (this was a define in g_phys.c)
|
||||
|
||||
//ROGUE cvars
|
||||
cvar_t *g_showlogic;
|
||||
cvar_t *gamerules;
|
||||
cvar_t *huntercam;
|
||||
cvar_t *strong_mines;
|
||||
cvar_t *randomrespawn;
|
||||
//ROGUE
|
||||
|
||||
void SpawnEntities (char *mapname, char *entities, char *spawnpoint);
|
||||
void ClientThink (edict_t *ent, usercmd_t *cmd);
|
||||
qboolean ClientConnect (edict_t *ent, char *userinfo);
|
||||
void ClientUserinfoChanged (edict_t *ent, char *userinfo);
|
||||
void ClientDisconnect (edict_t *ent);
|
||||
void ClientBegin (edict_t *ent);
|
||||
void ClientCommand (edict_t *ent);
|
||||
void RunEntity (edict_t *ent);
|
||||
void WriteGame (char *filename, qboolean autosave);
|
||||
void ReadGame (char *filename);
|
||||
void WriteLevel (char *filename);
|
||||
void ReadLevel (char *filename);
|
||||
void InitGame (void);
|
||||
void G_RunFrame (void);
|
||||
|
||||
|
||||
//===================================================================
|
||||
|
||||
|
||||
void ShutdownGame (void)
|
||||
{
|
||||
gi.dprintf ("==== ShutdownGame ====\n");
|
||||
|
||||
gi.FreeTags (TAG_LEVEL);
|
||||
gi.FreeTags (TAG_GAME);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=================
|
||||
GetGameAPI
|
||||
|
||||
Returns a pointer to the structure with all entry points
|
||||
and global variables
|
||||
=================
|
||||
*/
|
||||
game_export_t *GetGameAPI (game_import_t *import)
|
||||
{
|
||||
gi = *import;
|
||||
|
||||
globals.apiversion = GAME_API_VERSION;
|
||||
globals.Init = InitGame;
|
||||
globals.Shutdown = ShutdownGame;
|
||||
globals.SpawnEntities = SpawnEntities;
|
||||
|
||||
globals.WriteGame = WriteGame;
|
||||
globals.ReadGame = ReadGame;
|
||||
globals.WriteLevel = WriteLevel;
|
||||
globals.ReadLevel = ReadLevel;
|
||||
|
||||
globals.ClientThink = ClientThink;
|
||||
globals.ClientConnect = ClientConnect;
|
||||
globals.ClientUserinfoChanged = ClientUserinfoChanged;
|
||||
globals.ClientDisconnect = ClientDisconnect;
|
||||
globals.ClientBegin = ClientBegin;
|
||||
globals.ClientCommand = ClientCommand;
|
||||
|
||||
globals.RunFrame = G_RunFrame;
|
||||
|
||||
globals.ServerCommand = ServerCommand;
|
||||
|
||||
globals.edict_size = sizeof(edict_t);
|
||||
|
||||
return &globals;
|
||||
}
|
||||
|
||||
#ifndef GAME_HARD_LINKED
|
||||
// this is only here so the functions in q_shared.c and q_shwin.c can link
|
||||
void Sys_Error (char *error, ...)
|
||||
{
|
||||
va_list argptr;
|
||||
char text[1024];
|
||||
|
||||
va_start (argptr, error);
|
||||
vsprintf (text, error, argptr);
|
||||
va_end (argptr);
|
||||
|
||||
gi.error (ERR_FATAL, "%s", text);
|
||||
}
|
||||
|
||||
void Com_Printf (char *msg, ...)
|
||||
{
|
||||
va_list argptr;
|
||||
char text[1024];
|
||||
|
||||
va_start (argptr, msg);
|
||||
vsprintf (text, msg, argptr);
|
||||
va_end (argptr);
|
||||
|
||||
gi.dprintf ("%s", text);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
//======================================================================
|
||||
|
||||
|
||||
/*
|
||||
=================
|
||||
ClientEndServerFrames
|
||||
=================
|
||||
*/
|
||||
void ClientEndServerFrames (void)
|
||||
{
|
||||
int i;
|
||||
edict_t *ent;
|
||||
|
||||
// calc the player views now that all pushing
|
||||
// and damage has been added
|
||||
for (i=0 ; i<maxclients->value ; i++)
|
||||
{
|
||||
ent = g_edicts + 1 + i;
|
||||
if (!ent->inuse || !ent->client)
|
||||
continue;
|
||||
ClientEndServerFrame (ent);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
CreateTargetChangeLevel
|
||||
|
||||
Returns the created target changelevel
|
||||
=================
|
||||
*/
|
||||
edict_t *CreateTargetChangeLevel(char *map)
|
||||
{
|
||||
edict_t *ent;
|
||||
|
||||
ent = G_Spawn ();
|
||||
ent->classname = "target_changelevel";
|
||||
Com_sprintf(level.nextmap, sizeof(level.nextmap), "%s", map);
|
||||
ent->map = level.nextmap;
|
||||
return ent;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
EndDMLevel
|
||||
|
||||
The timelimit or fraglimit has been exceeded
|
||||
=================
|
||||
*/
|
||||
void EndDMLevel (void)
|
||||
{
|
||||
edict_t *ent;
|
||||
char *s, *t, *f;
|
||||
static const char *seps = " ,\n\r";
|
||||
|
||||
// stay on same level flag
|
||||
if ((int)dmflags->value & DF_SAME_LEVEL)
|
||||
{
|
||||
BeginIntermission (CreateTargetChangeLevel (level.mapname) );
|
||||
return;
|
||||
}
|
||||
|
||||
// see if it's in the map list
|
||||
if (*sv_maplist->string) {
|
||||
s = strdup(sv_maplist->string);
|
||||
f = NULL;
|
||||
t = strtok(s, seps);
|
||||
while (t != NULL) {
|
||||
if (Q_stricmp(t, level.mapname) == 0) {
|
||||
// it's in the list, go to the next one
|
||||
t = strtok(NULL, seps);
|
||||
if (t == NULL) { // end of list, go to first one
|
||||
if (f == NULL) // there isn't a first one, same level
|
||||
BeginIntermission (CreateTargetChangeLevel (level.mapname) );
|
||||
else
|
||||
BeginIntermission (CreateTargetChangeLevel (f) );
|
||||
} else
|
||||
BeginIntermission (CreateTargetChangeLevel (t) );
|
||||
free(s);
|
||||
return;
|
||||
}
|
||||
if (!f)
|
||||
f = t;
|
||||
t = strtok(NULL, seps);
|
||||
}
|
||||
free(s);
|
||||
}
|
||||
|
||||
if (level.nextmap[0]) // go to a specific map
|
||||
BeginIntermission (CreateTargetChangeLevel (level.nextmap) );
|
||||
else { // search for a changelevel
|
||||
ent = G_Find (NULL, FOFS(classname), "target_changelevel");
|
||||
if (!ent)
|
||||
{ // the map designer didn't include a changelevel,
|
||||
// so create a fake ent that goes back to the same level
|
||||
BeginIntermission (CreateTargetChangeLevel (level.mapname) );
|
||||
return;
|
||||
}
|
||||
BeginIntermission (ent);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
CheckDMRules
|
||||
=================
|
||||
*/
|
||||
void CheckDMRules (void)
|
||||
{
|
||||
int i;
|
||||
gclient_t *cl;
|
||||
|
||||
if (level.intermissiontime)
|
||||
return;
|
||||
|
||||
if (!deathmatch->value)
|
||||
return;
|
||||
|
||||
//=======
|
||||
//ROGUE
|
||||
if (gamerules && gamerules->value && DMGame.CheckDMRules)
|
||||
{
|
||||
if(DMGame.CheckDMRules())
|
||||
return;
|
||||
}
|
||||
//ROGUE
|
||||
//=======
|
||||
|
||||
if (timelimit->value)
|
||||
{
|
||||
if (level.time >= timelimit->value*60)
|
||||
{
|
||||
gi.bprintf (PRINT_HIGH, "Timelimit hit.\n");
|
||||
EndDMLevel ();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (fraglimit->value)
|
||||
{
|
||||
for (i=0 ; i<maxclients->value ; i++)
|
||||
{
|
||||
cl = game.clients + i;
|
||||
if (!g_edicts[i+1].inuse)
|
||||
continue;
|
||||
|
||||
if (cl->resp.score >= fraglimit->value)
|
||||
{
|
||||
gi.bprintf (PRINT_HIGH, "Fraglimit hit.\n");
|
||||
EndDMLevel ();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
ExitLevel
|
||||
=============
|
||||
*/
|
||||
void ExitLevel (void)
|
||||
{
|
||||
int i;
|
||||
edict_t *ent;
|
||||
char command [256];
|
||||
|
||||
Com_sprintf (command, sizeof(command), "gamemap \"%s\"\n", level.changemap);
|
||||
gi.AddCommandString (command);
|
||||
level.changemap = NULL;
|
||||
level.exitintermission = 0;
|
||||
level.intermissiontime = 0;
|
||||
ClientEndServerFrames ();
|
||||
|
||||
// clear some things before going to next level
|
||||
for (i=0 ; i<maxclients->value ; i++)
|
||||
{
|
||||
ent = g_edicts + 1 + i;
|
||||
if (!ent->inuse)
|
||||
continue;
|
||||
if (ent->health > ent->client->pers.max_health)
|
||||
ent->health = ent->client->pers.max_health;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
G_RunFrame
|
||||
|
||||
Advances the world by 0.1 seconds
|
||||
================
|
||||
*/
|
||||
void G_RunFrame (void)
|
||||
{
|
||||
int i;
|
||||
edict_t *ent;
|
||||
|
||||
level.framenum++;
|
||||
level.time = level.framenum*FRAMETIME;
|
||||
|
||||
// choose a client for monsters to target this frame
|
||||
AI_SetSightClient ();
|
||||
|
||||
// exit intermissions
|
||||
|
||||
if (level.exitintermission)
|
||||
{
|
||||
ExitLevel ();
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// treat each object in turn
|
||||
// even the world gets a chance to think
|
||||
//
|
||||
ent = &g_edicts[0];
|
||||
for (i=0 ; i<globals.num_edicts ; i++, ent++)
|
||||
{
|
||||
if (!ent->inuse)
|
||||
continue;
|
||||
|
||||
level.current_entity = ent;
|
||||
|
||||
VectorCopy (ent->s.origin, ent->s.old_origin);
|
||||
|
||||
// if the ground entity moved, make sure we are still on it
|
||||
if ((ent->groundentity) && (ent->groundentity->linkcount != ent->groundentity_linkcount))
|
||||
{
|
||||
ent->groundentity = NULL;
|
||||
if ( !(ent->flags & (FL_SWIM|FL_FLY)) && (ent->svflags & SVF_MONSTER) )
|
||||
{
|
||||
M_CheckGround (ent);
|
||||
}
|
||||
}
|
||||
|
||||
if (i > 0 && i <= maxclients->value)
|
||||
{
|
||||
ClientBeginServerFrame (ent);
|
||||
continue;
|
||||
}
|
||||
|
||||
G_RunEntity (ent);
|
||||
}
|
||||
|
||||
// see if it is time to end a deathmatch
|
||||
CheckDMRules ();
|
||||
|
||||
// build the playerstate_t structures for all players
|
||||
ClientEndServerFrames ();
|
||||
}
|
||||
|
979
g_monster.c
Normal file
979
g_monster.c
Normal file
|
@ -0,0 +1,979 @@
|
|||
#include "g_local.h"
|
||||
|
||||
|
||||
//
|
||||
// monster weapons
|
||||
//
|
||||
|
||||
//FIXME mosnters should call these with a totally accurate direction
|
||||
// and we can mess it up based on skill. Spread should be for normal
|
||||
// and we can tighten or loosen based on skill. We could muck with
|
||||
// the damages too, but I'm not sure that's such a good idea.
|
||||
void monster_fire_bullet (edict_t *self, vec3_t start, vec3_t dir, int damage, int kick, int hspread, int vspread, int flashtype)
|
||||
{
|
||||
fire_bullet (self, start, dir, damage, kick, hspread, vspread, MOD_UNKNOWN);
|
||||
|
||||
gi.WriteByte (svc_muzzleflash2);
|
||||
gi.WriteShort (self - g_edicts);
|
||||
gi.WriteByte (flashtype);
|
||||
gi.multicast (start, MULTICAST_PVS);
|
||||
}
|
||||
|
||||
void monster_fire_shotgun (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int hspread, int vspread, int count, int flashtype)
|
||||
{
|
||||
fire_shotgun (self, start, aimdir, damage, kick, hspread, vspread, count, MOD_UNKNOWN);
|
||||
|
||||
gi.WriteByte (svc_muzzleflash2);
|
||||
gi.WriteShort (self - g_edicts);
|
||||
gi.WriteByte (flashtype);
|
||||
gi.multicast (start, MULTICAST_PVS);
|
||||
}
|
||||
|
||||
void monster_fire_blaster (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int flashtype, int effect)
|
||||
{
|
||||
fire_blaster (self, start, dir, damage, speed, effect, false);
|
||||
|
||||
gi.WriteByte (svc_muzzleflash2);
|
||||
gi.WriteShort (self - g_edicts);
|
||||
gi.WriteByte (flashtype);
|
||||
gi.multicast (start, MULTICAST_PVS);
|
||||
}
|
||||
|
||||
//ROGUE
|
||||
void monster_fire_blaster2 (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int flashtype, int effect)
|
||||
{
|
||||
fire_blaster2 (self, start, dir, damage, speed, effect, false);
|
||||
|
||||
gi.WriteByte (svc_muzzleflash2);
|
||||
gi.WriteShort (self - g_edicts);
|
||||
gi.WriteByte (flashtype);
|
||||
gi.multicast (start, MULTICAST_PVS);
|
||||
}
|
||||
|
||||
// FIXME -- add muzzle flash
|
||||
void monster_fire_tracker (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, edict_t *enemy, int flashtype)
|
||||
{
|
||||
fire_tracker (self, start, dir, damage, speed, enemy);
|
||||
|
||||
gi.WriteByte (svc_muzzleflash2);
|
||||
gi.WriteShort (self - g_edicts);
|
||||
gi.WriteByte (flashtype);
|
||||
gi.multicast (start, MULTICAST_PVS);
|
||||
}
|
||||
|
||||
void monster_fire_heat (edict_t *self, vec3_t start, vec3_t dir, vec3_t offset, int damage, int kick, int flashtype)
|
||||
{
|
||||
fire_heat (self, start, dir, offset, damage, kick, true);
|
||||
|
||||
gi.WriteByte (svc_muzzleflash2);
|
||||
gi.WriteShort (self - g_edicts);
|
||||
gi.WriteByte (flashtype);
|
||||
gi.multicast (start, MULTICAST_PVS);
|
||||
}
|
||||
//ROGUE
|
||||
|
||||
void monster_fire_grenade (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, int flashtype)
|
||||
{
|
||||
fire_grenade (self, start, aimdir, damage, speed, 2.5, damage+40);
|
||||
|
||||
gi.WriteByte (svc_muzzleflash2);
|
||||
gi.WriteShort (self - g_edicts);
|
||||
gi.WriteByte (flashtype);
|
||||
gi.multicast (start, MULTICAST_PVS);
|
||||
}
|
||||
|
||||
void monster_fire_rocket (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int flashtype)
|
||||
{
|
||||
fire_rocket (self, start, dir, damage, speed, damage+20, damage);
|
||||
|
||||
gi.WriteByte (svc_muzzleflash2);
|
||||
gi.WriteShort (self - g_edicts);
|
||||
gi.WriteByte (flashtype);
|
||||
gi.multicast (start, MULTICAST_PVS);
|
||||
}
|
||||
|
||||
void monster_fire_railgun (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int flashtype)
|
||||
{
|
||||
// PMM
|
||||
if (!(gi.pointcontents (start) & MASK_SOLID))
|
||||
fire_rail (self, start, aimdir, damage, kick);
|
||||
|
||||
gi.WriteByte (svc_muzzleflash2);
|
||||
gi.WriteShort (self - g_edicts);
|
||||
gi.WriteByte (flashtype);
|
||||
gi.multicast (start, MULTICAST_PVS);
|
||||
}
|
||||
|
||||
void monster_fire_bfg (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, int kick, float damage_radius, int flashtype)
|
||||
{
|
||||
fire_bfg (self, start, aimdir, damage, speed, damage_radius);
|
||||
|
||||
gi.WriteByte (svc_muzzleflash2);
|
||||
gi.WriteShort (self - g_edicts);
|
||||
gi.WriteByte (flashtype);
|
||||
gi.multicast (start, MULTICAST_PVS);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// Monster utility functions
|
||||
//
|
||||
|
||||
void M_FliesOff (edict_t *self)
|
||||
{
|
||||
self->s.effects &= ~EF_FLIES;
|
||||
self->s.sound = 0;
|
||||
}
|
||||
|
||||
void M_FliesOn (edict_t *self)
|
||||
{
|
||||
if (self->waterlevel)
|
||||
return;
|
||||
self->s.effects |= EF_FLIES;
|
||||
self->s.sound = gi.soundindex ("infantry/inflies1.wav");
|
||||
self->think = M_FliesOff;
|
||||
self->nextthink = level.time + 60;
|
||||
}
|
||||
|
||||
void M_FlyCheck (edict_t *self)
|
||||
{
|
||||
if (self->waterlevel)
|
||||
return;
|
||||
|
||||
if (random() > 0.5)
|
||||
return;
|
||||
|
||||
self->think = M_FliesOn;
|
||||
self->nextthink = level.time + 5 + 10 * random();
|
||||
}
|
||||
|
||||
void AttackFinished (edict_t *self, float time)
|
||||
{
|
||||
self->monsterinfo.attack_finished = level.time + time;
|
||||
}
|
||||
|
||||
|
||||
void M_CheckGround (edict_t *ent)
|
||||
{
|
||||
vec3_t point;
|
||||
trace_t trace;
|
||||
|
||||
if (ent->flags & (FL_SWIM|FL_FLY))
|
||||
return;
|
||||
|
||||
#ifdef ROGUE_GRAVITY
|
||||
if ((ent->velocity[2] * ent->gravityVector[2]) < -100) // PGM
|
||||
#else
|
||||
if (ent->velocity[2] > 100)
|
||||
#endif
|
||||
{
|
||||
ent->groundentity = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
// if the hull point one-quarter unit down is solid the entity is on ground
|
||||
point[0] = ent->s.origin[0];
|
||||
point[1] = ent->s.origin[1];
|
||||
#ifdef ROGUE_GRAVITY
|
||||
point[2] = ent->s.origin[2] + (0.25 * ent->gravityVector[2]); //PGM
|
||||
#else
|
||||
point[2] = ent->s.origin[2] - 0.25;
|
||||
#endif
|
||||
|
||||
trace = gi.trace (ent->s.origin, ent->mins, ent->maxs, point, ent, MASK_MONSTERSOLID);
|
||||
|
||||
// check steepness
|
||||
#ifdef ROGUE_GRAVITY
|
||||
//PGM
|
||||
if ( ent->gravityVector[2] < 0) // normal gravity
|
||||
{
|
||||
if ( trace.plane.normal[2] < 0.7 && !trace.startsolid)
|
||||
{
|
||||
ent->groundentity = NULL;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else // inverted gravity
|
||||
{
|
||||
if ( trace.plane.normal[2] > -0.7 && !trace.startsolid)
|
||||
{
|
||||
ent->groundentity = NULL;
|
||||
return;
|
||||
}
|
||||
}
|
||||
//PGM
|
||||
#else
|
||||
if ( trace.plane.normal[2] < 0.7 && !trace.startsolid)
|
||||
{
|
||||
ent->groundentity = NULL;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
// ent->groundentity = trace.ent;
|
||||
// ent->groundentity_linkcount = trace.ent->linkcount;
|
||||
// if (!trace.startsolid && !trace.allsolid)
|
||||
// VectorCopy (trace.endpos, ent->s.origin);
|
||||
if (!trace.startsolid && !trace.allsolid)
|
||||
{
|
||||
VectorCopy (trace.endpos, ent->s.origin);
|
||||
ent->groundentity = trace.ent;
|
||||
ent->groundentity_linkcount = trace.ent->linkcount;
|
||||
ent->velocity[2] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void M_CatagorizePosition (edict_t *ent)
|
||||
{
|
||||
vec3_t point;
|
||||
int cont;
|
||||
|
||||
//
|
||||
// get waterlevel
|
||||
//
|
||||
point[0] = ent->s.origin[0];
|
||||
point[1] = ent->s.origin[1];
|
||||
point[2] = ent->s.origin[2] + ent->mins[2] + 1;
|
||||
cont = gi.pointcontents (point);
|
||||
|
||||
if (!(cont & MASK_WATER))
|
||||
{
|
||||
ent->waterlevel = 0;
|
||||
ent->watertype = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
ent->watertype = cont;
|
||||
ent->waterlevel = 1;
|
||||
point[2] += 26;
|
||||
cont = gi.pointcontents (point);
|
||||
if (!(cont & MASK_WATER))
|
||||
return;
|
||||
|
||||
ent->waterlevel = 2;
|
||||
point[2] += 22;
|
||||
cont = gi.pointcontents (point);
|
||||
if (cont & MASK_WATER)
|
||||
ent->waterlevel = 3;
|
||||
}
|
||||
|
||||
|
||||
void M_WorldEffects (edict_t *ent)
|
||||
{
|
||||
int dmg;
|
||||
|
||||
if (ent->health > 0)
|
||||
{
|
||||
if (!(ent->flags & FL_SWIM))
|
||||
{
|
||||
if (ent->waterlevel < 3)
|
||||
{
|
||||
ent->air_finished = level.time + 12;
|
||||
}
|
||||
else if (ent->air_finished < level.time)
|
||||
{ // drown!
|
||||
if (ent->pain_debounce_time < level.time)
|
||||
{
|
||||
dmg = 2 + 2 * floor(level.time - ent->air_finished);
|
||||
if (dmg > 15)
|
||||
dmg = 15;
|
||||
T_Damage (ent, world, world, vec3_origin, ent->s.origin, vec3_origin, dmg, 0, DAMAGE_NO_ARMOR, MOD_WATER);
|
||||
ent->pain_debounce_time = level.time + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ent->waterlevel > 0)
|
||||
{
|
||||
ent->air_finished = level.time + 9;
|
||||
}
|
||||
else if (ent->air_finished < level.time)
|
||||
{ // suffocate!
|
||||
if (ent->pain_debounce_time < level.time)
|
||||
{
|
||||
dmg = 2 + 2 * floor(level.time - ent->air_finished);
|
||||
if (dmg > 15)
|
||||
dmg = 15;
|
||||
T_Damage (ent, world, world, vec3_origin, ent->s.origin, vec3_origin, dmg, 0, DAMAGE_NO_ARMOR, MOD_WATER);
|
||||
ent->pain_debounce_time = level.time + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ent->waterlevel == 0)
|
||||
{
|
||||
if (ent->flags & FL_INWATER)
|
||||
{
|
||||
gi.sound (ent, CHAN_BODY, gi.soundindex("player/watr_out.wav"), 1, ATTN_NORM, 0);
|
||||
ent->flags &= ~FL_INWATER;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if ((ent->watertype & CONTENTS_LAVA) && !(ent->flags & FL_IMMUNE_LAVA))
|
||||
{
|
||||
if (ent->damage_debounce_time < level.time)
|
||||
{
|
||||
ent->damage_debounce_time = level.time + 0.2;
|
||||
T_Damage (ent, world, world, vec3_origin, ent->s.origin, vec3_origin, 10*ent->waterlevel, 0, 0, MOD_LAVA);
|
||||
}
|
||||
}
|
||||
if ((ent->watertype & CONTENTS_SLIME) && !(ent->flags & FL_IMMUNE_SLIME))
|
||||
{
|
||||
if (ent->damage_debounce_time < level.time)
|
||||
{
|
||||
ent->damage_debounce_time = level.time + 1;
|
||||
T_Damage (ent, world, world, vec3_origin, ent->s.origin, vec3_origin, 4*ent->waterlevel, 0, 0, MOD_SLIME);
|
||||
}
|
||||
}
|
||||
|
||||
if ( !(ent->flags & FL_INWATER) )
|
||||
{
|
||||
if (!(ent->svflags & SVF_DEADMONSTER))
|
||||
{
|
||||
if (ent->watertype & CONTENTS_LAVA)
|
||||
if (random() <= 0.5)
|
||||
gi.sound (ent, CHAN_BODY, gi.soundindex("player/lava1.wav"), 1, ATTN_NORM, 0);
|
||||
else
|
||||
gi.sound (ent, CHAN_BODY, gi.soundindex("player/lava2.wav"), 1, ATTN_NORM, 0);
|
||||
else if (ent->watertype & CONTENTS_SLIME)
|
||||
gi.sound (ent, CHAN_BODY, gi.soundindex("player/watr_in.wav"), 1, ATTN_NORM, 0);
|
||||
else if (ent->watertype & CONTENTS_WATER)
|
||||
gi.sound (ent, CHAN_BODY, gi.soundindex("player/watr_in.wav"), 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
ent->flags |= FL_INWATER;
|
||||
ent->damage_debounce_time = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void M_droptofloor (edict_t *ent)
|
||||
{
|
||||
vec3_t end;
|
||||
trace_t trace;
|
||||
|
||||
#ifdef ROGUE_GRAVITY
|
||||
//PGM
|
||||
if(ent->gravityVector[2] < 0)
|
||||
{
|
||||
ent->s.origin[2] += 1;
|
||||
VectorCopy (ent->s.origin, end);
|
||||
end[2] -= 256;
|
||||
}
|
||||
else
|
||||
{
|
||||
ent->s.origin[2] -= 1;
|
||||
VectorCopy (ent->s.origin, end);
|
||||
end[2] += 256;
|
||||
}
|
||||
//PGM
|
||||
#else
|
||||
ent->s.origin[2] += 1;
|
||||
VectorCopy (ent->s.origin, end);
|
||||
end[2] -= 256;
|
||||
#endif
|
||||
|
||||
trace = gi.trace (ent->s.origin, ent->mins, ent->maxs, end, ent, MASK_MONSTERSOLID);
|
||||
|
||||
if (trace.fraction == 1 || trace.allsolid)
|
||||
return;
|
||||
|
||||
VectorCopy (trace.endpos, ent->s.origin);
|
||||
|
||||
gi.linkentity (ent);
|
||||
M_CheckGround (ent);
|
||||
M_CatagorizePosition (ent);
|
||||
}
|
||||
|
||||
|
||||
void M_SetEffects (edict_t *ent)
|
||||
{
|
||||
int remaining;
|
||||
|
||||
ent->s.effects &= ~(EF_COLOR_SHELL|EF_POWERSCREEN|EF_DOUBLE|EF_QUAD|EF_PENT);
|
||||
ent->s.renderfx &= ~(RF_SHELL_RED|RF_SHELL_GREEN|RF_SHELL_BLUE|RF_SHELL_DOUBLE);
|
||||
|
||||
if (ent->monsterinfo.aiflags & AI_RESURRECTING)
|
||||
{
|
||||
ent->s.effects |= EF_COLOR_SHELL;
|
||||
ent->s.renderfx |= RF_SHELL_RED;
|
||||
}
|
||||
|
||||
if (ent->health <= 0)
|
||||
return;
|
||||
|
||||
if (ent->powerarmor_time > level.time)
|
||||
{
|
||||
if (ent->monsterinfo.power_armor_type == POWER_ARMOR_SCREEN)
|
||||
{
|
||||
ent->s.effects |= EF_POWERSCREEN;
|
||||
}
|
||||
else if (ent->monsterinfo.power_armor_type == POWER_ARMOR_SHIELD)
|
||||
{
|
||||
ent->s.effects |= EF_COLOR_SHELL;
|
||||
ent->s.renderfx |= RF_SHELL_GREEN;
|
||||
}
|
||||
}
|
||||
// PMM - new monster powerups
|
||||
if (ent->monsterinfo.quad_framenum > level.framenum)
|
||||
{
|
||||
remaining = ent->monsterinfo.quad_framenum - level.framenum;
|
||||
if (remaining > 30 || (remaining & 4) )
|
||||
ent->s.effects |= EF_QUAD;
|
||||
}
|
||||
else
|
||||
ent->s.effects &= ~EF_QUAD;
|
||||
|
||||
if (ent->monsterinfo.double_framenum > level.framenum)
|
||||
{
|
||||
remaining = ent->monsterinfo.double_framenum - level.framenum;
|
||||
if (remaining > 30 || (remaining & 4) )
|
||||
ent->s.effects |= EF_DOUBLE;
|
||||
}
|
||||
else
|
||||
ent->s.effects &= ~EF_DOUBLE;
|
||||
|
||||
if (ent->monsterinfo.invincible_framenum > level.framenum)
|
||||
{
|
||||
remaining = ent->monsterinfo.invincible_framenum - level.framenum;
|
||||
if (remaining > 30 || (remaining & 4) )
|
||||
ent->s.effects |= EF_PENT;
|
||||
}
|
||||
else
|
||||
ent->s.effects &= ~EF_PENT;
|
||||
|
||||
// PMM
|
||||
// PMM - testing
|
||||
// ent->s.effects |= EF_COLOR_SHELL;
|
||||
// ent->s.renderfx |= RF_SHELL_HALF_DAM;
|
||||
/*
|
||||
if (fmod (level.time, 4.0) > 2.0)
|
||||
{
|
||||
gi.dprintf ("invulnerable ");
|
||||
ent->s.renderfx |= RF_SHELL_RED;
|
||||
}
|
||||
else
|
||||
ent->s.renderfx &= ~RF_SHELL_RED;
|
||||
|
||||
if (fmod (level.time, 8.0) > 4.0)
|
||||
{
|
||||
gi.dprintf ("shield ");
|
||||
ent->s.renderfx |= RF_SHELL_GREEN;
|
||||
}
|
||||
else
|
||||
ent->s.renderfx &= ~RF_SHELL_GREEN;
|
||||
|
||||
if (fmod (level.time, 16.0) > 8.0)
|
||||
{
|
||||
gi.dprintf ("quad ");
|
||||
ent->s.renderfx |= RF_SHELL_BLUE;\
|
||||
}
|
||||
else
|
||||
ent->s.renderfx &= ~RF_SHELL_BLUE;
|
||||
|
||||
if (fmod (level.time, 32.0) > 16.0)
|
||||
{
|
||||
gi.dprintf ("double ");
|
||||
ent->s.renderfx |= RF_SHELL_DOUBLE;
|
||||
}
|
||||
else
|
||||
ent->s.renderfx &= ~RF_SHELL_DOUBLE;
|
||||
|
||||
if (fmod (level.time, 64.0) > 32.0)
|
||||
{
|
||||
gi.dprintf ("half ");
|
||||
ent->s.renderfx |= RF_SHELL_HALF_DAM;
|
||||
}
|
||||
else
|
||||
ent->s.renderfx &= ~RF_SHELL_HALF_DAM;
|
||||
|
||||
gi.dprintf ("\n");
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
void M_MoveFrame (edict_t *self)
|
||||
{
|
||||
mmove_t *move;
|
||||
int index;
|
||||
|
||||
move = self->monsterinfo.currentmove;
|
||||
self->nextthink = level.time + FRAMETIME;
|
||||
|
||||
if ((self->monsterinfo.nextframe) && (self->monsterinfo.nextframe >= move->firstframe) && (self->monsterinfo.nextframe <= move->lastframe))
|
||||
{
|
||||
self->s.frame = self->monsterinfo.nextframe;
|
||||
self->monsterinfo.nextframe = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (self->s.frame == move->lastframe)
|
||||
{
|
||||
if (move->endfunc)
|
||||
{
|
||||
move->endfunc (self);
|
||||
|
||||
// regrab move, endfunc is very likely to change it
|
||||
move = self->monsterinfo.currentmove;
|
||||
|
||||
// check for death
|
||||
if (self->svflags & SVF_DEADMONSTER)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (self->s.frame < move->firstframe || self->s.frame > move->lastframe)
|
||||
{
|
||||
self->monsterinfo.aiflags &= ~AI_HOLD_FRAME;
|
||||
self->s.frame = move->firstframe;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!(self->monsterinfo.aiflags & AI_HOLD_FRAME))
|
||||
{
|
||||
self->s.frame++;
|
||||
if (self->s.frame > move->lastframe)
|
||||
self->s.frame = move->firstframe;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
index = self->s.frame - move->firstframe;
|
||||
|
||||
if (move->frame[index].aifunc)
|
||||
if (!(self->monsterinfo.aiflags & AI_HOLD_FRAME))
|
||||
move->frame[index].aifunc (self, move->frame[index].dist * self->monsterinfo.scale);
|
||||
else
|
||||
move->frame[index].aifunc (self, 0);
|
||||
|
||||
if (move->frame[index].thinkfunc)
|
||||
move->frame[index].thinkfunc (self);
|
||||
}
|
||||
|
||||
|
||||
void monster_think (edict_t *self)
|
||||
{
|
||||
M_MoveFrame (self);
|
||||
if (self->linkcount != self->monsterinfo.linkcount)
|
||||
{
|
||||
self->monsterinfo.linkcount = self->linkcount;
|
||||
M_CheckGround (self);
|
||||
}
|
||||
M_CatagorizePosition (self);
|
||||
M_WorldEffects (self);
|
||||
M_SetEffects (self);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
monster_use
|
||||
|
||||
Using a monster makes it angry at the current activator
|
||||
================
|
||||
*/
|
||||
void monster_use (edict_t *self, edict_t *other, edict_t *activator)
|
||||
{
|
||||
if (self->enemy)
|
||||
return;
|
||||
if (self->health <= 0)
|
||||
return;
|
||||
if (activator->flags & FL_NOTARGET)
|
||||
return;
|
||||
if (!(activator->client) && !(activator->monsterinfo.aiflags & AI_GOOD_GUY))
|
||||
return;
|
||||
if (activator->flags & FL_DISGUISED) // PGM
|
||||
return; // PGM
|
||||
|
||||
// delay reaction so if the monster is teleported, its sound is still heard
|
||||
self->enemy = activator;
|
||||
FoundTarget (self);
|
||||
}
|
||||
|
||||
|
||||
void monster_start_go (edict_t *self);
|
||||
|
||||
|
||||
void monster_triggered_spawn (edict_t *self)
|
||||
{
|
||||
self->s.origin[2] += 1;
|
||||
KillBox (self);
|
||||
|
||||
self->solid = SOLID_BBOX;
|
||||
self->movetype = MOVETYPE_STEP;
|
||||
self->svflags &= ~SVF_NOCLIENT;
|
||||
self->air_finished = level.time + 12;
|
||||
gi.linkentity (self);
|
||||
|
||||
monster_start_go (self);
|
||||
|
||||
if (self->enemy && !(self->spawnflags & 1) && !(self->enemy->flags & FL_NOTARGET))
|
||||
{
|
||||
if(!(self->enemy->flags & FL_DISGUISED)) // PGM
|
||||
FoundTarget (self);
|
||||
else // PMM - just in case, make sure to clear the enemy so FindTarget doesn't get confused
|
||||
self->enemy = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
self->enemy = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void monster_triggered_spawn_use (edict_t *self, edict_t *other, edict_t *activator)
|
||||
{
|
||||
// we have a one frame delay here so we don't telefrag the guy who activated us
|
||||
self->think = monster_triggered_spawn;
|
||||
self->nextthink = level.time + FRAMETIME;
|
||||
if (activator->client)
|
||||
self->enemy = activator;
|
||||
self->use = monster_use;
|
||||
}
|
||||
|
||||
void monster_triggered_start (edict_t *self)
|
||||
{
|
||||
self->solid = SOLID_NOT;
|
||||
self->movetype = MOVETYPE_NONE;
|
||||
self->svflags |= SVF_NOCLIENT;
|
||||
self->nextthink = 0;
|
||||
self->use = monster_triggered_spawn_use;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
monster_death_use
|
||||
|
||||
When a monster dies, it fires all of its targets with the current
|
||||
enemy as activator.
|
||||
================
|
||||
*/
|
||||
void monster_death_use (edict_t *self)
|
||||
{
|
||||
self->flags &= ~(FL_FLY|FL_SWIM);
|
||||
self->monsterinfo.aiflags &= AI_GOOD_GUY;
|
||||
|
||||
if (self->item)
|
||||
{
|
||||
Drop_Item (self, self->item);
|
||||
self->item = NULL;
|
||||
}
|
||||
|
||||
if (self->deathtarget)
|
||||
self->target = self->deathtarget;
|
||||
|
||||
if (!self->target)
|
||||
return;
|
||||
|
||||
G_UseTargets (self, self->enemy);
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
|
||||
qboolean monster_start (edict_t *self)
|
||||
{
|
||||
if (deathmatch->value)
|
||||
{
|
||||
G_FreeEdict (self);
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((self->spawnflags & 4) && !(self->monsterinfo.aiflags & AI_GOOD_GUY))
|
||||
{
|
||||
self->spawnflags &= ~4;
|
||||
self->spawnflags |= 1;
|
||||
// gi.dprintf("fixed spawnflags on %s at %s\n", self->classname, vtos(self->s.origin));
|
||||
}
|
||||
|
||||
if ((!(self->monsterinfo.aiflags & AI_GOOD_GUY)) && (!(self->monsterinfo.aiflags & AI_DO_NOT_COUNT)))
|
||||
level.total_monsters++;
|
||||
|
||||
self->nextthink = level.time + FRAMETIME;
|
||||
self->svflags |= SVF_MONSTER;
|
||||
self->s.renderfx |= RF_FRAMELERP;
|
||||
self->takedamage = DAMAGE_AIM;
|
||||
self->air_finished = level.time + 12;
|
||||
self->use = monster_use;
|
||||
self->max_health = self->health;
|
||||
self->clipmask = MASK_MONSTERSOLID;
|
||||
|
||||
self->s.skinnum = 0;
|
||||
self->deadflag = DEAD_NO;
|
||||
self->svflags &= ~SVF_DEADMONSTER;
|
||||
|
||||
if (!self->monsterinfo.checkattack)
|
||||
self->monsterinfo.checkattack = M_CheckAttack;
|
||||
VectorCopy (self->s.origin, self->s.old_origin);
|
||||
|
||||
if (st.item)
|
||||
{
|
||||
self->item = FindItemByClassname (st.item);
|
||||
if (!self->item)
|
||||
gi.dprintf("%s at %s has bad item: %s\n", self->classname, vtos(self->s.origin), st.item);
|
||||
}
|
||||
|
||||
// randomize what frame they start on
|
||||
if (self->monsterinfo.currentmove)
|
||||
self->s.frame = self->monsterinfo.currentmove->firstframe + (rand() % (self->monsterinfo.currentmove->lastframe - self->monsterinfo.currentmove->firstframe + 1));
|
||||
|
||||
// PMM - get this so I don't have to do it in all of the monsters
|
||||
self->monsterinfo.base_height = self->maxs[2];
|
||||
|
||||
// PMM - clear these
|
||||
self->monsterinfo.quad_framenum = 0;
|
||||
self->monsterinfo.double_framenum = 0;
|
||||
self->monsterinfo.invincible_framenum = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void monster_start_go (edict_t *self)
|
||||
{
|
||||
vec3_t v;
|
||||
|
||||
if (self->health <= 0)
|
||||
return;
|
||||
|
||||
// check for target to combat_point and change to combattarget
|
||||
if (self->target)
|
||||
{
|
||||
qboolean notcombat;
|
||||
qboolean fixup;
|
||||
edict_t *target;
|
||||
|
||||
target = NULL;
|
||||
notcombat = false;
|
||||
fixup = false;
|
||||
while ((target = G_Find (target, FOFS(targetname), self->target)) != NULL)
|
||||
{
|
||||
if (strcmp(target->classname, "point_combat") == 0)
|
||||
{
|
||||
self->combattarget = self->target;
|
||||
fixup = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
notcombat = true;
|
||||
}
|
||||
}
|
||||
if (notcombat && self->combattarget)
|
||||
gi.dprintf("%s at %s has target with mixed types\n", self->classname, vtos(self->s.origin));
|
||||
if (fixup)
|
||||
self->target = NULL;
|
||||
}
|
||||
|
||||
// validate combattarget
|
||||
if (self->combattarget)
|
||||
{
|
||||
edict_t *target;
|
||||
|
||||
target = NULL;
|
||||
while ((target = G_Find (target, FOFS(targetname), self->combattarget)) != NULL)
|
||||
{
|
||||
if (strcmp(target->classname, "point_combat") != 0)
|
||||
{
|
||||
gi.dprintf("%s at (%i %i %i) has a bad combattarget %s : %s at (%i %i %i)\n",
|
||||
self->classname, (int)self->s.origin[0], (int)self->s.origin[1], (int)self->s.origin[2],
|
||||
self->combattarget, target->classname, (int)target->s.origin[0], (int)target->s.origin[1],
|
||||
(int)target->s.origin[2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (self->target)
|
||||
{
|
||||
self->goalentity = self->movetarget = G_PickTarget(self->target);
|
||||
if (!self->movetarget)
|
||||
{
|
||||
gi.dprintf ("%s can't find target %s at %s\n", self->classname, self->target, vtos(self->s.origin));
|
||||
self->target = NULL;
|
||||
self->monsterinfo.pausetime = 100000000;
|
||||
self->monsterinfo.stand (self);
|
||||
}
|
||||
else if (strcmp (self->movetarget->classname, "path_corner") == 0)
|
||||
{
|
||||
VectorSubtract (self->goalentity->s.origin, self->s.origin, v);
|
||||
self->ideal_yaw = self->s.angles[YAW] = vectoyaw(v);
|
||||
self->monsterinfo.walk (self);
|
||||
self->target = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
self->goalentity = self->movetarget = NULL;
|
||||
self->monsterinfo.pausetime = 100000000;
|
||||
self->monsterinfo.stand (self);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
self->monsterinfo.pausetime = 100000000;
|
||||
self->monsterinfo.stand (self);
|
||||
}
|
||||
|
||||
self->think = monster_think;
|
||||
self->nextthink = level.time + FRAMETIME;
|
||||
}
|
||||
|
||||
|
||||
void walkmonster_start_go (edict_t *self)
|
||||
{
|
||||
if (!(self->spawnflags & 2) && level.time < 1)
|
||||
{
|
||||
M_droptofloor (self);
|
||||
|
||||
if (self->groundentity)
|
||||
if (!M_walkmove (self, 0, 0))
|
||||
gi.dprintf ("%s in solid at %s\n", self->classname, vtos(self->s.origin));
|
||||
}
|
||||
|
||||
if (!self->yaw_speed)
|
||||
self->yaw_speed = 20;
|
||||
// PMM - stalkers are too short for this
|
||||
if (!(strcmp(self->classname, "monster_stalker")))
|
||||
self->viewheight = 15;
|
||||
else
|
||||
self->viewheight = 25;
|
||||
|
||||
monster_start_go (self);
|
||||
|
||||
if (self->spawnflags & 2)
|
||||
monster_triggered_start (self);
|
||||
}
|
||||
|
||||
void walkmonster_start (edict_t *self)
|
||||
{
|
||||
self->think = walkmonster_start_go;
|
||||
monster_start (self);
|
||||
}
|
||||
|
||||
|
||||
void flymonster_start_go (edict_t *self)
|
||||
{
|
||||
if (!M_walkmove (self, 0, 0))
|
||||
gi.dprintf ("%s in solid at %s\n", self->classname, vtos(self->s.origin));
|
||||
|
||||
if (!self->yaw_speed)
|
||||
self->yaw_speed = 10;
|
||||
self->viewheight = 25;
|
||||
|
||||
monster_start_go (self);
|
||||
|
||||
if (self->spawnflags & 2)
|
||||
monster_triggered_start (self);
|
||||
}
|
||||
|
||||
|
||||
void flymonster_start (edict_t *self)
|
||||
{
|
||||
self->flags |= FL_FLY;
|
||||
self->think = flymonster_start_go;
|
||||
monster_start (self);
|
||||
}
|
||||
|
||||
|
||||
void swimmonster_start_go (edict_t *self)
|
||||
{
|
||||
if (!self->yaw_speed)
|
||||
self->yaw_speed = 10;
|
||||
self->viewheight = 10;
|
||||
|
||||
monster_start_go (self);
|
||||
|
||||
if (self->spawnflags & 2)
|
||||
monster_triggered_start (self);
|
||||
}
|
||||
|
||||
void swimmonster_start (edict_t *self)
|
||||
{
|
||||
self->flags |= FL_SWIM;
|
||||
self->think = swimmonster_start_go;
|
||||
monster_start (self);
|
||||
}
|
||||
|
||||
//ROGUE
|
||||
|
||||
void stationarymonster_start_go (edict_t *self);
|
||||
|
||||
void stationarymonster_triggered_spawn (edict_t *self)
|
||||
{
|
||||
KillBox (self);
|
||||
|
||||
self->solid = SOLID_BBOX;
|
||||
self->movetype = MOVETYPE_NONE;
|
||||
self->svflags &= ~SVF_NOCLIENT;
|
||||
self->air_finished = level.time + 12;
|
||||
gi.linkentity (self);
|
||||
|
||||
// FIXME - why doesn't this happen with real monsters?
|
||||
self->spawnflags &= ~2;
|
||||
|
||||
stationarymonster_start_go (self);
|
||||
|
||||
if (self->enemy && !(self->spawnflags & 1) && !(self->enemy->flags & FL_NOTARGET))
|
||||
{
|
||||
if(!(self->enemy->flags & FL_DISGUISED)) // PGM
|
||||
FoundTarget (self);
|
||||
else // PMM - just in case, make sure to clear the enemy so FindTarget doesn't get confused
|
||||
self->enemy = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
self->enemy = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void stationarymonster_triggered_spawn_use (edict_t *self, edict_t *other, edict_t *activator)
|
||||
{
|
||||
// we have a one frame delay here so we don't telefrag the guy who activated us
|
||||
self->think = stationarymonster_triggered_spawn;
|
||||
self->nextthink = level.time + FRAMETIME;
|
||||
if (activator->client)
|
||||
self->enemy = activator;
|
||||
self->use = monster_use;
|
||||
}
|
||||
|
||||
void stationarymonster_triggered_start (edict_t *self)
|
||||
{
|
||||
self->solid = SOLID_NOT;
|
||||
self->movetype = MOVETYPE_NONE;
|
||||
self->svflags |= SVF_NOCLIENT;
|
||||
self->nextthink = 0;
|
||||
self->use = stationarymonster_triggered_spawn_use;
|
||||
}
|
||||
|
||||
void stationarymonster_start_go (edict_t *self)
|
||||
{
|
||||
// PGM - only turrets use this, so remove the error message. They're supposed to be in solid.
|
||||
|
||||
// if (!M_walkmove (self, 0, 0))
|
||||
// gi.dprintf ("%s in solid at %s\n", self->classname, vtos(self->s.origin));
|
||||
|
||||
if (!self->yaw_speed)
|
||||
self->yaw_speed = 20;
|
||||
// self->viewheight = 25;
|
||||
|
||||
monster_start_go (self);
|
||||
|
||||
if (self->spawnflags & 2)
|
||||
stationarymonster_triggered_start (self);
|
||||
}
|
||||
|
||||
void stationarymonster_start (edict_t *self)
|
||||
{
|
||||
self->think = stationarymonster_start_go;
|
||||
monster_start (self);
|
||||
}
|
||||
|
||||
void monster_done_dodge (edict_t *self)
|
||||
{
|
||||
// if ((g_showlogic) && (g_showlogic->value))
|
||||
// gi.dprintf ("%s done dodging\n", self->classname);
|
||||
self->monsterinfo.aiflags &= ~AI_DODGING;
|
||||
}
|
||||
//ROGUE
|
384
g_newdm.c
Normal file
384
g_newdm.c
Normal file
|
@ -0,0 +1,384 @@
|
|||
// g_newdm.c
|
||||
// pmack
|
||||
// june 1998
|
||||
|
||||
#include "g_local.h"
|
||||
#include "m_player.h"
|
||||
|
||||
dm_game_rt DMGame;
|
||||
|
||||
// ****************************
|
||||
// General DM Stuff
|
||||
// ****************************
|
||||
|
||||
void InitGameRules(void)
|
||||
{
|
||||
int gameNum;
|
||||
|
||||
// clear out the game rule structure before we start
|
||||
memset(&DMGame, 0, sizeof(dm_game_rt));
|
||||
|
||||
if(gamerules && gamerules->value)
|
||||
{
|
||||
gameNum = gamerules->value;
|
||||
switch(gameNum)
|
||||
{
|
||||
case RDM_TAG:
|
||||
DMGame.GameInit = Tag_GameInit;
|
||||
DMGame.PostInitSetup = Tag_PostInitSetup;
|
||||
DMGame.PlayerDeath = Tag_PlayerDeath;
|
||||
DMGame.Score = Tag_Score;
|
||||
DMGame.PlayerEffects = Tag_PlayerEffects;
|
||||
DMGame.DogTag = Tag_DogTag;
|
||||
DMGame.PlayerDisconnect = Tag_PlayerDisconnect;
|
||||
DMGame.ChangeDamage = Tag_ChangeDamage;
|
||||
break;
|
||||
/*
|
||||
case RDM_DEATHBALL:
|
||||
DMGame.GameInit = DBall_GameInit;
|
||||
DMGame.ChangeKnockback = DBall_ChangeKnockback;
|
||||
DMGame.ChangeDamage = DBall_ChangeDamage;
|
||||
DMGame.ClientBegin = DBall_ClientBegin;
|
||||
DMGame.SelectSpawnPoint = DBall_SelectSpawnPoint;
|
||||
DMGame.PostInitSetup = DBall_PostInitSetup;
|
||||
DMGame.CheckDMRules = DBall_CheckDMRules;
|
||||
break;
|
||||
*/
|
||||
// reset gamerules if it's not a valid number
|
||||
default:
|
||||
gamerules->value = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// if we're set up to play, initialize the game as needed.
|
||||
if(DMGame.GameInit)
|
||||
DMGame.GameInit();
|
||||
}
|
||||
|
||||
//=================
|
||||
//=================
|
||||
#define IT_TYPE_MASK (IT_WEAPON|IT_AMMO|IT_POWERUP|IT_ARMOR|IT_KEY)
|
||||
|
||||
extern void ED_CallSpawn (edict_t *ent);
|
||||
extern qboolean Pickup_Health (edict_t *ent, edict_t *other);
|
||||
extern qboolean Pickup_Adrenaline (edict_t *ent, edict_t *other);
|
||||
extern qboolean Pickup_Armor (edict_t *ent, edict_t *other);
|
||||
extern qboolean Pickup_PowerArmor (edict_t *ent, edict_t *other);
|
||||
|
||||
char *FindSubstituteItem (edict_t *ent)
|
||||
{
|
||||
int i;
|
||||
int itflags, myflags;
|
||||
float rnd;
|
||||
int count;
|
||||
int pick;
|
||||
gitem_t *it;
|
||||
|
||||
// there are only two classes of power armor, and we don't want
|
||||
// to give out power screens. therefore, power shields should
|
||||
// remain power shields. (powerscreens shouldn't be there at all...)
|
||||
if (ent->item->pickup == Pickup_PowerArmor)
|
||||
return NULL;
|
||||
|
||||
// health is special case
|
||||
if ((ent->item->pickup == Pickup_Health) || (ent->item->pickup == Pickup_Adrenaline))
|
||||
{
|
||||
// health pellets stay health pellets
|
||||
if(!strcmp(ent->classname, "item_health_small"))
|
||||
return NULL;
|
||||
|
||||
rnd = random();
|
||||
if(rnd < 0.6)
|
||||
return "item_health";
|
||||
else if(rnd < 0.9)
|
||||
return "item_health_large";
|
||||
else if(rnd < 0.99)
|
||||
return "item_adrenaline";
|
||||
else
|
||||
return "item_health_mega";
|
||||
}
|
||||
// armor is also special case
|
||||
else if(ent->item->pickup == Pickup_Armor)
|
||||
{
|
||||
// armor shards stay armor shards
|
||||
if (ent->item->tag == ARMOR_SHARD)
|
||||
return NULL;
|
||||
|
||||
rnd = random();
|
||||
if(rnd < 0.6)
|
||||
return "item_armor_jacket";
|
||||
else if(rnd < 0.9)
|
||||
return "item_armor_combat";
|
||||
else
|
||||
return "item_armor_body";
|
||||
}
|
||||
|
||||
|
||||
// we want to stay within the item class
|
||||
myflags = ent->item->flags & IT_TYPE_MASK;
|
||||
if ((myflags & IT_AMMO) && (myflags & IT_WEAPON))
|
||||
myflags = IT_AMMO;
|
||||
|
||||
count = 0;
|
||||
|
||||
// first pass, count the matching items
|
||||
it = itemlist;
|
||||
for (i=0 ; i<game.num_items ; i++, it++)
|
||||
{
|
||||
itflags = it->flags;
|
||||
|
||||
if (!itflags || (itflags & IT_NOT_GIVEABLE))
|
||||
continue;
|
||||
|
||||
// prox,grenades,etc should count as ammo.
|
||||
if ((itflags & IT_AMMO) && (itflags & IT_WEAPON))
|
||||
itflags = IT_AMMO;
|
||||
|
||||
// don't respawn spheres if they're dmflag disabled.
|
||||
if ( (int)dmflags->value & DF_NO_SPHERES )
|
||||
{
|
||||
if (!strcmp (ent->classname, "item_sphere_vengeance") ||
|
||||
!strcmp (ent->classname, "item_sphere_hunter") ||
|
||||
!strcmp (ent->classname, "item_spehre_defender"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ((int)dmflags->value & DF_NO_NUKES) && !strcmp(ent->classname, "ammo_nuke") )
|
||||
continue;
|
||||
|
||||
if ( ((int)dmflags->value & DF_NO_MINES) &&
|
||||
(!strcmp(ent->classname, "ammo_prox") || !strcmp(ent->classname, "ammo_tesla")))
|
||||
continue;
|
||||
|
||||
if ((itflags & IT_TYPE_MASK) == (myflags & IT_TYPE_MASK))
|
||||
count++;
|
||||
}
|
||||
|
||||
if(!count)
|
||||
return NULL;
|
||||
|
||||
pick = ceil(random() * count);
|
||||
count = 0;
|
||||
|
||||
// second pass, pick one.
|
||||
it = itemlist;
|
||||
for (i=0 ; i<game.num_items ; i++, it++)
|
||||
{
|
||||
itflags = it->flags;
|
||||
|
||||
if (!itflags || (itflags & IT_NOT_GIVEABLE))
|
||||
continue;
|
||||
|
||||
// prox,grenades,etc should count as ammo.
|
||||
if ((itflags & IT_AMMO) && (itflags & IT_WEAPON))
|
||||
itflags = IT_AMMO;
|
||||
|
||||
if ( ((int)dmflags->value & DF_NO_NUKES) && !strcmp(ent->classname, "ammo_nuke") )
|
||||
continue;
|
||||
|
||||
if ( ((int)dmflags->value & DF_NO_MINES) &&
|
||||
(!strcmp(ent->classname, "ammo_prox") || !strcmp(ent->classname, "ammo_tesla")))
|
||||
continue;
|
||||
|
||||
if ((itflags & IT_TYPE_MASK) == (myflags & IT_TYPE_MASK))
|
||||
{
|
||||
count++;
|
||||
if(pick == count)
|
||||
return it->classname;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//=================
|
||||
//=================
|
||||
edict_t *DoRandomRespawn (edict_t *ent)
|
||||
{
|
||||
edict_t *newEnt;
|
||||
char *classname;
|
||||
|
||||
classname = FindSubstituteItem (ent);
|
||||
if (classname == NULL)
|
||||
return NULL;
|
||||
|
||||
gi.unlinkentity (ent);
|
||||
|
||||
newEnt = G_Spawn();
|
||||
newEnt->classname = classname;
|
||||
VectorCopy (ent->s.origin, newEnt->s.origin);
|
||||
VectorCopy (ent->s.old_origin, newEnt->s.old_origin);
|
||||
VectorCopy (ent->mins, newEnt->mins);
|
||||
VectorCopy (ent->maxs, newEnt->maxs);
|
||||
|
||||
VectorSet (newEnt->gravityVector, 0, 0, -1);
|
||||
|
||||
ED_CallSpawn (newEnt);
|
||||
|
||||
newEnt->s.renderfx |= RF_IR_VISIBLE;
|
||||
|
||||
return newEnt;
|
||||
}
|
||||
|
||||
//=================
|
||||
//=================
|
||||
void PrecacheForRandomRespawn (void)
|
||||
{
|
||||
gitem_t *it;
|
||||
int i;
|
||||
int itflags;
|
||||
|
||||
it = itemlist;
|
||||
for (i=0 ; i<game.num_items ; i++, it++)
|
||||
{
|
||||
itflags = it->flags;
|
||||
|
||||
if (!itflags || (itflags & IT_NOT_GIVEABLE))
|
||||
continue;
|
||||
|
||||
PrecacheItem(it);
|
||||
}
|
||||
}
|
||||
|
||||
// ***************************
|
||||
// DOPPLEGANGER
|
||||
// ***************************
|
||||
|
||||
extern edict_t *Sphere_Spawn (edict_t *owner, int spawnflags);
|
||||
|
||||
void fire_doppleganger (edict_t *ent, vec3_t start, vec3_t aimdir);
|
||||
void doppleganger_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point);
|
||||
|
||||
void doppleganger_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
|
||||
{
|
||||
edict_t *sphere;
|
||||
float dist;
|
||||
vec3_t dir;
|
||||
|
||||
if((self->enemy) && (self->enemy != self->teammaster))
|
||||
{
|
||||
VectorSubtract(self->enemy->s.origin, self->s.origin, dir);
|
||||
dist = VectorLength(dir);
|
||||
|
||||
if(dist > 768)
|
||||
{
|
||||
sphere = Sphere_Spawn (self, SPHERE_HUNTER | SPHERE_DOPPLEGANGER);
|
||||
sphere->pain(sphere, attacker, 0, 0);
|
||||
}
|
||||
else //if(dist > 256)
|
||||
{
|
||||
sphere = Sphere_Spawn (self, SPHERE_VENGEANCE | SPHERE_DOPPLEGANGER);
|
||||
sphere->pain(sphere, attacker, 0, 0);
|
||||
}
|
||||
// else
|
||||
// {
|
||||
// T_RadiusClassDamage (self, self->teammaster, 175, "doppleganger", 384, MOD_DOPPLE_EXPLODE);
|
||||
// }
|
||||
}
|
||||
|
||||
if(self->teamchain)
|
||||
BecomeExplosion1(self->teamchain);
|
||||
BecomeExplosion1(self);
|
||||
}
|
||||
|
||||
void doppleganger_pain (edict_t *self, edict_t *other, float kick, int damage)
|
||||
{
|
||||
self->enemy = other;
|
||||
}
|
||||
|
||||
void doppleganger_timeout (edict_t *self)
|
||||
{
|
||||
// T_RadiusClassDamage (self, self->teammaster, 140, "doppleganger", 256, MOD_DOPPLE_EXPLODE);
|
||||
|
||||
if(self->teamchain)
|
||||
BecomeExplosion1(self->teamchain);
|
||||
BecomeExplosion1(self);
|
||||
}
|
||||
|
||||
void body_think (edict_t *self)
|
||||
{
|
||||
float r;
|
||||
|
||||
if(abs(self->ideal_yaw - anglemod(self->s.angles[YAW])) < 2)
|
||||
{
|
||||
if(self->timestamp < level.time)
|
||||
{
|
||||
r = random();
|
||||
if(r < 0.10)
|
||||
{
|
||||
self->ideal_yaw = random() * 350.0;
|
||||
self->timestamp = level.time + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
M_ChangeYaw(self);
|
||||
|
||||
self->s.frame ++;
|
||||
if (self->s.frame > FRAME_stand40)
|
||||
self->s.frame = FRAME_stand01;
|
||||
|
||||
self->nextthink = level.time + 0.1;
|
||||
}
|
||||
|
||||
void fire_doppleganger (edict_t *ent, vec3_t start, vec3_t aimdir)
|
||||
{
|
||||
edict_t *base;
|
||||
edict_t *body;
|
||||
vec3_t dir;
|
||||
vec3_t forward, right, up;
|
||||
int number;
|
||||
|
||||
vectoangles2 (aimdir, dir);
|
||||
AngleVectors (dir, forward, right, up);
|
||||
|
||||
base = G_Spawn();
|
||||
VectorCopy (start, base->s.origin);
|
||||
VectorCopy (dir, base->s.angles);
|
||||
VectorClear (base->velocity);
|
||||
VectorClear (base->avelocity);
|
||||
base->movetype = MOVETYPE_TOSS;
|
||||
base->solid = SOLID_BBOX;
|
||||
base->s.renderfx |= RF_IR_VISIBLE;
|
||||
base->s.angles[PITCH]=0;
|
||||
VectorSet (base->mins, -16, -16, -24);
|
||||
VectorSet (base->maxs, 16, 16, 32);
|
||||
// base->s.modelindex = gi.modelindex ("models/objects/dopplebase/tris.md2");
|
||||
base->s.modelindex = 0;
|
||||
base->teammaster = ent;
|
||||
base->svflags |= SVF_DAMAGEABLE;
|
||||
base->takedamage = DAMAGE_AIM;
|
||||
base->health = 30;
|
||||
base->pain = doppleganger_pain;
|
||||
base->die = doppleganger_die;
|
||||
|
||||
// FIXME - remove with style
|
||||
base->nextthink = level.time + 30;
|
||||
base->think = doppleganger_timeout;
|
||||
|
||||
base->classname = "doppleganger";
|
||||
|
||||
gi.linkentity (base);
|
||||
|
||||
body = G_Spawn();
|
||||
number = body->s.number;
|
||||
body->s = ent->s;
|
||||
body->s.sound = 0;
|
||||
body->s.event = 0;
|
||||
// body->s.modelindex2 = 0; // no attached items (CTF flag, etc)
|
||||
body->s.number = number;
|
||||
body->yaw_speed = 30;
|
||||
body->ideal_yaw = 0;
|
||||
VectorCopy (start, body->s.origin);
|
||||
body->s.origin[2] += 8;
|
||||
body->think = body_think;
|
||||
body->nextthink = level.time + FRAMETIME;
|
||||
gi.linkentity (body);
|
||||
|
||||
base->teamchain = body;
|
||||
body->teammaster = base;
|
||||
}
|
||||
|
347
g_newfnc.c
Normal file
347
g_newfnc.c
Normal file
|
@ -0,0 +1,347 @@
|
|||
#include "g_local.h"
|
||||
|
||||
//void plat_CalcMove (edict_t *ent, vec3_t dest, void(*func)(edict_t*));
|
||||
void Move_Calc (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
|
||||
|
||||
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;
|
||||
|
||||
// trigger all paired doors
|
||||
for (ent = self ; ent ; ent = ent->teamchain)
|
||||
Move_Calc(ent, ent->moveinfo.start_origin, fd_secret_move1);
|
||||
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// Start moving sideways w/sound...
|
||||
void fd_secret_move2(edict_t *self)
|
||||
{
|
||||
// gi.dprintf("fd_secret_move2\n");
|
||||
Move_Calc(self, self->moveinfo.end_origin, 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;
|
||||
}
|
||||
}
|
||||
|
||||
// Move backward...
|
||||
void fd_secret_move4(edict_t *self)
|
||||
{
|
||||
// gi.dprintf("fd_secret_move4\n");
|
||||
Move_Calc(self, self->moveinfo.start_origin, 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;
|
||||
}
|
||||
|
||||
void fd_secret_move6(edict_t *self)
|
||||
{
|
||||
// gi.dprintf("fd_secret_move6\n");
|
||||
Move_Calc(self, self->move_origin, 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;
|
||||
}
|
||||
}
|
||||
|
||||
void secret_blocked(edict_t *self, edict_t *other)
|
||||
{
|
||||
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))
|
||||
return;
|
||||
|
||||
if (self->monsterinfo.attack_finished > level.time)
|
||||
return;
|
||||
|
||||
self->monsterinfo.attack_finished = level.time + 2;
|
||||
|
||||
if (self->message)
|
||||
{
|
||||
gi.centerprintf (other, self->message);
|
||||
// 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 = not implemented yet
|
||||
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
|
||||
reverse_left = the sideways move will be to right of arrow
|
||||
reverse_back = the to/fro move will be forward
|
||||
|
||||
VALUES:
|
||||
wait = # of seconds before coming back (5 default)
|
||||
dmg = damage to inflict when blocked (2 default)
|
||||
|
||||
*/
|
||||
|
||||
void SP_func_door_secret2 (edict_t *ent)
|
||||
{
|
||||
vec3_t forward,right,up;
|
||||
float lrSize, fbSize;
|
||||
|
||||
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");
|
||||
|
||||
if (!ent->dmg)
|
||||
ent->dmg = 2;
|
||||
|
||||
AngleVectors(ent->s.angles, forward, right, up);
|
||||
VectorCopy(ent->s.origin, ent->move_origin);
|
||||
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)
|
||||
{
|
||||
lrSize = ent->size[1];
|
||||
fbSize = ent->size[0];
|
||||
}
|
||||
else if(ent->move_angles[1] == 90 || ent->move_angles[1] == 270)
|
||||
{
|
||||
lrSize = ent->size[0];
|
||||
fbSize = ent->size[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
gi.dprintf("Secret door not at 0,90,180,270!\n");
|
||||
}
|
||||
|
||||
if(ent->spawnflags & SEC_MOVE_FORWARD)
|
||||
VectorScale(forward, fbSize, forward);
|
||||
else
|
||||
{
|
||||
VectorScale(forward, fbSize * -1 , forward);
|
||||
}
|
||||
|
||||
if(ent->spawnflags & SEC_MOVE_RIGHT)
|
||||
VectorScale(right, lrSize, right);
|
||||
else
|
||||
{
|
||||
VectorScale(right, lrSize * -1, right);
|
||||
}
|
||||
|
||||
if(ent->spawnflags & SEC_1ST_DOWN)
|
||||
{
|
||||
VectorAdd(ent->s.origin, forward, ent->moveinfo.start_origin);
|
||||
VectorAdd(ent->moveinfo.start_origin, right, ent->moveinfo.end_origin);
|
||||
}
|
||||
else
|
||||
{
|
||||
VectorAdd(ent->s.origin, right, ent->moveinfo.start_origin);
|
||||
VectorAdd(ent->moveinfo.start_origin, forward, 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;
|
||||
|
||||
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
|
||||
|
||||
gi.linkentity(ent);
|
||||
}
|
||||
|
||||
// ==================================================
|
||||
|
||||
#define FWALL_START_ON 1
|
||||
|
||||
void force_wall_think(edict_t *self)
|
||||
{
|
||||
if(!self->wait)
|
||||
{
|
||||
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 + 0.1;
|
||||
}
|
||||
|
||||
void force_wall_use (edict_t *self, edict_t *other, edict_t *activator)
|
||||
{
|
||||
if(!self->wait)
|
||||
{
|
||||
self->wait = 1;
|
||||
self->think = NULL;
|
||||
self->nextthink = 0;
|
||||
self->solid = SOLID_NOT;
|
||||
gi.linkentity( self );
|
||||
}
|
||||
else
|
||||
{
|
||||
self->wait = 0;
|
||||
self->think = force_wall_think;
|
||||
self->nextthink = level.time + 0.1;
|
||||
self->solid = SOLID_BSP;
|
||||
KillBox(self); // Is this appropriate?
|
||||
gi.linkentity (self);
|
||||
}
|
||||
}
|
||||
|
||||
/*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.
|
||||
208: green, 240: red, 241: blue, 224: orange
|
||||
*/
|
||||
void SP_func_force_wall(edict_t *ent)
|
||||
{
|
||||
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 + 0.1;
|
||||
}
|
||||
else
|
||||
ent->solid = SOLID_NOT;
|
||||
|
||||
ent->use = force_wall_use;
|
||||
|
||||
ent->svflags = SVF_NOCLIENT;
|
||||
|
||||
gi.linkentity(ent);
|
||||
}
|
352
g_newtarg.c
Normal file
352
g_newtarg.c
Normal file
|
@ -0,0 +1,352 @@
|
|||
#include "g_local.h"
|
||||
|
||||
//==========================================================
|
||||
|
||||
/*QUAKED target_steam (1 0 0) (-8 -8 -8) (8 8 8)
|
||||
Creates a steam effect (particles w/ velocity in a line).
|
||||
|
||||
speed = velocity of particles (default 50)
|
||||
count = number of particles (default 32)
|
||||
sounds = color of particles (default 8 for steam)
|
||||
the color range is from this color to this color + 6
|
||||
wait = seconds to run before stopping (overrides default
|
||||
value derived from func_timer)
|
||||
|
||||
best way to use this is to tie it to a func_timer that "pokes"
|
||||
it every second (or however long you set the wait time, above)
|
||||
|
||||
note that the width of the base is proportional to the speed
|
||||
good colors to use:
|
||||
6-9 - varying whites (darker to brighter)
|
||||
224 - sparks
|
||||
176 - blue water
|
||||
80 - brown water
|
||||
208 - slime
|
||||
232 - blood
|
||||
*/
|
||||
|
||||
void use_target_steam (edict_t *self, edict_t *other, edict_t *activator)
|
||||
{
|
||||
// FIXME - this needs to be a global
|
||||
static int nextid;
|
||||
vec3_t point;
|
||||
|
||||
if (nextid > 20000)
|
||||
nextid = nextid %20000;
|
||||
|
||||
nextid++;
|
||||
|
||||
// automagically set wait from func_timer unless they set it already, or
|
||||
// default to 1000 if not called by a func_timer (eek!)
|
||||
if (!self->wait)
|
||||
if (other)
|
||||
self->wait = other->wait * 1000;
|
||||
else
|
||||
self->wait = 1000;
|
||||
|
||||
if (self->enemy)
|
||||
{
|
||||
VectorMA (self->enemy->absmin, 0.5, self->enemy->size, point);
|
||||
VectorSubtract (point, self->s.origin, self->movedir);
|
||||
VectorNormalize (self->movedir);
|
||||
}
|
||||
|
||||
VectorMA (self->s.origin, self->plat2flags*0.5, self->movedir, point);
|
||||
if (self->wait > 100)
|
||||
{
|
||||
gi.WriteByte (svc_temp_entity);
|
||||
gi.WriteByte (TE_STEAM);
|
||||
gi.WriteShort (nextid);
|
||||
gi.WriteByte (self->count);
|
||||
gi.WritePosition (self->s.origin);
|
||||
gi.WriteDir (self->movedir);
|
||||
gi.WriteByte (self->sounds&0xff);
|
||||
gi.WriteShort ( (short int)(self->plat2flags) );
|
||||
gi.WriteLong ( (int)(self->wait) );
|
||||
gi.multicast (self->s.origin, MULTICAST_PVS);
|
||||
}
|
||||
else
|
||||
{
|
||||
gi.WriteByte (svc_temp_entity);
|
||||
gi.WriteByte (TE_STEAM);
|
||||
gi.WriteShort ((short int)-1);
|
||||
gi.WriteByte (self->count);
|
||||
gi.WritePosition (self->s.origin);
|
||||
gi.WriteDir (self->movedir);
|
||||
gi.WriteByte (self->sounds&0xff);
|
||||
gi.WriteShort ( (short int)(self->plat2flags) );
|
||||
gi.multicast (self->s.origin, MULTICAST_PVS);
|
||||
}
|
||||
}
|
||||
|
||||
void target_steam_start (edict_t *self)
|
||||
{
|
||||
edict_t *ent;
|
||||
|
||||
self->use = use_target_steam;
|
||||
|
||||
if (self->target)
|
||||
{
|
||||
ent = G_Find (NULL, FOFS(targetname), self->target);
|
||||
if (!ent)
|
||||
gi.dprintf ("%s at %s: %s is a bad target\n", self->classname, vtos(self->s.origin), self->target);
|
||||
self->enemy = ent;
|
||||
}
|
||||
else
|
||||
{
|
||||
G_SetMovedir (self->s.angles, self->movedir);
|
||||
}
|
||||
|
||||
if (!self->count)
|
||||
self->count = 32;
|
||||
if (!self->plat2flags)
|
||||
self->plat2flags = 75;
|
||||
if (!self->sounds)
|
||||
self->sounds = 8;
|
||||
if (self->wait)
|
||||
self->wait *= 1000; // we want it in milliseconds, not seconds
|
||||
|
||||
// paranoia is good
|
||||
self->sounds &= 0xff;
|
||||
self->count &= 0xff;
|
||||
|
||||
self->svflags = SVF_NOCLIENT;
|
||||
|
||||
gi.linkentity (self);
|
||||
}
|
||||
|
||||
void SP_target_steam (edict_t *self)
|
||||
{
|
||||
self->plat2flags = self->speed;
|
||||
|
||||
if (self->target)
|
||||
{
|
||||
self->think = target_steam_start;
|
||||
self->nextthink = level.time + 1;
|
||||
}
|
||||
else
|
||||
target_steam_start (self);
|
||||
}
|
||||
|
||||
|
||||
//==========================================================
|
||||
// target_anger
|
||||
//==========================================================
|
||||
|
||||
void target_anger_use (edict_t *self, edict_t *other, edict_t *activator)
|
||||
{
|
||||
edict_t *target;
|
||||
edict_t *t;
|
||||
|
||||
t = NULL;
|
||||
target = G_Find (t, FOFS(targetname), self->killtarget);
|
||||
|
||||
if (target && self->target)
|
||||
{
|
||||
// Make whatever a "good guy" so the monster will try to kill it!
|
||||
target->monsterinfo.aiflags |= AI_GOOD_GUY;
|
||||
target->svflags |= SVF_MONSTER;
|
||||
target->health = 300;
|
||||
|
||||
t = NULL;
|
||||
while ((t = G_Find (t, FOFS(targetname), self->target)))
|
||||
{
|
||||
if (t == self)
|
||||
{
|
||||
gi.dprintf ("WARNING: entity used itself.\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (t->use)
|
||||
{
|
||||
if (t->health < 0)
|
||||
{
|
||||
// if ((g_showlogic) && (g_showlogic->value))
|
||||
// gi.dprintf ("target_anger with dead monster!\n");
|
||||
return;
|
||||
}
|
||||
t->enemy = target;
|
||||
t->monsterinfo.aiflags |= AI_TARGET_ANGER;
|
||||
FoundTarget (t);
|
||||
}
|
||||
}
|
||||
if (!self->inuse)
|
||||
{
|
||||
gi.dprintf("entity was removed while using targets\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*QUAKED target_anger (1 0 0) (-8 -8 -8) (8 8 8)
|
||||
This trigger will cause an entity to be angry at another entity when a player touches it. Target the
|
||||
entity you want to anger, and killtarget the entity you want it to be angry at.
|
||||
|
||||
target - entity to piss off
|
||||
killtarget - entity to be pissed off at
|
||||
*/
|
||||
void SP_target_anger (edict_t *self)
|
||||
{
|
||||
if (!self->target)
|
||||
{
|
||||
gi.dprintf("target_anger without target!\n");
|
||||
G_FreeEdict (self);
|
||||
return;
|
||||
}
|
||||
if (!self->killtarget)
|
||||
{
|
||||
gi.dprintf("target_anger without killtarget!\n");
|
||||
G_FreeEdict (self);
|
||||
return;
|
||||
}
|
||||
|
||||
self->use = target_anger_use;
|
||||
self->svflags = SVF_NOCLIENT;
|
||||
}
|
||||
|
||||
// ================
|
||||
// target_spawn
|
||||
// ================
|
||||
/*
|
||||
extern edict_t *CreateMonster(vec3_t origin, vec3_t angles, char *classname);
|
||||
|
||||
void target_spawn_use (edict_t *self, edict_t *other, edict_t *activator)
|
||||
{
|
||||
edict_t *newEnt;
|
||||
|
||||
newEnt = CreateMonster (self->s.origin, self->s.angles, "monster_infantry");
|
||||
if(newEnt)
|
||||
newEnt->enemy = other;
|
||||
}
|
||||
*/
|
||||
|
||||
/*Q U AKED target_spawn (1 0 0) (-32 -32 -24) (32 32 72)
|
||||
*/
|
||||
/*
|
||||
void SP_target_spawn (edict_t *self)
|
||||
{
|
||||
self->use = target_spawn_use;
|
||||
self->svflags = SVF_NOCLIENT;
|
||||
}
|
||||
*/
|
||||
|
||||
// ***********************************
|
||||
// target_killplayers
|
||||
// ***********************************
|
||||
|
||||
void target_killplayers_use (edict_t *self, edict_t *other, edict_t *activator)
|
||||
{
|
||||
int i;
|
||||
edict_t *ent, *player;
|
||||
|
||||
// kill the players
|
||||
for (i=0 ; i<game.maxclients ; i++)
|
||||
{
|
||||
player = &g_edicts[1+i];
|
||||
if (!player->inuse)
|
||||
continue;
|
||||
|
||||
// nail it
|
||||
T_Damage (player, self, self, vec3_origin, self->s.origin, vec3_origin, 100000, 0, DAMAGE_NO_PROTECTION, MOD_TELEFRAG);
|
||||
}
|
||||
|
||||
// kill any visible monsters
|
||||
for (ent = g_edicts; ent < &g_edicts[globals.num_edicts] ; ent++)
|
||||
{
|
||||
if (!ent->inuse)
|
||||
continue;
|
||||
if (ent->health < 1)
|
||||
continue;
|
||||
if (!ent->takedamage)
|
||||
continue;
|
||||
|
||||
for(i=0;i<game.maxclients ; i++)
|
||||
{
|
||||
player = &g_edicts[1+i];
|
||||
if(!player->inuse)
|
||||
continue;
|
||||
|
||||
if(visible(player, ent))
|
||||
{
|
||||
T_Damage (ent, self, self, vec3_origin, ent->s.origin, vec3_origin,
|
||||
ent->health, 0, DAMAGE_NO_PROTECTION, MOD_TELEFRAG);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*QUAKED target_killplayers (1 0 0) (-8 -8 -8) (8 8 8)
|
||||
When triggered, this will kill all the players on the map.
|
||||
*/
|
||||
void SP_target_killplayers (edict_t *self)
|
||||
{
|
||||
self->use = target_killplayers_use;
|
||||
self->svflags = SVF_NOCLIENT;
|
||||
}
|
||||
|
||||
/*QUAKED target_blacklight (1 0 1) (-16 -16 -24) (16 16 24)
|
||||
Pulsing black light with sphere in the center
|
||||
*/
|
||||
void blacklight_think (edict_t *self)
|
||||
{
|
||||
self->s.angles[0] = rand()%360;
|
||||
self->s.angles[1] = rand()%360;
|
||||
self->s.angles[2] = rand()%360;
|
||||
self->nextthink = level.time + 0.1;
|
||||
}
|
||||
|
||||
void SP_target_blacklight(edict_t *ent)
|
||||
{
|
||||
if (deathmatch->value)
|
||||
{ // auto-remove for deathmatch
|
||||
G_FreeEdict (ent);
|
||||
return;
|
||||
}
|
||||
|
||||
VectorClear (ent->mins);
|
||||
VectorClear (ent->maxs);
|
||||
|
||||
ent->s.effects |= (EF_TRACKERTRAIL|EF_TRACKER);
|
||||
ent->think = blacklight_think;
|
||||
ent->s.modelindex = gi.modelindex ("models/items/spawngro2/tris.md2");
|
||||
ent->s.frame = 1;
|
||||
ent->nextthink = level.time + 0.1;
|
||||
gi.linkentity (ent);
|
||||
}
|
||||
|
||||
/*QUAKED target_orb (1 0 1) (-16 -16 -24) (16 16 24)
|
||||
Translucent pulsing orb with speckles
|
||||
*/
|
||||
void orb_think (edict_t *self)
|
||||
{
|
||||
self->s.angles[0] = rand()%360;
|
||||
self->s.angles[1] = rand()%360;
|
||||
self->s.angles[2] = rand()%360;
|
||||
// self->s.effects |= (EF_TRACKERTRAIL|EF_DOUBLE);
|
||||
self->nextthink = level.time + 0.1;
|
||||
}
|
||||
|
||||
void SP_target_orb(edict_t *ent)
|
||||
{
|
||||
if (deathmatch->value)
|
||||
{ // auto-remove for deathmatch
|
||||
G_FreeEdict (ent);
|
||||
return;
|
||||
}
|
||||
|
||||
VectorClear (ent->mins);
|
||||
VectorClear (ent->maxs);
|
||||
|
||||
// ent->s.effects |= EF_TRACKERTRAIL;
|
||||
ent->think = orb_think;
|
||||
ent->nextthink = level.time + 0.1;
|
||||
ent->s.modelindex = gi.modelindex ("models/items/spawngro2/tris.md2");
|
||||
ent->s.frame = 2;
|
||||
ent->s.effects |= EF_SPHERETRANS;
|
||||
gi.linkentity (ent);
|
||||
}
|
||||
|
176
g_newtrig.c
Normal file
176
g_newtrig.c
Normal file
|
@ -0,0 +1,176 @@
|
|||
// g_newtrig.c
|
||||
// pmack
|
||||
// october 1997
|
||||
|
||||
#include "g_local.h"
|
||||
|
||||
#define TELEPORT_PLAYER_ONLY 1
|
||||
#define TELEPORT_SILENT 2
|
||||
#define TELEPORT_CTF_ONLY 4
|
||||
#define TELEPORT_START_ON 8
|
||||
|
||||
extern void TeleportEffect (vec3_t origin);
|
||||
|
||||
/*QUAKED info_teleport_destination (.5 .5 .5) (-16 -16 -24) (16 16 32)
|
||||
Destination marker for a teleporter.
|
||||
*/
|
||||
void SP_info_teleport_destination (edict_t *self)
|
||||
{
|
||||
}
|
||||
|
||||
/*QUAKED trigger_teleport (.5 .5 .5) ? player_only silent ctf_only start_on
|
||||
Any object touching this will be transported to the corresponding
|
||||
info_teleport_destination entity. You must set the "target" field,
|
||||
and create an object with a "targetname" field that matches.
|
||||
|
||||
If the trigger_teleport has a targetname, it will only teleport
|
||||
entities when it has been fired.
|
||||
|
||||
player_only: only players are teleported
|
||||
silent: <not used right now>
|
||||
ctf_only: <not used right now>
|
||||
start_on: when trigger has targetname, start active, deactivate when used.
|
||||
*/
|
||||
void trigger_teleport_touch(edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
|
||||
{
|
||||
edict_t *dest;
|
||||
int i;
|
||||
|
||||
if(/*(self->spawnflags & TELEPORT_PLAYER_ONLY) &&*/ !(other->client))
|
||||
return;
|
||||
|
||||
if(self->delay)
|
||||
return;
|
||||
|
||||
dest = G_Find (NULL, FOFS(targetname), self->target);
|
||||
if(!dest)
|
||||
{
|
||||
gi.dprintf("Teleport Destination not found!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
gi.WriteByte (svc_temp_entity);
|
||||
gi.WriteByte (TE_TELEPORT_EFFECT);
|
||||
gi.WritePosition (other->s.origin);
|
||||
gi.multicast (other->s.origin, MULTICAST_PVS);
|
||||
|
||||
// unlink to make sure it can't possibly interfere with KillBox
|
||||
gi.unlinkentity (other);
|
||||
|
||||
VectorCopy (dest->s.origin, other->s.origin);
|
||||
VectorCopy (dest->s.origin, other->s.old_origin);
|
||||
other->s.origin[2] += 10;
|
||||
|
||||
// clear the velocity and hold them in place briefly
|
||||
VectorClear (other->velocity);
|
||||
if(other->client)
|
||||
{
|
||||
other->client->ps.pmove.pm_time = 160>>3; // hold time
|
||||
other->client->ps.pmove.pm_flags |= PMF_TIME_TELEPORT;
|
||||
|
||||
// draw the teleport splash at source and on the player
|
||||
// self->s.event = EV_PLAYER_TELEPORT;
|
||||
other->s.event = EV_PLAYER_TELEPORT;
|
||||
|
||||
// set angles
|
||||
for (i=0 ; i<3 ; i++)
|
||||
other->client->ps.pmove.delta_angles[i] = ANGLE2SHORT(dest->s.angles[i] - other->client->resp.cmd_angles[i]);
|
||||
|
||||
VectorClear (other->client->ps.viewangles);
|
||||
VectorClear (other->client->v_angle);
|
||||
}
|
||||
|
||||
|
||||
VectorClear (other->s.angles);
|
||||
|
||||
// kill anything at the destination
|
||||
KillBox (other);
|
||||
|
||||
gi.linkentity (other);
|
||||
}
|
||||
|
||||
void trigger_teleport_use (edict_t *self, edict_t *other, edict_t *activator)
|
||||
{
|
||||
if(self->delay)
|
||||
self->delay = 0;
|
||||
else
|
||||
self->delay = 1;
|
||||
}
|
||||
|
||||
void SP_trigger_teleport(edict_t *self)
|
||||
{
|
||||
if (!self->wait)
|
||||
self->wait = 0.2;
|
||||
|
||||
self->delay = 0;
|
||||
|
||||
if (self->targetname)
|
||||
{
|
||||
self->use = trigger_teleport_use;
|
||||
if(!(self->spawnflags & TELEPORT_START_ON))
|
||||
self->delay = 1;
|
||||
}
|
||||
|
||||
self->touch = trigger_teleport_touch;
|
||||
|
||||
self->solid = SOLID_TRIGGER;
|
||||
self->movetype = MOVETYPE_NONE;
|
||||
// self->flags |= FL_NOCLIENT;
|
||||
|
||||
if (!VectorCompare(self->s.angles, vec3_origin))
|
||||
G_SetMovedir (self->s.angles, self->movedir);
|
||||
|
||||
gi.setmodel (self, self->model);
|
||||
gi.linkentity (self);
|
||||
}
|
||||
|
||||
// ***************************
|
||||
// TRIGGER_DISGUISE
|
||||
// ***************************
|
||||
|
||||
/*QUAKED trigger_disguise (.5 .5 .5) ? TOGGLE START_ON REMOVE
|
||||
Anything passing through this trigger when it is active will
|
||||
be marked as disguised.
|
||||
|
||||
TOGGLE - field is turned off and on when used.
|
||||
START_ON - field is active when spawned.
|
||||
REMOVE - field removes the disguise
|
||||
*/
|
||||
|
||||
void trigger_disguise_touch(edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
|
||||
{
|
||||
if (other->client)
|
||||
{
|
||||
if(self->spawnflags & 4)
|
||||
other->flags &= ~FL_DISGUISED;
|
||||
else
|
||||
other->flags |= FL_DISGUISED;
|
||||
}
|
||||
}
|
||||
|
||||
void trigger_disguise_use (edict_t *self, edict_t *other, edict_t *activator)
|
||||
{
|
||||
if(self->solid == SOLID_NOT)
|
||||
self->solid = SOLID_TRIGGER;
|
||||
else
|
||||
self->solid = SOLID_NOT;
|
||||
|
||||
gi.linkentity(self);
|
||||
}
|
||||
|
||||
void SP_trigger_disguise (edict_t *self)
|
||||
{
|
||||
if(self->spawnflags & 2)
|
||||
self->solid = SOLID_TRIGGER;
|
||||
else
|
||||
self->solid = SOLID_NOT;
|
||||
|
||||
self->touch = trigger_disguise_touch;
|
||||
self->use = trigger_disguise_use;
|
||||
self->movetype = MOVETYPE_NONE;
|
||||
self->svflags = SVF_NOCLIENT;
|
||||
|
||||
gi.setmodel (self, self->model);
|
||||
gi.linkentity(self);
|
||||
|
||||
}
|
2281
g_newweap.c
Normal file
2281
g_newweap.c
Normal file
File diff suppressed because it is too large
Load diff
798
g_save.c
Normal file
798
g_save.c
Normal file
|
@ -0,0 +1,798 @@
|
|||
|
||||
#include "g_local.h"
|
||||
|
||||
#define Function(f) {#f, f}
|
||||
|
||||
mmove_t mmove_reloc;
|
||||
|
||||
field_t fields[] = {
|
||||
{"classname", FOFS(classname), F_LSTRING},
|
||||
{"model", FOFS(model), F_LSTRING},
|
||||
{"spawnflags", FOFS(spawnflags), F_INT},
|
||||
{"speed", FOFS(speed), F_FLOAT},
|
||||
{"accel", FOFS(accel), F_FLOAT},
|
||||
{"decel", FOFS(decel), F_FLOAT},
|
||||
{"target", FOFS(target), F_LSTRING},
|
||||
{"targetname", FOFS(targetname), F_LSTRING},
|
||||
{"pathtarget", FOFS(pathtarget), F_LSTRING},
|
||||
{"deathtarget", FOFS(deathtarget), F_LSTRING},
|
||||
{"killtarget", FOFS(killtarget), F_LSTRING},
|
||||
{"combattarget", FOFS(combattarget), F_LSTRING},
|
||||
{"message", FOFS(message), F_LSTRING},
|
||||
{"team", FOFS(team), F_LSTRING},
|
||||
{"wait", FOFS(wait), F_FLOAT},
|
||||
{"delay", FOFS(delay), F_FLOAT},
|
||||
{"random", FOFS(random), F_FLOAT},
|
||||
{"move_origin", FOFS(move_origin), F_VECTOR},
|
||||
{"move_angles", FOFS(move_angles), F_VECTOR},
|
||||
{"style", FOFS(style), F_INT},
|
||||
{"count", FOFS(count), F_INT},
|
||||
{"health", FOFS(health), F_INT},
|
||||
{"sounds", FOFS(sounds), F_INT},
|
||||
{"light", 0, F_IGNORE},
|
||||
{"dmg", FOFS(dmg), F_INT},
|
||||
{"mass", FOFS(mass), F_INT},
|
||||
{"volume", FOFS(volume), F_FLOAT},
|
||||
{"attenuation", FOFS(attenuation), F_FLOAT},
|
||||
{"map", FOFS(map), F_LSTRING},
|
||||
{"origin", FOFS(s.origin), F_VECTOR},
|
||||
{"angles", FOFS(s.angles), F_VECTOR},
|
||||
{"angle", FOFS(s.angles), F_ANGLEHACK},
|
||||
|
||||
{"goalentity", FOFS(goalentity), F_EDICT, FFL_NOSPAWN},
|
||||
{"movetarget", FOFS(movetarget), F_EDICT, FFL_NOSPAWN},
|
||||
{"enemy", FOFS(enemy), F_EDICT, FFL_NOSPAWN},
|
||||
{"oldenemy", FOFS(oldenemy), F_EDICT, FFL_NOSPAWN},
|
||||
{"activator", FOFS(activator), F_EDICT, FFL_NOSPAWN},
|
||||
{"groundentity", FOFS(groundentity), F_EDICT, FFL_NOSPAWN},
|
||||
{"teamchain", FOFS(teamchain), F_EDICT, FFL_NOSPAWN},
|
||||
{"teammaster", FOFS(teammaster), F_EDICT, FFL_NOSPAWN},
|
||||
{"owner", FOFS(owner), F_EDICT, FFL_NOSPAWN},
|
||||
{"mynoise", FOFS(mynoise), F_EDICT, FFL_NOSPAWN},
|
||||
{"mynoise2", FOFS(mynoise2), F_EDICT, FFL_NOSPAWN},
|
||||
{"target_ent", FOFS(target_ent), F_EDICT, FFL_NOSPAWN},
|
||||
{"chain", FOFS(chain), F_EDICT, FFL_NOSPAWN},
|
||||
|
||||
{"prethink", FOFS(prethink), F_FUNCTION, FFL_NOSPAWN},
|
||||
{"think", FOFS(think), F_FUNCTION, FFL_NOSPAWN},
|
||||
{"blocked", FOFS(blocked), F_FUNCTION, FFL_NOSPAWN},
|
||||
{"touch", FOFS(touch), F_FUNCTION, FFL_NOSPAWN},
|
||||
{"use", FOFS(use), F_FUNCTION, FFL_NOSPAWN},
|
||||
{"pain", FOFS(pain), F_FUNCTION, FFL_NOSPAWN},
|
||||
{"die", FOFS(die), F_FUNCTION, FFL_NOSPAWN},
|
||||
|
||||
{"stand", FOFS(monsterinfo.stand), F_FUNCTION, FFL_NOSPAWN},
|
||||
{"idle", FOFS(monsterinfo.idle), F_FUNCTION, FFL_NOSPAWN},
|
||||
{"search", FOFS(monsterinfo.search), F_FUNCTION, FFL_NOSPAWN},
|
||||
{"walk", FOFS(monsterinfo.walk), F_FUNCTION, FFL_NOSPAWN},
|
||||
{"run", FOFS(monsterinfo.run), F_FUNCTION, FFL_NOSPAWN},
|
||||
{"dodge", FOFS(monsterinfo.dodge), F_FUNCTION, FFL_NOSPAWN},
|
||||
{"attack", FOFS(monsterinfo.attack), F_FUNCTION, FFL_NOSPAWN},
|
||||
{"melee", FOFS(monsterinfo.melee), F_FUNCTION, FFL_NOSPAWN},
|
||||
{"sight", FOFS(monsterinfo.sight), F_FUNCTION, FFL_NOSPAWN},
|
||||
{"checkattack", FOFS(monsterinfo.checkattack), F_FUNCTION, FFL_NOSPAWN},
|
||||
{"currentmove", FOFS(monsterinfo.currentmove), F_MMOVE, FFL_NOSPAWN},
|
||||
|
||||
{"endfunc", FOFS(moveinfo.endfunc), F_FUNCTION, FFL_NOSPAWN},
|
||||
|
||||
// temp spawn vars -- only valid when the spawn function is called
|
||||
{"lip", STOFS(lip), F_INT, FFL_SPAWNTEMP},
|
||||
{"distance", STOFS(distance), F_INT, FFL_SPAWNTEMP},
|
||||
{"height", STOFS(height), F_INT, FFL_SPAWNTEMP},
|
||||
{"noise", STOFS(noise), F_LSTRING, FFL_SPAWNTEMP},
|
||||
{"pausetime", STOFS(pausetime), F_FLOAT, FFL_SPAWNTEMP},
|
||||
{"item", STOFS(item), F_LSTRING, FFL_SPAWNTEMP},
|
||||
|
||||
//need for item field in edict struct, FFL_SPAWNTEMP item will be skipped on saves
|
||||
{"item", FOFS(item), F_ITEM},
|
||||
|
||||
{"gravity", STOFS(gravity), F_LSTRING, FFL_SPAWNTEMP},
|
||||
{"sky", STOFS(sky), F_LSTRING, FFL_SPAWNTEMP},
|
||||
{"skyrotate", STOFS(skyrotate), F_FLOAT, FFL_SPAWNTEMP},
|
||||
{"skyaxis", STOFS(skyaxis), F_VECTOR, FFL_SPAWNTEMP},
|
||||
{"minyaw", STOFS(minyaw), F_FLOAT, FFL_SPAWNTEMP},
|
||||
{"maxyaw", STOFS(maxyaw), F_FLOAT, FFL_SPAWNTEMP},
|
||||
{"minpitch", STOFS(minpitch), F_FLOAT, FFL_SPAWNTEMP},
|
||||
{"maxpitch", STOFS(maxpitch), F_FLOAT, FFL_SPAWNTEMP},
|
||||
{"nextmap", STOFS(nextmap), F_LSTRING, FFL_SPAWNTEMP},
|
||||
|
||||
// ROGUE
|
||||
{"bad_area", FOFS(bad_area), F_EDICT},
|
||||
// while the hint_path stuff could be reassembled on the fly, no reason to be different
|
||||
{"hint_chain", FOFS(hint_chain), F_EDICT},
|
||||
{"monster_hint_chain", FOFS(monster_hint_chain), F_EDICT},
|
||||
{"target_hint_chain", FOFS(target_hint_chain), F_EDICT},
|
||||
//
|
||||
{"goal_hint", FOFS(monsterinfo.goal_hint), F_EDICT},
|
||||
{"badMedic1", FOFS(monsterinfo.badMedic1), F_EDICT},
|
||||
{"badMedic2", FOFS(monsterinfo.badMedic2), F_EDICT},
|
||||
{"last_player_enemy", FOFS(monsterinfo.last_player_enemy), F_EDICT},
|
||||
{"commander", FOFS(monsterinfo.commander), F_EDICT},
|
||||
{"blocked", FOFS(monsterinfo.blocked), F_MMOVE, FFL_NOSPAWN},
|
||||
{"duck", FOFS(monsterinfo.duck), F_MMOVE, FFL_NOSPAWN},
|
||||
{"unduck", FOFS(monsterinfo.unduck), F_MMOVE, FFL_NOSPAWN},
|
||||
{"sidestep", FOFS(monsterinfo.sidestep), F_MMOVE, FFL_NOSPAWN},
|
||||
// ROGUE
|
||||
|
||||
{0, 0, 0, 0}
|
||||
|
||||
};
|
||||
|
||||
field_t levelfields[] =
|
||||
{
|
||||
{"changemap", LLOFS(changemap), F_LSTRING},
|
||||
|
||||
{"sight_client", LLOFS(sight_client), F_EDICT},
|
||||
{"sight_entity", LLOFS(sight_entity), F_EDICT},
|
||||
{"sound_entity", LLOFS(sound_entity), F_EDICT},
|
||||
{"sound2_entity", LLOFS(sound2_entity), F_EDICT},
|
||||
|
||||
// ROGUE
|
||||
{"disguise_violator", LLOFS(disguise_violator), F_EDICT},
|
||||
// ROGUE
|
||||
|
||||
{NULL, 0, F_INT}
|
||||
};
|
||||
|
||||
field_t clientfields[] =
|
||||
{
|
||||
{"pers.weapon", CLOFS(pers.weapon), F_ITEM},
|
||||
{"pers.lastweapon", CLOFS(pers.lastweapon), F_ITEM},
|
||||
{"newweapon", CLOFS(newweapon), F_ITEM},
|
||||
// ROGUE
|
||||
{"owned_sphere", CLOFS(owned_sphere), F_EDICT},
|
||||
// ROGUE
|
||||
|
||||
{NULL, 0, F_INT}
|
||||
};
|
||||
|
||||
/*
|
||||
============
|
||||
InitGame
|
||||
|
||||
This will be called when the dll is first loaded, which
|
||||
only happens when a new game is started or a save game
|
||||
is loaded.
|
||||
============
|
||||
*/
|
||||
void InitGame (void)
|
||||
{
|
||||
gi.dprintf ("==== InitGame ====\n");
|
||||
|
||||
gun_x = gi.cvar ("gun_x", "0", 0);
|
||||
gun_y = gi.cvar ("gun_y", "0", 0);
|
||||
gun_z = gi.cvar ("gun_z", "0", 0);
|
||||
|
||||
//FIXME: sv_ prefix is wrong for these
|
||||
sv_rollspeed = gi.cvar ("sv_rollspeed", "200", 0);
|
||||
sv_rollangle = gi.cvar ("sv_rollangle", "2", 0);
|
||||
sv_maxvelocity = gi.cvar ("sv_maxvelocity", "2000", 0);
|
||||
sv_gravity = gi.cvar ("sv_gravity", "800", 0);
|
||||
|
||||
sv_stopspeed = gi.cvar ("sv_stopspeed", "100", 0); // PGM - was #define in g_phys.c
|
||||
|
||||
//ROGUE
|
||||
g_showlogic = gi.cvar ("g_showlogic", "0", 0);
|
||||
huntercam = gi.cvar ("huntercam", "1", CVAR_SERVERINFO|CVAR_LATCH);
|
||||
strong_mines = gi.cvar ("strong_mines", "0", 0);
|
||||
randomrespawn = gi.cvar ("randomrespawn", "0", 0);
|
||||
//ROGUE
|
||||
|
||||
// noset vars
|
||||
dedicated = gi.cvar ("dedicated", "0", CVAR_NOSET);
|
||||
|
||||
// latched vars
|
||||
sv_cheats = gi.cvar ("cheats", "0", CVAR_SERVERINFO|CVAR_LATCH);
|
||||
gi.cvar ("gamename", GAMEVERSION , CVAR_SERVERINFO | CVAR_LATCH);
|
||||
gi.cvar ("gamedate", __DATE__ , CVAR_SERVERINFO | CVAR_LATCH);
|
||||
|
||||
maxclients = gi.cvar ("maxclients", "4", CVAR_SERVERINFO | CVAR_LATCH);
|
||||
maxspectators = gi.cvar ("maxspectators", "4", CVAR_SERVERINFO);
|
||||
deathmatch = gi.cvar ("deathmatch", "0", CVAR_LATCH);
|
||||
coop = gi.cvar ("coop", "0", CVAR_LATCH);
|
||||
skill = gi.cvar ("skill", "1", CVAR_LATCH);
|
||||
maxentities = gi.cvar ("maxentities", "1024", CVAR_LATCH);
|
||||
gamerules = gi.cvar ("gamerules", "0", CVAR_LATCH); //PGM
|
||||
|
||||
// change anytime vars
|
||||
dmflags = gi.cvar ("dmflags", "0", CVAR_SERVERINFO);
|
||||
fraglimit = gi.cvar ("fraglimit", "0", CVAR_SERVERINFO);
|
||||
timelimit = gi.cvar ("timelimit", "0", CVAR_SERVERINFO);
|
||||
password = gi.cvar ("password", "", CVAR_USERINFO);
|
||||
spectator_password = gi.cvar ("spectator_password", "", CVAR_USERINFO);
|
||||
filterban = gi.cvar ("filterban", "1", 0);
|
||||
|
||||
g_select_empty = gi.cvar ("g_select_empty", "0", CVAR_ARCHIVE);
|
||||
|
||||
run_pitch = gi.cvar ("run_pitch", "0.002", 0);
|
||||
run_roll = gi.cvar ("run_roll", "0.005", 0);
|
||||
bob_up = gi.cvar ("bob_up", "0.005", 0);
|
||||
bob_pitch = gi.cvar ("bob_pitch", "0.002", 0);
|
||||
bob_roll = gi.cvar ("bob_roll", "0.002", 0);
|
||||
|
||||
// flood control
|
||||
flood_msgs = gi.cvar ("flood_msgs", "4", 0);
|
||||
flood_persecond = gi.cvar ("flood_persecond", "4", 0);
|
||||
flood_waitdelay = gi.cvar ("flood_waitdelay", "10", 0);
|
||||
|
||||
// dm map list
|
||||
sv_maplist = gi.cvar ("sv_maplist", "", 0);
|
||||
|
||||
// items
|
||||
InitItems ();
|
||||
|
||||
Com_sprintf (game.helpmessage1, sizeof(game.helpmessage1), "");
|
||||
|
||||
Com_sprintf (game.helpmessage2, sizeof(game.helpmessage2), "");
|
||||
|
||||
// initialize all entities for this game
|
||||
game.maxentities = maxentities->value;
|
||||
g_edicts = gi.TagMalloc (game.maxentities * sizeof(g_edicts[0]), TAG_GAME);
|
||||
globals.edicts = g_edicts;
|
||||
globals.max_edicts = game.maxentities;
|
||||
|
||||
// initialize all clients for this game
|
||||
game.maxclients = maxclients->value;
|
||||
game.clients = gi.TagMalloc (game.maxclients * sizeof(game.clients[0]), TAG_GAME);
|
||||
globals.num_edicts = game.maxclients+1;
|
||||
|
||||
//======
|
||||
//ROGUE
|
||||
if(gamerules)
|
||||
{
|
||||
InitGameRules(); // if there are game rules to set up, do so now.
|
||||
}
|
||||
//ROGUE
|
||||
//======
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
|
||||
void WriteField1 (FILE *f, field_t *field, byte *base)
|
||||
{
|
||||
void *p;
|
||||
int len;
|
||||
int index;
|
||||
|
||||
if (field->flags & FFL_SPAWNTEMP)
|
||||
return;
|
||||
|
||||
p = (void *)(base + field->ofs);
|
||||
switch (field->type)
|
||||
{
|
||||
case F_INT:
|
||||
case F_FLOAT:
|
||||
case F_ANGLEHACK:
|
||||
case F_VECTOR:
|
||||
case F_IGNORE:
|
||||
break;
|
||||
|
||||
case F_LSTRING:
|
||||
case F_GSTRING:
|
||||
if ( *(char **)p )
|
||||
len = strlen(*(char **)p) + 1;
|
||||
else
|
||||
len = 0;
|
||||
*(int *)p = len;
|
||||
break;
|
||||
case F_EDICT:
|
||||
if ( *(edict_t **)p == NULL)
|
||||
index = -1;
|
||||
else
|
||||
index = *(edict_t **)p - g_edicts;
|
||||
*(int *)p = index;
|
||||
break;
|
||||
case F_CLIENT:
|
||||
if ( *(gclient_t **)p == NULL)
|
||||
index = -1;
|
||||
else
|
||||
index = *(gclient_t **)p - game.clients;
|
||||
*(int *)p = index;
|
||||
break;
|
||||
case F_ITEM:
|
||||
if ( *(edict_t **)p == NULL)
|
||||
index = -1;
|
||||
else
|
||||
index = *(gitem_t **)p - itemlist;
|
||||
*(int *)p = index;
|
||||
break;
|
||||
|
||||
//relative to code segment
|
||||
case F_FUNCTION:
|
||||
if (*(byte **)p == NULL)
|
||||
index = 0;
|
||||
else
|
||||
index = *(byte **)p - ((byte *)InitGame);
|
||||
*(int *)p = index;
|
||||
break;
|
||||
|
||||
//relative to data segment
|
||||
case F_MMOVE:
|
||||
if (*(byte **)p == NULL)
|
||||
index = 0;
|
||||
else
|
||||
index = *(byte **)p - (byte *)&mmove_reloc;
|
||||
*(int *)p = index;
|
||||
break;
|
||||
|
||||
default:
|
||||
gi.error ("WriteEdict: unknown field type");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void WriteField2 (FILE *f, field_t *field, byte *base)
|
||||
{
|
||||
int len;
|
||||
void *p;
|
||||
|
||||
if (field->flags & FFL_SPAWNTEMP)
|
||||
return;
|
||||
|
||||
p = (void *)(base + field->ofs);
|
||||
switch (field->type)
|
||||
{
|
||||
case F_LSTRING:
|
||||
if ( *(char **)p )
|
||||
{
|
||||
len = strlen(*(char **)p) + 1;
|
||||
fwrite (*(char **)p, len, 1, f);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ReadField (FILE *f, field_t *field, byte *base)
|
||||
{
|
||||
void *p;
|
||||
int len;
|
||||
int index;
|
||||
|
||||
if (field->flags & FFL_SPAWNTEMP)
|
||||
return;
|
||||
|
||||
p = (void *)(base + field->ofs);
|
||||
switch (field->type)
|
||||
{
|
||||
case F_INT:
|
||||
case F_FLOAT:
|
||||
case F_ANGLEHACK:
|
||||
case F_VECTOR:
|
||||
case F_IGNORE:
|
||||
break;
|
||||
|
||||
case F_LSTRING:
|
||||
len = *(int *)p;
|
||||
if (!len)
|
||||
*(char **)p = NULL;
|
||||
else
|
||||
{
|
||||
*(char **)p = gi.TagMalloc (len, TAG_LEVEL);
|
||||
fread (*(char **)p, len, 1, f);
|
||||
}
|
||||
break;
|
||||
case F_EDICT:
|
||||
index = *(int *)p;
|
||||
if ( index == -1 )
|
||||
*(edict_t **)p = NULL;
|
||||
else
|
||||
*(edict_t **)p = &g_edicts[index];
|
||||
break;
|
||||
case F_CLIENT:
|
||||
index = *(int *)p;
|
||||
if ( index == -1 )
|
||||
*(gclient_t **)p = NULL;
|
||||
else
|
||||
*(gclient_t **)p = &game.clients[index];
|
||||
break;
|
||||
case F_ITEM:
|
||||
index = *(int *)p;
|
||||
if ( index == -1 )
|
||||
*(gitem_t **)p = NULL;
|
||||
else
|
||||
*(gitem_t **)p = &itemlist[index];
|
||||
break;
|
||||
|
||||
//relative to code segment
|
||||
case F_FUNCTION:
|
||||
index = *(int *)p;
|
||||
if ( index == 0 )
|
||||
*(byte **)p = NULL;
|
||||
else
|
||||
*(byte **)p = ((byte *)InitGame) + index;
|
||||
break;
|
||||
|
||||
//relative to data segment
|
||||
case F_MMOVE:
|
||||
index = *(int *)p;
|
||||
if (index == 0)
|
||||
*(byte **)p = NULL;
|
||||
else
|
||||
*(byte **)p = (byte *)&mmove_reloc + index;
|
||||
break;
|
||||
|
||||
default:
|
||||
gi.error ("ReadEdict: unknown field type");
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================
|
||||
|
||||
/*
|
||||
==============
|
||||
WriteClient
|
||||
|
||||
All pointer variables (except function pointers) must be handled specially.
|
||||
==============
|
||||
*/
|
||||
void WriteClient (FILE *f, gclient_t *client)
|
||||
{
|
||||
field_t *field;
|
||||
gclient_t temp;
|
||||
|
||||
// all of the ints, floats, and vectors stay as they are
|
||||
temp = *client;
|
||||
|
||||
// change the pointers to lengths or indexes
|
||||
for (field=clientfields ; field->name ; field++)
|
||||
{
|
||||
WriteField1 (f, field, (byte *)&temp);
|
||||
}
|
||||
|
||||
// write the block
|
||||
fwrite (&temp, sizeof(temp), 1, f);
|
||||
|
||||
// now write any allocated data following the edict
|
||||
for (field=clientfields ; field->name ; field++)
|
||||
{
|
||||
WriteField2 (f, field, (byte *)client);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==============
|
||||
ReadClient
|
||||
|
||||
All pointer variables (except function pointers) must be handled specially.
|
||||
==============
|
||||
*/
|
||||
void ReadClient (FILE *f, gclient_t *client)
|
||||
{
|
||||
field_t *field;
|
||||
|
||||
fread (client, sizeof(*client), 1, f);
|
||||
|
||||
for (field=clientfields ; field->name ; field++)
|
||||
{
|
||||
ReadField (f, field, (byte *)client);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
WriteGame
|
||||
|
||||
This will be called whenever the game goes to a new level,
|
||||
and when the user explicitly saves the game.
|
||||
|
||||
Game information include cross level data, like multi level
|
||||
triggers, help computer info, and all client states.
|
||||
|
||||
A single player death will automatically restore from the
|
||||
last save position.
|
||||
============
|
||||
*/
|
||||
void WriteGame (char *filename, qboolean autosave)
|
||||
{
|
||||
FILE *f;
|
||||
int i;
|
||||
char str[16];
|
||||
|
||||
if (!autosave)
|
||||
SaveClientData ();
|
||||
|
||||
f = fopen (filename, "wb");
|
||||
if (!f)
|
||||
gi.error ("Couldn't open %s", filename);
|
||||
|
||||
memset (str, 0, sizeof(str));
|
||||
strcpy (str, __DATE__);
|
||||
fwrite (str, sizeof(str), 1, f);
|
||||
|
||||
game.autosaved = autosave;
|
||||
fwrite (&game, sizeof(game), 1, f);
|
||||
game.autosaved = false;
|
||||
|
||||
for (i=0 ; i<game.maxclients ; i++)
|
||||
WriteClient (f, &game.clients[i]);
|
||||
|
||||
fclose (f);
|
||||
}
|
||||
|
||||
void ReadGame (char *filename)
|
||||
{
|
||||
FILE *f;
|
||||
int i;
|
||||
char str[16];
|
||||
|
||||
gi.FreeTags (TAG_GAME);
|
||||
|
||||
f = fopen (filename, "rb");
|
||||
if (!f)
|
||||
gi.error ("Couldn't open %s", filename);
|
||||
|
||||
fread (str, sizeof(str), 1, f);
|
||||
if (strcmp (str, __DATE__))
|
||||
{
|
||||
fclose (f);
|
||||
gi.error ("Savegame from an older version.\n");
|
||||
}
|
||||
|
||||
g_edicts = gi.TagMalloc (game.maxentities * sizeof(g_edicts[0]), TAG_GAME);
|
||||
globals.edicts = g_edicts;
|
||||
|
||||
fread (&game, sizeof(game), 1, f);
|
||||
game.clients = gi.TagMalloc (game.maxclients * sizeof(game.clients[0]), TAG_GAME);
|
||||
for (i=0 ; i<game.maxclients ; i++)
|
||||
ReadClient (f, &game.clients[i]);
|
||||
|
||||
fclose (f);
|
||||
}
|
||||
|
||||
//==========================================================
|
||||
|
||||
|
||||
/*
|
||||
==============
|
||||
WriteEdict
|
||||
|
||||
All pointer variables (except function pointers) must be handled specially.
|
||||
==============
|
||||
*/
|
||||
void WriteEdict (FILE *f, edict_t *ent)
|
||||
{
|
||||
field_t *field;
|
||||
edict_t temp;
|
||||
|
||||
// all of the ints, floats, and vectors stay as they are
|
||||
temp = *ent;
|
||||
|
||||
// change the pointers to lengths or indexes
|
||||
for (field=fields ; field->name ; field++)
|
||||
{
|
||||
WriteField1 (f, field, (byte *)&temp);
|
||||
}
|
||||
|
||||
// write the block
|
||||
fwrite (&temp, sizeof(temp), 1, f);
|
||||
|
||||
// now write any allocated data following the edict
|
||||
for (field=fields ; field->name ; field++)
|
||||
{
|
||||
WriteField2 (f, field, (byte *)ent);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
==============
|
||||
WriteLevelLocals
|
||||
|
||||
All pointer variables (except function pointers) must be handled specially.
|
||||
==============
|
||||
*/
|
||||
void WriteLevelLocals (FILE *f)
|
||||
{
|
||||
field_t *field;
|
||||
level_locals_t temp;
|
||||
|
||||
// all of the ints, floats, and vectors stay as they are
|
||||
temp = level;
|
||||
|
||||
// change the pointers to lengths or indexes
|
||||
for (field=levelfields ; field->name ; field++)
|
||||
{
|
||||
WriteField1 (f, field, (byte *)&temp);
|
||||
}
|
||||
|
||||
// write the block
|
||||
fwrite (&temp, sizeof(temp), 1, f);
|
||||
|
||||
// now write any allocated data following the edict
|
||||
for (field=levelfields ; field->name ; field++)
|
||||
{
|
||||
WriteField2 (f, field, (byte *)&level);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==============
|
||||
ReadEdict
|
||||
|
||||
All pointer variables (except function pointers) must be handled specially.
|
||||
==============
|
||||
*/
|
||||
void ReadEdict (FILE *f, edict_t *ent)
|
||||
{
|
||||
field_t *field;
|
||||
|
||||
fread (ent, sizeof(*ent), 1, f);
|
||||
|
||||
for (field=fields ; field->name ; field++)
|
||||
{
|
||||
ReadField (f, field, (byte *)ent);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==============
|
||||
ReadLevelLocals
|
||||
|
||||
All pointer variables (except function pointers) must be handled specially.
|
||||
==============
|
||||
*/
|
||||
void ReadLevelLocals (FILE *f)
|
||||
{
|
||||
field_t *field;
|
||||
|
||||
fread (&level, sizeof(level), 1, f);
|
||||
|
||||
for (field=levelfields ; field->name ; field++)
|
||||
{
|
||||
ReadField (f, field, (byte *)&level);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
WriteLevel
|
||||
|
||||
=================
|
||||
*/
|
||||
void WriteLevel (char *filename)
|
||||
{
|
||||
int i;
|
||||
edict_t *ent;
|
||||
FILE *f;
|
||||
void *base;
|
||||
|
||||
f = fopen (filename, "wb");
|
||||
if (!f)
|
||||
gi.error ("Couldn't open %s", filename);
|
||||
|
||||
// write out edict size for checking
|
||||
i = sizeof(edict_t);
|
||||
fwrite (&i, sizeof(i), 1, f);
|
||||
|
||||
// write out a function pointer for checking
|
||||
base = (void *)InitGame;
|
||||
fwrite (&base, sizeof(base), 1, f);
|
||||
|
||||
// write out level_locals_t
|
||||
WriteLevelLocals (f);
|
||||
|
||||
// write out all the entities
|
||||
for (i=0 ; i<globals.num_edicts ; i++)
|
||||
{
|
||||
ent = &g_edicts[i];
|
||||
if (!ent->inuse)
|
||||
continue;
|
||||
fwrite (&i, sizeof(i), 1, f);
|
||||
WriteEdict (f, ent);
|
||||
}
|
||||
i = -1;
|
||||
fwrite (&i, sizeof(i), 1, f);
|
||||
|
||||
fclose (f);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=================
|
||||
ReadLevel
|
||||
|
||||
SpawnEntities will already have been called on the
|
||||
level the same way it was when the level was saved.
|
||||
|
||||
That is necessary to get the baselines
|
||||
set up identically.
|
||||
|
||||
The server will have cleared all of the world links before
|
||||
calling ReadLevel.
|
||||
|
||||
No clients are connected yet.
|
||||
=================
|
||||
*/
|
||||
void ReadLevel (char *filename)
|
||||
{
|
||||
int entnum;
|
||||
FILE *f;
|
||||
int i;
|
||||
void *base;
|
||||
edict_t *ent;
|
||||
|
||||
f = fopen (filename, "rb");
|
||||
if (!f)
|
||||
gi.error ("Couldn't open %s", filename);
|
||||
|
||||
// free any dynamic memory allocated by loading the level
|
||||
// base state
|
||||
gi.FreeTags (TAG_LEVEL);
|
||||
|
||||
// wipe all the entities
|
||||
memset (g_edicts, 0, game.maxentities*sizeof(g_edicts[0]));
|
||||
globals.num_edicts = maxclients->value+1;
|
||||
|
||||
// check edict size
|
||||
fread (&i, sizeof(i), 1, f);
|
||||
if (i != sizeof(edict_t))
|
||||
{
|
||||
fclose (f);
|
||||
gi.error ("ReadLevel: mismatched edict size");
|
||||
}
|
||||
|
||||
// check function pointer base address
|
||||
fread (&base, sizeof(base), 1, f);
|
||||
#ifdef _WIN32
|
||||
if (base != (void *)InitGame)
|
||||
{
|
||||
fclose (f);
|
||||
gi.error ("ReadLevel: function pointers have moved");
|
||||
}
|
||||
#else
|
||||
gi.dprintf("Function offsets %d\n", ((byte *)base) - ((byte *)InitGame));
|
||||
#endif
|
||||
|
||||
// load the level locals
|
||||
ReadLevelLocals (f);
|
||||
|
||||
// load all the entities
|
||||
while (1)
|
||||
{
|
||||
if (fread (&entnum, sizeof(entnum), 1, f) != 1)
|
||||
{
|
||||
fclose (f);
|
||||
gi.error ("ReadLevel: failed to read entnum");
|
||||
}
|
||||
if (entnum == -1)
|
||||
break;
|
||||
if (entnum >= globals.num_edicts)
|
||||
globals.num_edicts = entnum+1;
|
||||
|
||||
ent = &g_edicts[entnum];
|
||||
ReadEdict (f, ent);
|
||||
|
||||
// let the server rebuild world links for this ent
|
||||
memset (&ent->area, 0, sizeof(ent->area));
|
||||
gi.linkentity (ent);
|
||||
}
|
||||
|
||||
fclose (f);
|
||||
|
||||
// PMM - rebuild the hint path chains
|
||||
// InitHintPaths();
|
||||
// pmm
|
||||
|
||||
// mark all clients as unconnected
|
||||
for (i=0 ; i<maxclients->value ; i++)
|
||||
{
|
||||
ent = &g_edicts[i+1];
|
||||
ent->client = game.clients + i;
|
||||
ent->client->pers.connected = false;
|
||||
}
|
||||
|
||||
// do any load time things at this point
|
||||
for (i=0 ; i<globals.num_edicts ; i++)
|
||||
{
|
||||
ent = &g_edicts[i];
|
||||
|
||||
if (!ent->inuse)
|
||||
continue;
|
||||
|
||||
// fire any cross-level triggers
|
||||
if (ent->classname)
|
||||
if (strcmp(ent->classname, "target_crosslevel_target") == 0)
|
||||
ent->nextthink = level.time + ent->delay;
|
||||
}
|
||||
}
|
789
g_sphere.c
Normal file
789
g_sphere.c
Normal file
|
@ -0,0 +1,789 @@
|
|||
// g_sphere.c
|
||||
// pmack
|
||||
// april 1998
|
||||
|
||||
// defender - actively finds and shoots at enemies
|
||||
// hunter - waits until < 25% health and vore ball tracks person who hurt you
|
||||
// vengeance - kills person who killed you.
|
||||
|
||||
#include "g_local.h"
|
||||
|
||||
#define DEFENDER_LIFESPAN 30
|
||||
#define HUNTER_LIFESPAN 30
|
||||
#define VENGEANCE_LIFESPAN 30
|
||||
#define MINIMUM_FLY_TIME 15
|
||||
//#define MINIMUM_FLY_TIME 30
|
||||
|
||||
// FIXME - do we need to be calling ED_NewString at all?
|
||||
extern char *ED_NewString (char *string);
|
||||
void LookAtKiller (edict_t *self, edict_t *inflictor, edict_t *attacker);
|
||||
|
||||
|
||||
void defender_think (edict_t *self);
|
||||
void hunter_think (edict_t *self);
|
||||
void vengeance_think (edict_t *self);
|
||||
void vengeance_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf);
|
||||
void hunter_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf);
|
||||
|
||||
// *************************
|
||||
// General Sphere Code
|
||||
// *************************
|
||||
|
||||
// =================
|
||||
// =================
|
||||
void sphere_think_explode (edict_t *self)
|
||||
{
|
||||
if(self->owner && self->owner->client && !(self->spawnflags & SPHERE_DOPPLEGANGER))
|
||||
{
|
||||
self->owner->client->owned_sphere = NULL;
|
||||
}
|
||||
BecomeExplosion1 (self);
|
||||
}
|
||||
|
||||
// =================
|
||||
// sphere_explode
|
||||
// =================
|
||||
void sphere_explode (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
|
||||
{
|
||||
// if(self->owner && self->owner->client)
|
||||
// gi.cprintf(self->owner, PRINT_HIGH, "Sphere timed out\n");
|
||||
// gi.dprintf("player died, blowing up\n");
|
||||
sphere_think_explode (self);
|
||||
}
|
||||
|
||||
// =================
|
||||
// sphere_if_idle_die - if the sphere is not currently attacking, blow up.
|
||||
// =================
|
||||
void sphere_if_idle_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
|
||||
{
|
||||
if(!self->enemy)
|
||||
{
|
||||
// gi.dprintf("player died, blowing up\n");
|
||||
sphere_think_explode(self);
|
||||
}
|
||||
}
|
||||
|
||||
// *************************
|
||||
// Sphere Movement
|
||||
// *************************
|
||||
|
||||
// =================
|
||||
// =================
|
||||
void sphere_fly (edict_t *self)
|
||||
{
|
||||
vec3_t dest;
|
||||
vec3_t dir;
|
||||
|
||||
if(level.time >= self->wait)
|
||||
{
|
||||
// gi.dprintf("fly: timed out\n");
|
||||
sphere_think_explode(self);
|
||||
return;
|
||||
}
|
||||
|
||||
VectorCopy (self->owner->s.origin, dest);
|
||||
dest[2] = self->owner->absmax[2] + 4;
|
||||
|
||||
if(level.time == (float)(int)level.time)
|
||||
{
|
||||
if(!visible(self, self->owner))
|
||||
{
|
||||
VectorCopy(dest, self->s.origin);
|
||||
gi.linkentity(self);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
VectorSubtract (dest, self->s.origin, dir);
|
||||
VectorScale (dir, 5, self->velocity);
|
||||
}
|
||||
|
||||
// =================
|
||||
// =================
|
||||
void sphere_chase (edict_t *self, int stupidChase)
|
||||
{
|
||||
vec3_t dest;
|
||||
vec3_t dir;
|
||||
float dist;
|
||||
|
||||
if(level.time >= self->wait || (self->enemy && self->enemy->health < 1))
|
||||
{
|
||||
sphere_think_explode(self);
|
||||
return;
|
||||
}
|
||||
|
||||
VectorCopy (self->enemy->s.origin, dest);
|
||||
if(self->enemy->client)
|
||||
dest[2] += self->enemy->viewheight;
|
||||
|
||||
if(visible(self, self->enemy) || stupidChase)
|
||||
{
|
||||
// if moving, hunter sphere uses active sound
|
||||
if(!stupidChase)
|
||||
self->s.sound = gi.soundindex ("spheres/h_active.wav");
|
||||
|
||||
VectorSubtract (dest, self->s.origin, dir);
|
||||
VectorNormalize (dir);
|
||||
vectoangles2(dir, self->s.angles);
|
||||
VectorScale (dir, 500, self->velocity);
|
||||
VectorCopy(dest, self->monsterinfo.saved_goal);
|
||||
}
|
||||
else if (VectorCompare (self->monsterinfo.saved_goal, vec3_origin))
|
||||
{
|
||||
VectorSubtract(self->enemy->s.origin, self->s.origin, dir);
|
||||
dist = VectorNormalize(dir);
|
||||
vectoangles2(dir, self->s.angles);
|
||||
|
||||
// if lurking, hunter sphere uses lurking sound
|
||||
self->s.sound = gi.soundindex ("spheres/h_lurk.wav");
|
||||
VectorClear (self->velocity);
|
||||
}
|
||||
else
|
||||
{
|
||||
VectorSubtract(self->monsterinfo.saved_goal, self->s.origin, dir);
|
||||
dist = VectorNormalize(dir);
|
||||
|
||||
if(dist > 1)
|
||||
{
|
||||
vectoangles2(dir, self->s.angles);
|
||||
|
||||
if(dist > 500)
|
||||
VectorScale(dir, 500, self->velocity);
|
||||
else if (dist < 20)
|
||||
VectorScale(dir, (dist / FRAMETIME), self->velocity);
|
||||
else
|
||||
VectorScale(dir, dist, self->velocity);
|
||||
|
||||
// if moving, hunter sphere uses active sound
|
||||
if(!stupidChase)
|
||||
self->s.sound = gi.soundindex ("spheres/h_active.wav");
|
||||
}
|
||||
else
|
||||
{
|
||||
VectorSubtract(self->enemy->s.origin, self->s.origin, dir);
|
||||
dist = VectorNormalize(dir);
|
||||
vectoangles2(dir, self->s.angles);
|
||||
|
||||
// if not moving, hunter sphere uses lurk sound
|
||||
if(!stupidChase)
|
||||
self->s.sound = gi.soundindex ("spheres/h_lurk.wav");
|
||||
|
||||
VectorClear(self->velocity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// *************************
|
||||
// Attack related stuff
|
||||
// *************************
|
||||
|
||||
// =================
|
||||
// =================
|
||||
void sphere_fire (edict_t *self, edict_t *enemy)
|
||||
{
|
||||
vec3_t dest;
|
||||
vec3_t dir;
|
||||
|
||||
if(level.time >= self->wait || !enemy)
|
||||
{
|
||||
sphere_think_explode(self);
|
||||
return;
|
||||
}
|
||||
|
||||
VectorCopy (enemy->s.origin, dest);
|
||||
self->s.effects |= EF_ROCKET;
|
||||
|
||||
VectorSubtract (dest, self->s.origin, dir);
|
||||
VectorNormalize (dir);
|
||||
vectoangles2 ( dir, self->s.angles );
|
||||
VectorScale (dir, 1000, self->velocity);
|
||||
|
||||
self->touch = vengeance_touch;
|
||||
self->think = sphere_think_explode;
|
||||
self->nextthink = self->wait;
|
||||
}
|
||||
|
||||
// =================
|
||||
// =================
|
||||
void sphere_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf, int mod)
|
||||
{
|
||||
if(self->spawnflags & SPHERE_DOPPLEGANGER)
|
||||
{
|
||||
if (other == self->teammaster)
|
||||
return;
|
||||
|
||||
self->takedamage = DAMAGE_NO;
|
||||
self->owner = self->teammaster;
|
||||
self->teammaster = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (other == self->owner)
|
||||
return;
|
||||
// PMM - don't blow up on bodies
|
||||
if (!strcmp(other->classname, "bodyque"))
|
||||
return;
|
||||
}
|
||||
|
||||
if (surf && (surf->flags & SURF_SKY))
|
||||
{
|
||||
G_FreeEdict (self);
|
||||
return;
|
||||
}
|
||||
|
||||
if (other->takedamage)
|
||||
{
|
||||
T_Damage (other, self, self->owner, self->velocity, self->s.origin, plane->normal,
|
||||
10000, 1, DAMAGE_DESTROY_ARMOR, mod);
|
||||
}
|
||||
else
|
||||
{
|
||||
T_RadiusDamage (self, self->owner, 512, self->owner, 256, mod);
|
||||
}
|
||||
|
||||
sphere_think_explode (self);
|
||||
}
|
||||
|
||||
// =================
|
||||
// =================
|
||||
void vengeance_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
|
||||
{
|
||||
if(self->spawnflags & SPHERE_DOPPLEGANGER)
|
||||
sphere_touch (self, other, plane, surf, MOD_DOPPLE_VENGEANCE);
|
||||
else
|
||||
sphere_touch (self, other, plane, surf, MOD_VENGEANCE_SPHERE);
|
||||
}
|
||||
|
||||
// =================
|
||||
// =================
|
||||
void hunter_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
|
||||
{
|
||||
edict_t *owner;
|
||||
|
||||
// don't blow up if you hit the world.... sheesh.
|
||||
if(other==world)
|
||||
return;
|
||||
|
||||
if(self->owner)
|
||||
{
|
||||
// if owner is flying with us, make sure they stop too.
|
||||
owner=self->owner;
|
||||
if(owner->flags & FL_SAM_RAIMI)
|
||||
{
|
||||
VectorClear(owner->velocity);
|
||||
owner->movetype = MOVETYPE_NONE;
|
||||
gi.linkentity(owner);
|
||||
}
|
||||
}
|
||||
|
||||
if(self->spawnflags & SPHERE_DOPPLEGANGER)
|
||||
sphere_touch (self, other, plane, surf, MOD_DOPPLE_HUNTER);
|
||||
else
|
||||
sphere_touch (self, other, plane, surf, MOD_HUNTER_SPHERE);
|
||||
}
|
||||
|
||||
// =================
|
||||
// =================
|
||||
void defender_shoot (edict_t *self, edict_t *enemy)
|
||||
{
|
||||
vec3_t dir;
|
||||
vec3_t start;
|
||||
|
||||
if(!(enemy->inuse) || enemy->health <= 0)
|
||||
return;
|
||||
|
||||
if(enemy == self->owner)
|
||||
return;
|
||||
|
||||
VectorSubtract (enemy->s.origin, self->s.origin, dir);
|
||||
VectorNormalize (dir);
|
||||
|
||||
if(self->monsterinfo.attack_finished > level.time)
|
||||
return;
|
||||
|
||||
if(!visible(self, self->enemy))
|
||||
return;
|
||||
|
||||
VectorCopy(self->s.origin, start);
|
||||
start[2] += 2;
|
||||
fire_blaster2 (self->owner, start, dir, 10, 1000, EF_BLASTER, 0);
|
||||
|
||||
self->monsterinfo.attack_finished = level.time + 0.4;
|
||||
}
|
||||
|
||||
// *************************
|
||||
// Activation Related Stuff
|
||||
// *************************
|
||||
|
||||
// =================
|
||||
// =================
|
||||
void body_gib (edict_t *self)
|
||||
{
|
||||
int n;
|
||||
|
||||
gi.sound (self, CHAN_BODY, gi.soundindex ("misc/udeath.wav"), 1, ATTN_NORM, 0);
|
||||
for (n= 0; n < 4; n++)
|
||||
ThrowGib (self, "models/objects/gibs/sm_meat/tris.md2", 50, GIB_ORGANIC);
|
||||
ThrowGib (self, "models/objects/gibs/skull/tris.md2", 50, GIB_ORGANIC);
|
||||
}
|
||||
|
||||
// =================
|
||||
// =================
|
||||
void hunter_pain (edict_t *self, edict_t *other, float kick, int damage)
|
||||
{
|
||||
edict_t *owner;
|
||||
float dist;
|
||||
vec3_t dir;
|
||||
|
||||
if(self->enemy)
|
||||
return;
|
||||
|
||||
owner = self->owner;
|
||||
|
||||
if(!(self->spawnflags & SPHERE_DOPPLEGANGER))
|
||||
{
|
||||
if(owner && (owner->health > 0))
|
||||
return;
|
||||
|
||||
//PMM
|
||||
if(other == owner)
|
||||
{
|
||||
// if ((g_showlogic) && (g_showlogic->value))
|
||||
// gi.dprintf ("hunter: won't get mad at my owner!\n");
|
||||
return;
|
||||
}
|
||||
//pmm
|
||||
}
|
||||
else
|
||||
{
|
||||
// if fired by a doppleganger, set it to 10 second timeout
|
||||
self->wait = level.time + MINIMUM_FLY_TIME;
|
||||
}
|
||||
|
||||
if((self->wait - level.time) < MINIMUM_FLY_TIME)
|
||||
self->wait = level.time + MINIMUM_FLY_TIME;
|
||||
self->s.effects |= EF_BLASTER | EF_TRACKER;
|
||||
self->touch = hunter_touch;
|
||||
self->enemy = other;
|
||||
|
||||
// if(g_showlogic && g_showlogic->value)
|
||||
// gi.dprintf("hunter_pain: mad at %s\n", other->classname);
|
||||
|
||||
// if we're not owned by a player, no sam raimi
|
||||
// if we're spawned by a doppleganger, no sam raimi
|
||||
if((self->spawnflags & SPHERE_DOPPLEGANGER) || !(owner && owner->client))
|
||||
return;
|
||||
|
||||
// sam raimi cam is disabled if FORCE_RESPAWN is set.
|
||||
// sam raimi cam is also disabled if huntercam->value is 0.
|
||||
if(!((int)dmflags->value & DF_FORCE_RESPAWN) && (huntercam && (huntercam->value)))
|
||||
{
|
||||
VectorSubtract(other->s.origin, self->s.origin, dir);
|
||||
dist=VectorLength(dir);
|
||||
|
||||
if(owner && (dist >= 192))
|
||||
{
|
||||
// detach owner from body and send him flying
|
||||
owner->movetype = MOVETYPE_FLYMISSILE;
|
||||
|
||||
// gib like we just died, even though we didn't, really.
|
||||
body_gib(owner);
|
||||
|
||||
// move the sphere to the owner's current viewpoint.
|
||||
// we know it's a valid spot (or will be momentarily)
|
||||
VectorCopy(owner->s.origin, self->s.origin);
|
||||
self->s.origin[2] += owner->viewheight;
|
||||
|
||||
// move the player's origin to the sphere's new origin
|
||||
VectorCopy(self->s.origin, owner->s.origin);
|
||||
VectorCopy(self->s.angles, owner->s.angles);
|
||||
VectorCopy(self->s.angles, owner->client->v_angle);
|
||||
VectorClear(owner->mins);
|
||||
VectorClear(owner->maxs);
|
||||
VectorSet(owner->mins, -5, -5, -5);
|
||||
VectorSet(owner->maxs, 5, 5, 5);
|
||||
owner->client->ps.fov = 140;
|
||||
owner->s.modelindex = 0;
|
||||
owner->s.modelindex2 = 0;
|
||||
owner->viewheight = 8;
|
||||
owner->solid = SOLID_NOT;
|
||||
owner->flags |= FL_SAM_RAIMI;
|
||||
gi.linkentity(owner);
|
||||
|
||||
// PMM - set bounding box so we don't clip out of world
|
||||
// VectorSet(self->mins, -5, -5, -5);
|
||||
// VectorSet(self->maxs, 5, 5, 5);
|
||||
self->solid = SOLID_BBOX;
|
||||
gi.linkentity (self);
|
||||
}
|
||||
// else
|
||||
// gi.dprintf("too close for sam raimi cam\n");
|
||||
}
|
||||
}
|
||||
|
||||
// =================
|
||||
// =================
|
||||
void defender_pain (edict_t *self, edict_t *other, float kick, int damage)
|
||||
{
|
||||
//PMM
|
||||
if(other == self->owner)
|
||||
{
|
||||
// if ((g_showlogic) && (g_showlogic->value))
|
||||
// gi.dprintf ("defender: won't get mad at my owner!\n");
|
||||
return;
|
||||
}
|
||||
//pmm
|
||||
self->enemy = other;
|
||||
}
|
||||
|
||||
// =================
|
||||
// =================
|
||||
void vengeance_pain (edict_t *self, edict_t *other, float kick, int damage)
|
||||
{
|
||||
if(self->enemy)
|
||||
return;
|
||||
|
||||
if(!(self->spawnflags & SPHERE_DOPPLEGANGER))
|
||||
{
|
||||
if(self->owner->health >= 25)
|
||||
return;
|
||||
|
||||
//PMM
|
||||
if(other == self->owner)
|
||||
{
|
||||
// if ((g_showlogic) && (g_showlogic->value))
|
||||
// gi.dprintf ("vengeance: won't get mad at my owner!\n");
|
||||
return;
|
||||
}
|
||||
//pmm
|
||||
}
|
||||
else
|
||||
{
|
||||
self->wait = level.time + MINIMUM_FLY_TIME;
|
||||
}
|
||||
|
||||
if((self->wait - level.time) < MINIMUM_FLY_TIME)
|
||||
self->wait = level.time + MINIMUM_FLY_TIME;
|
||||
self->s.effects |= EF_ROCKET;
|
||||
self->touch = vengeance_touch;
|
||||
self->enemy = other;
|
||||
}
|
||||
|
||||
// *************************
|
||||
// Think Functions
|
||||
// *************************
|
||||
|
||||
// ===================
|
||||
// ===================
|
||||
void defender_think (edict_t *self)
|
||||
{
|
||||
if(!self->owner)
|
||||
{
|
||||
// gi.dprintf("think: no owner\n");
|
||||
G_FreeEdict(self);
|
||||
return;
|
||||
}
|
||||
|
||||
// if we've exited the level, just remove ourselves.
|
||||
if (level.intermissiontime)
|
||||
{
|
||||
sphere_think_explode(self);
|
||||
return;
|
||||
}
|
||||
|
||||
if(self->owner->health <=0)
|
||||
{
|
||||
sphere_think_explode(self);
|
||||
return;
|
||||
}
|
||||
|
||||
// if(level.time - self->timestamp > 1)
|
||||
// {
|
||||
// gi.sound (self, CHAN_VOICE, gi.soundindex ("powerup/dsphere.wav"), 0.6, ATTN_NORM, 0);
|
||||
// self->timestamp = level.time;
|
||||
// }
|
||||
|
||||
self->s.frame++;
|
||||
if(self->s.frame>19)
|
||||
self->s.frame = 0;
|
||||
|
||||
if(self->enemy)
|
||||
{
|
||||
if(self->enemy->health > 0)
|
||||
{
|
||||
// gi.dprintf( "shooting at %s\n", self->enemy->classname);
|
||||
defender_shoot (self, self->enemy);
|
||||
}
|
||||
else
|
||||
self->enemy = NULL;
|
||||
}
|
||||
// else
|
||||
// {
|
||||
// self->ideal_yaw+=3;
|
||||
// M_ChangeYaw (self);
|
||||
// }
|
||||
|
||||
sphere_fly (self);
|
||||
|
||||
if(self->inuse)
|
||||
self->nextthink = level.time + 0.1;
|
||||
}
|
||||
|
||||
// =================
|
||||
// =================
|
||||
void hunter_think (edict_t *self)
|
||||
{
|
||||
edict_t *owner;
|
||||
vec3_t dir, ang;
|
||||
|
||||
// if we've exited the level, just remove ourselves.
|
||||
if (level.intermissiontime)
|
||||
{
|
||||
sphere_think_explode(self);
|
||||
return;
|
||||
}
|
||||
|
||||
owner = self->owner;
|
||||
if(!owner && !(self->spawnflags & SPHERE_DOPPLEGANGER))
|
||||
{
|
||||
// gi.dprintf("think: no owner\n");
|
||||
G_FreeEdict(self);
|
||||
return;
|
||||
}
|
||||
|
||||
if(owner)
|
||||
self->ideal_yaw = owner->s.angles[YAW];
|
||||
else if(self->enemy) // fired by doppleganger
|
||||
{
|
||||
VectorSubtract(self->enemy->s.origin, self->s.origin, dir);
|
||||
vectoangles2(dir, ang);
|
||||
self->ideal_yaw = ang[YAW];
|
||||
}
|
||||
|
||||
M_ChangeYaw(self);
|
||||
|
||||
// if(level.time - self->timestamp > 1)
|
||||
// {
|
||||
// gi.sound (self, CHAN_VOICE, gi.soundindex ("powerup/hsphere.wav"), 0.5, ATTN_NORM, 0);
|
||||
// self->timestamp = level.time;
|
||||
// }
|
||||
|
||||
if(self->enemy)
|
||||
{
|
||||
sphere_chase (self, 0);
|
||||
|
||||
// deal with sam raimi cam
|
||||
if(owner && (owner->flags & FL_SAM_RAIMI))
|
||||
{
|
||||
if(self->inuse)
|
||||
{
|
||||
owner->movetype = MOVETYPE_FLYMISSILE;
|
||||
// VectorCopy(self->s.angles, owner->s.angles);
|
||||
// VectorCopy(self->s.angles, owner->client->v_angle);
|
||||
LookAtKiller (owner, self, self->enemy);
|
||||
// owner->viewheight = 22;
|
||||
|
||||
// owner->client->v_angle[YAW]+=5;
|
||||
// owner is flying with us, move him too
|
||||
owner->movetype = MOVETYPE_FLYMISSILE;
|
||||
owner->viewheight = self->s.origin[2] - owner->s.origin[2];
|
||||
// VectorCopy(self->s.angles, owner->s.angles);
|
||||
// VectorCopy(self->s.angles, owner->client->v_angle);
|
||||
VectorCopy(self->s.origin, owner->s.origin);
|
||||
VectorCopy(self->velocity, owner->velocity);
|
||||
VectorClear(owner->mins);
|
||||
VectorClear(owner->maxs);
|
||||
gi.linkentity(owner);
|
||||
}
|
||||
else // sphere timed out
|
||||
{
|
||||
VectorClear(owner->velocity);
|
||||
owner->movetype = MOVETYPE_NONE;
|
||||
gi.linkentity(owner);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// self->ideal_yaw+=3;
|
||||
// M_ChangeYaw (self);
|
||||
sphere_fly (self);
|
||||
}
|
||||
|
||||
if(self->inuse)
|
||||
self->nextthink = level.time + 0.1;
|
||||
}
|
||||
|
||||
// =================
|
||||
// =================
|
||||
void vengeance_think (edict_t *self)
|
||||
{
|
||||
// if we've exited the level, just remove ourselves.
|
||||
if (level.intermissiontime)
|
||||
{
|
||||
sphere_think_explode(self);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!(self->owner) && !(self->spawnflags & SPHERE_DOPPLEGANGER))
|
||||
{
|
||||
// gi.dprintf("think: no owner\n");
|
||||
G_FreeEdict(self);
|
||||
return;
|
||||
}
|
||||
|
||||
// if(level.time - self->timestamp > 1)
|
||||
// {
|
||||
// gi.sound (self, CHAN_VOICE, gi.soundindex ("powerup/vsphere.wav"), 0.5, ATTN_NORM, 0);
|
||||
// self->timestamp = level.time;
|
||||
// }
|
||||
|
||||
if(self->enemy)
|
||||
{
|
||||
// sphere_fire (self, self->owner->enemy);
|
||||
sphere_chase (self, 1);
|
||||
}
|
||||
else
|
||||
sphere_fly (self);
|
||||
|
||||
if(self->inuse)
|
||||
self->nextthink = level.time + 0.1;
|
||||
}
|
||||
|
||||
// *************************
|
||||
// Spawning / Creation
|
||||
// *************************
|
||||
|
||||
// monsterinfo_t
|
||||
// =================
|
||||
// =================
|
||||
edict_t *Sphere_Spawn (edict_t *owner, int spawnflags)
|
||||
{
|
||||
edict_t *sphere;
|
||||
|
||||
sphere = G_Spawn();
|
||||
VectorCopy(owner->s.origin, sphere->s.origin);
|
||||
sphere->s.origin[2] = owner->absmax[2];
|
||||
sphere->s.angles[YAW] = owner->s.angles[YAW];
|
||||
sphere->solid = SOLID_BBOX;
|
||||
sphere->clipmask = MASK_SHOT;
|
||||
sphere->s.renderfx = RF_FULLBRIGHT | RF_IR_VISIBLE;
|
||||
sphere->movetype = MOVETYPE_FLYMISSILE;
|
||||
|
||||
if(spawnflags & SPHERE_DOPPLEGANGER)
|
||||
sphere->teammaster = owner->teammaster;
|
||||
else
|
||||
sphere->owner = owner;
|
||||
|
||||
sphere->classname = "sphere";
|
||||
sphere->yaw_speed = 40;
|
||||
sphere->monsterinfo.attack_finished = 0;
|
||||
sphere->spawnflags = spawnflags; // need this for the HUD to recognize sphere
|
||||
//PMM
|
||||
sphere->takedamage = DAMAGE_NO;
|
||||
|
||||
switch(spawnflags & SPHERE_TYPE)
|
||||
{
|
||||
case SPHERE_DEFENDER:
|
||||
sphere->s.modelindex = gi.modelindex("models/items/defender/tris.md2");
|
||||
// PMM - this doesn't work, causes problems with other stuff
|
||||
// sphere->s.modelindex2 = gi.modelindex("models/items/shell/tris.md2") | 0x80;
|
||||
sphere->s.modelindex2 = gi.modelindex("models/items/shell/tris.md2");
|
||||
sphere->s.sound = gi.soundindex ("spheres/d_idle.wav");
|
||||
sphere->pain = defender_pain;
|
||||
sphere->wait = level.time + DEFENDER_LIFESPAN;
|
||||
sphere->die = sphere_explode;
|
||||
sphere->think = defender_think;
|
||||
break;
|
||||
case SPHERE_HUNTER:
|
||||
sphere->s.modelindex = gi.modelindex("models/items/hunter/tris.md2");
|
||||
sphere->s.sound = gi.soundindex ("spheres/h_idle.wav");
|
||||
sphere->wait = level.time + HUNTER_LIFESPAN;
|
||||
sphere->pain = hunter_pain;
|
||||
sphere->die = sphere_if_idle_die;
|
||||
sphere->think = hunter_think;
|
||||
break;
|
||||
case SPHERE_VENGEANCE:
|
||||
sphere->s.modelindex = gi.modelindex("models/items/vengnce/tris.md2");
|
||||
sphere->s.sound = gi.soundindex ("spheres/v_idle.wav");
|
||||
sphere->wait = level.time + VENGEANCE_LIFESPAN;
|
||||
sphere->pain = vengeance_pain;
|
||||
sphere->die = sphere_if_idle_die;
|
||||
sphere->think = vengeance_think;
|
||||
VectorSet (sphere->avelocity, 30, 30, 0);
|
||||
break;
|
||||
default:
|
||||
gi.dprintf("Tried to create an invalid sphere\n");
|
||||
G_FreeEdict(sphere);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sphere->nextthink = level.time + 0.1;
|
||||
|
||||
gi.linkentity (sphere);
|
||||
|
||||
return sphere;
|
||||
}
|
||||
|
||||
// =================
|
||||
// Own_Sphere - attach the sphere to the client so we can
|
||||
// directly access it later
|
||||
// =================
|
||||
void Own_Sphere (edict_t *self, edict_t *sphere)
|
||||
{
|
||||
if(!sphere)
|
||||
return;
|
||||
|
||||
// ownership only for players
|
||||
if(self->client)
|
||||
{
|
||||
// if they don't have one
|
||||
if(!(self->client->owned_sphere))
|
||||
{
|
||||
self->client->owned_sphere = sphere;
|
||||
}
|
||||
// they already have one, take care of the old one
|
||||
else
|
||||
{
|
||||
if(self->client->owned_sphere->inuse)
|
||||
{
|
||||
G_FreeEdict(self->client->owned_sphere);
|
||||
self->client->owned_sphere = sphere;
|
||||
}
|
||||
else
|
||||
{
|
||||
self->client->owned_sphere = sphere;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// =================
|
||||
// =================
|
||||
void Defender_Launch (edict_t *self)
|
||||
{
|
||||
edict_t *sphere;
|
||||
|
||||
sphere = Sphere_Spawn (self, SPHERE_DEFENDER);
|
||||
Own_Sphere (self, sphere);
|
||||
}
|
||||
|
||||
// =================
|
||||
// =================
|
||||
void Hunter_Launch (edict_t *self)
|
||||
{
|
||||
edict_t *sphere;
|
||||
|
||||
sphere = Sphere_Spawn (self, SPHERE_HUNTER);
|
||||
Own_Sphere (self, sphere);
|
||||
}
|
||||
|
||||
// =================
|
||||
// =================
|
||||
void Vengeance_Launch (edict_t *self)
|
||||
{
|
||||
edict_t *sphere;
|
||||
|
||||
sphere = Sphere_Spawn (self, SPHERE_VENGEANCE);
|
||||
Own_Sphere (self, sphere);
|
||||
}
|
281
g_svcmds.c
Normal file
281
g_svcmds.c
Normal file
|
@ -0,0 +1,281 @@
|
|||
|
||||
#include "g_local.h"
|
||||
|
||||
|
||||
void Svcmd_Test_f (void)
|
||||
{
|
||||
gi.cprintf (NULL, PRINT_HIGH, "Svcmd_Test_f()\n");
|
||||
}
|
||||
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
PACKET FILTERING
|
||||
|
||||
|
||||
You can add or remove addresses from the filter list with:
|
||||
|
||||
addip <ip>
|
||||
removeip <ip>
|
||||
|
||||
The ip address is specified in dot format, and any unspecified digits will match any value, so you can specify an entire class C network with "addip 192.246.40".
|
||||
|
||||
Removeip will only remove an address specified exactly the same way. You cannot addip a subnet, then removeip a single host.
|
||||
|
||||
listip
|
||||
Prints the current list of filters.
|
||||
|
||||
writeip
|
||||
Dumps "addip <ip>" commands to listip.cfg so it can be execed at a later date. The filter lists are not saved and restored by default, because I beleive it would cause too much confusion.
|
||||
|
||||
filterban <0 or 1>
|
||||
|
||||
If 1 (the default), then ip addresses matching the current list will be prohibited from entering the game. This is the default setting.
|
||||
|
||||
If 0, then only addresses matching the list will be allowed. This lets you easily set up a private game, or a game that only allows players from your local network.
|
||||
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
typedef struct
|
||||
{
|
||||
unsigned mask;
|
||||
unsigned compare;
|
||||
} ipfilter_t;
|
||||
|
||||
#define MAX_IPFILTERS 1024
|
||||
|
||||
ipfilter_t ipfilters[MAX_IPFILTERS];
|
||||
int numipfilters;
|
||||
|
||||
/*
|
||||
=================
|
||||
StringToFilter
|
||||
=================
|
||||
*/
|
||||
static qboolean StringToFilter (char *s, ipfilter_t *f)
|
||||
{
|
||||
char num[128];
|
||||
int i, j;
|
||||
byte b[4];
|
||||
byte m[4];
|
||||
|
||||
for (i=0 ; i<4 ; i++)
|
||||
{
|
||||
b[i] = 0;
|
||||
m[i] = 0;
|
||||
}
|
||||
|
||||
for (i=0 ; i<4 ; i++)
|
||||
{
|
||||
if (*s < '0' || *s > '9')
|
||||
{
|
||||
gi.cprintf(NULL, PRINT_HIGH, "Bad filter address: %s\n", s);
|
||||
return false;
|
||||
}
|
||||
|
||||
j = 0;
|
||||
while (*s >= '0' && *s <= '9')
|
||||
{
|
||||
num[j++] = *s++;
|
||||
}
|
||||
num[j] = 0;
|
||||
b[i] = atoi(num);
|
||||
if (b[i] != 0)
|
||||
m[i] = 255;
|
||||
|
||||
if (!*s)
|
||||
break;
|
||||
s++;
|
||||
}
|
||||
|
||||
f->mask = *(unsigned *)m;
|
||||
f->compare = *(unsigned *)b;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
SV_FilterPacket
|
||||
=================
|
||||
*/
|
||||
qboolean SV_FilterPacket (char *from)
|
||||
{
|
||||
int i;
|
||||
unsigned in;
|
||||
byte m[4];
|
||||
char *p;
|
||||
|
||||
i = 0;
|
||||
p = from;
|
||||
while (*p && i < 4) {
|
||||
m[i] = 0;
|
||||
while (*p >= '0' && *p <= '9') {
|
||||
m[i] = m[i]*10 + (*p - '0');
|
||||
p++;
|
||||
}
|
||||
if (!*p || *p == ':')
|
||||
break;
|
||||
i++, p++;
|
||||
}
|
||||
|
||||
in = *(unsigned *)m;
|
||||
|
||||
for (i=0 ; i<numipfilters ; i++)
|
||||
if ( (in & ipfilters[i].mask) == ipfilters[i].compare)
|
||||
return (int)filterban->value;
|
||||
|
||||
return (int)!filterban->value;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=================
|
||||
SV_AddIP_f
|
||||
=================
|
||||
*/
|
||||
void SVCmd_AddIP_f (void)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (gi.argc() < 3) {
|
||||
gi.cprintf(NULL, PRINT_HIGH, "Usage: addip <ip-mask>\n");
|
||||
return;
|
||||
}
|
||||
|
||||
for (i=0 ; i<numipfilters ; i++)
|
||||
if (ipfilters[i].compare == 0xffffffff)
|
||||
break; // free spot
|
||||
if (i == numipfilters)
|
||||
{
|
||||
if (numipfilters == MAX_IPFILTERS)
|
||||
{
|
||||
gi.cprintf (NULL, PRINT_HIGH, "IP filter list is full\n");
|
||||
return;
|
||||
}
|
||||
numipfilters++;
|
||||
}
|
||||
|
||||
if (!StringToFilter (gi.argv(2), &ipfilters[i]))
|
||||
ipfilters[i].compare = 0xffffffff;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
SV_RemoveIP_f
|
||||
=================
|
||||
*/
|
||||
void SVCmd_RemoveIP_f (void)
|
||||
{
|
||||
ipfilter_t f;
|
||||
int i, j;
|
||||
|
||||
if (gi.argc() < 3) {
|
||||
gi.cprintf(NULL, PRINT_HIGH, "Usage: sv removeip <ip-mask>\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!StringToFilter (gi.argv(2), &f))
|
||||
return;
|
||||
|
||||
for (i=0 ; i<numipfilters ; i++)
|
||||
if (ipfilters[i].mask == f.mask
|
||||
&& ipfilters[i].compare == f.compare)
|
||||
{
|
||||
for (j=i+1 ; j<numipfilters ; j++)
|
||||
ipfilters[j-1] = ipfilters[j];
|
||||
numipfilters--;
|
||||
gi.cprintf (NULL, PRINT_HIGH, "Removed.\n");
|
||||
return;
|
||||
}
|
||||
gi.cprintf (NULL, PRINT_HIGH, "Didn't find %s.\n", gi.argv(2));
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
SV_ListIP_f
|
||||
=================
|
||||
*/
|
||||
void SVCmd_ListIP_f (void)
|
||||
{
|
||||
int i;
|
||||
byte b[4];
|
||||
|
||||
gi.cprintf (NULL, PRINT_HIGH, "Filter list:\n");
|
||||
for (i=0 ; i<numipfilters ; i++)
|
||||
{
|
||||
*(unsigned *)b = ipfilters[i].compare;
|
||||
gi.cprintf (NULL, PRINT_HIGH, "%3i.%3i.%3i.%3i\n", b[0], b[1], b[2], b[3]);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
SV_WriteIP_f
|
||||
=================
|
||||
*/
|
||||
void SVCmd_WriteIP_f (void)
|
||||
{
|
||||
FILE *f;
|
||||
char name[MAX_OSPATH];
|
||||
byte b[4];
|
||||
int i;
|
||||
cvar_t *game;
|
||||
|
||||
game = gi.cvar("game", "", 0);
|
||||
|
||||
if (!*game->string)
|
||||
sprintf (name, "%s/listip.cfg", GAMEVERSION);
|
||||
else
|
||||
sprintf (name, "%s/listip.cfg", game->string);
|
||||
|
||||
gi.cprintf (NULL, PRINT_HIGH, "Writing %s.\n", name);
|
||||
|
||||
f = fopen (name, "wb");
|
||||
if (!f)
|
||||
{
|
||||
gi.cprintf (NULL, PRINT_HIGH, "Couldn't open %s\n", name);
|
||||
return;
|
||||
}
|
||||
|
||||
fprintf(f, "set filterban %d\n", (int)filterban->value);
|
||||
|
||||
for (i=0 ; i<numipfilters ; i++)
|
||||
{
|
||||
*(unsigned *)b = ipfilters[i].compare;
|
||||
fprintf (f, "sv addip %i.%i.%i.%i\n", b[0], b[1], b[2], b[3]);
|
||||
}
|
||||
|
||||
fclose (f);
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
ServerCommand
|
||||
|
||||
ServerCommand will be called when an "sv" command is issued.
|
||||
The game can issue gi.argc() / gi.argv() commands to get the rest
|
||||
of the parameters
|
||||
=================
|
||||
*/
|
||||
void ServerCommand (void)
|
||||
{
|
||||
char *cmd;
|
||||
|
||||
cmd = gi.argv(1);
|
||||
if (Q_stricmp (cmd, "test") == 0)
|
||||
Svcmd_Test_f ();
|
||||
else if (Q_stricmp (cmd, "addip") == 0)
|
||||
SVCmd_AddIP_f ();
|
||||
else if (Q_stricmp (cmd, "removeip") == 0)
|
||||
SVCmd_RemoveIP_f ();
|
||||
else if (Q_stricmp (cmd, "listip") == 0)
|
||||
SVCmd_ListIP_f ();
|
||||
else if (Q_stricmp (cmd, "writeip") == 0)
|
||||
SVCmd_WriteIP_f ();
|
||||
else
|
||||
gi.cprintf (NULL, PRINT_HIGH, "Unknown server command \"%s\"\n", cmd);
|
||||
}
|
||||
|
825
g_target.c
Normal file
825
g_target.c
Normal file
|
@ -0,0 +1,825 @@
|
|||
#include "g_local.h"
|
||||
|
||||
/*QUAKED target_temp_entity (1 0 0) (-8 -8 -8) (8 8 8)
|
||||
Fire an origin based temp entity event to the clients.
|
||||
"style" type byte
|
||||
*/
|
||||
void Use_Target_Tent (edict_t *ent, edict_t *other, edict_t *activator)
|
||||
{
|
||||
gi.WriteByte (svc_temp_entity);
|
||||
gi.WriteByte (ent->style);
|
||||
gi.WritePosition (ent->s.origin);
|
||||
gi.multicast (ent->s.origin, MULTICAST_PVS);
|
||||
}
|
||||
|
||||
void SP_target_temp_entity (edict_t *ent)
|
||||
{
|
||||
ent->use = Use_Target_Tent;
|
||||
}
|
||||
|
||||
|
||||
//==========================================================
|
||||
|
||||
//==========================================================
|
||||
|
||||
/*QUAKED target_speaker (1 0 0) (-8 -8 -8) (8 8 8) looped-on looped-off reliable
|
||||
"noise" wav file to play
|
||||
"attenuation"
|
||||
-1 = none, send to whole level
|
||||
1 = normal fighting sounds
|
||||
2 = idle sound level
|
||||
3 = ambient sound level
|
||||
"volume" 0.0 to 1.0
|
||||
|
||||
Normal sounds play each time the target is used. The reliable flag can be set for crucial voiceovers.
|
||||
|
||||
Looped sounds are always atten 3 / vol 1, and the use function toggles it on/off.
|
||||
Multiple identical looping sounds will just increase volume without any speed cost.
|
||||
*/
|
||||
void Use_Target_Speaker (edict_t *ent, edict_t *other, edict_t *activator)
|
||||
{
|
||||
int chan;
|
||||
|
||||
if (ent->spawnflags & 3)
|
||||
{ // looping sound toggles
|
||||
if (ent->s.sound)
|
||||
ent->s.sound = 0; // turn it off
|
||||
else
|
||||
ent->s.sound = ent->noise_index; // start it
|
||||
}
|
||||
else
|
||||
{ // normal sound
|
||||
if (ent->spawnflags & 4)
|
||||
chan = CHAN_VOICE|CHAN_RELIABLE;
|
||||
else
|
||||
chan = CHAN_VOICE;
|
||||
// use a positioned_sound, because this entity won't normally be
|
||||
// sent to any clients because it is invisible
|
||||
gi.positioned_sound (ent->s.origin, ent, chan, ent->noise_index, ent->volume, ent->attenuation, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void SP_target_speaker (edict_t *ent)
|
||||
{
|
||||
char buffer[MAX_QPATH];
|
||||
|
||||
if(!st.noise)
|
||||
{
|
||||
gi.dprintf("target_speaker with no noise set at %s\n", vtos(ent->s.origin));
|
||||
return;
|
||||
}
|
||||
if (!strstr (st.noise, ".wav"))
|
||||
Com_sprintf (buffer, sizeof(buffer), "%s.wav", st.noise);
|
||||
else
|
||||
strncpy (buffer, st.noise, sizeof(buffer));
|
||||
ent->noise_index = gi.soundindex (buffer);
|
||||
|
||||
if (!ent->volume)
|
||||
ent->volume = 1.0;
|
||||
|
||||
if (!ent->attenuation)
|
||||
ent->attenuation = 1.0;
|
||||
else if (ent->attenuation == -1) // use -1 so 0 defaults to 1
|
||||
ent->attenuation = 0;
|
||||
|
||||
// check for prestarted looping sound
|
||||
if (ent->spawnflags & 1)
|
||||
ent->s.sound = ent->noise_index;
|
||||
|
||||
ent->use = Use_Target_Speaker;
|
||||
|
||||
// must link the entity so we get areas and clusters so
|
||||
// the server can determine who to send updates to
|
||||
gi.linkentity (ent);
|
||||
}
|
||||
|
||||
|
||||
//==========================================================
|
||||
|
||||
void Use_Target_Help (edict_t *ent, edict_t *other, edict_t *activator)
|
||||
{
|
||||
if (ent->spawnflags & 1)
|
||||
strncpy (game.helpmessage1, ent->message, sizeof(game.helpmessage2)-1);
|
||||
else
|
||||
strncpy (game.helpmessage2, ent->message, sizeof(game.helpmessage1)-1);
|
||||
|
||||
game.helpchanged++;
|
||||
}
|
||||
|
||||
/*QUAKED target_help (1 0 1) (-16 -16 -24) (16 16 24) help1
|
||||
When fired, the "message" key becomes the current personal computer string, and the message light will be set on all clients status bars.
|
||||
*/
|
||||
void SP_target_help(edict_t *ent)
|
||||
{
|
||||
if (deathmatch->value)
|
||||
{ // auto-remove for deathmatch
|
||||
G_FreeEdict (ent);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ent->message)
|
||||
{
|
||||
gi.dprintf ("%s with no message at %s\n", ent->classname, vtos(ent->s.origin));
|
||||
G_FreeEdict (ent);
|
||||
return;
|
||||
}
|
||||
ent->use = Use_Target_Help;
|
||||
}
|
||||
|
||||
//==========================================================
|
||||
|
||||
/*QUAKED target_secret (1 0 1) (-8 -8 -8) (8 8 8)
|
||||
Counts a secret found.
|
||||
These are single use targets.
|
||||
*/
|
||||
void use_target_secret (edict_t *ent, edict_t *other, edict_t *activator)
|
||||
{
|
||||
gi.sound (ent, CHAN_VOICE, ent->noise_index, 1, ATTN_NORM, 0);
|
||||
|
||||
level.found_secrets++;
|
||||
|
||||
G_UseTargets (ent, activator);
|
||||
G_FreeEdict (ent);
|
||||
}
|
||||
|
||||
void SP_target_secret (edict_t *ent)
|
||||
{
|
||||
if (deathmatch->value)
|
||||
{ // auto-remove for deathmatch
|
||||
G_FreeEdict (ent);
|
||||
return;
|
||||
}
|
||||
|
||||
ent->use = use_target_secret;
|
||||
if (!st.noise)
|
||||
st.noise = "misc/secret.wav";
|
||||
ent->noise_index = gi.soundindex (st.noise);
|
||||
ent->svflags = SVF_NOCLIENT;
|
||||
level.total_secrets++;
|
||||
// map bug hack
|
||||
if (!Q_stricmp(level.mapname, "mine3") && ent->s.origin[0] == 280 && ent->s.origin[1] == -2048 && ent->s.origin[2] == -624)
|
||||
ent->message = "You have found a secret area.";
|
||||
}
|
||||
|
||||
//==========================================================
|
||||
|
||||
/*QUAKED target_goal (1 0 1) (-8 -8 -8) (8 8 8)
|
||||
Counts a goal completed.
|
||||
These are single use targets.
|
||||
*/
|
||||
void use_target_goal (edict_t *ent, edict_t *other, edict_t *activator)
|
||||
{
|
||||
gi.sound (ent, CHAN_VOICE, ent->noise_index, 1, ATTN_NORM, 0);
|
||||
|
||||
level.found_goals++;
|
||||
|
||||
if (level.found_goals == level.total_goals)
|
||||
gi.configstring (CS_CDTRACK, "0");
|
||||
|
||||
G_UseTargets (ent, activator);
|
||||
G_FreeEdict (ent);
|
||||
}
|
||||
|
||||
void SP_target_goal (edict_t *ent)
|
||||
{
|
||||
if (deathmatch->value)
|
||||
{ // auto-remove for deathmatch
|
||||
G_FreeEdict (ent);
|
||||
return;
|
||||
}
|
||||
|
||||
ent->use = use_target_goal;
|
||||
if (!st.noise)
|
||||
st.noise = "misc/secret.wav";
|
||||
ent->noise_index = gi.soundindex (st.noise);
|
||||
ent->svflags = SVF_NOCLIENT;
|
||||
level.total_goals++;
|
||||
}
|
||||
|
||||
//==========================================================
|
||||
|
||||
|
||||
/*QUAKED target_explosion (1 0 0) (-8 -8 -8) (8 8 8)
|
||||
Spawns an explosion temporary entity when used.
|
||||
|
||||
"delay" wait this long before going off
|
||||
"dmg" how much radius damage should be done, defaults to 0
|
||||
*/
|
||||
void target_explosion_explode (edict_t *self)
|
||||
{
|
||||
float save;
|
||||
|
||||
gi.WriteByte (svc_temp_entity);
|
||||
gi.WriteByte (TE_EXPLOSION1);
|
||||
gi.WritePosition (self->s.origin);
|
||||
gi.multicast (self->s.origin, MULTICAST_PHS);
|
||||
|
||||
T_RadiusDamage (self, self->activator, self->dmg, NULL, self->dmg+40, MOD_EXPLOSIVE);
|
||||
|
||||
save = self->delay;
|
||||
self->delay = 0;
|
||||
G_UseTargets (self, self->activator);
|
||||
self->delay = save;
|
||||
}
|
||||
|
||||
void use_target_explosion (edict_t *self, edict_t *other, edict_t *activator)
|
||||
{
|
||||
self->activator = activator;
|
||||
|
||||
if (!self->delay)
|
||||
{
|
||||
target_explosion_explode (self);
|
||||
return;
|
||||
}
|
||||
|
||||
self->think = target_explosion_explode;
|
||||
self->nextthink = level.time + self->delay;
|
||||
}
|
||||
|
||||
void SP_target_explosion (edict_t *ent)
|
||||
{
|
||||
ent->use = use_target_explosion;
|
||||
ent->svflags = SVF_NOCLIENT;
|
||||
}
|
||||
|
||||
|
||||
//==========================================================
|
||||
|
||||
/*QUAKED target_changelevel (1 0 0) (-8 -8 -8) (8 8 8)
|
||||
Changes level to "map" when fired
|
||||
*/
|
||||
void use_target_changelevel (edict_t *self, edict_t *other, edict_t *activator)
|
||||
{
|
||||
if (level.intermissiontime)
|
||||
return; // already activated
|
||||
|
||||
if (!deathmatch->value && !coop->value)
|
||||
{
|
||||
if (g_edicts[1].health <= 0)
|
||||
return;
|
||||
}
|
||||
|
||||
// if noexit, do a ton of damage to other
|
||||
if (deathmatch->value && !( (int)dmflags->value & DF_ALLOW_EXIT) && other != world)
|
||||
{
|
||||
T_Damage (other, self, self, vec3_origin, other->s.origin, vec3_origin, 10 * other->max_health, 1000, 0, MOD_EXIT);
|
||||
return;
|
||||
}
|
||||
|
||||
// if multiplayer, let everyone know who hit the exit
|
||||
if (deathmatch->value)
|
||||
{
|
||||
if (activator && activator->client)
|
||||
gi.bprintf (PRINT_HIGH, "%s exited the level.\n", activator->client->pers.netname);
|
||||
}
|
||||
|
||||
// if going to a new unit, clear cross triggers
|
||||
if (strstr(self->map, "*"))
|
||||
game.serverflags &= ~(SFL_CROSS_TRIGGER_MASK);
|
||||
|
||||
BeginIntermission (self);
|
||||
}
|
||||
|
||||
void SP_target_changelevel (edict_t *ent)
|
||||
{
|
||||
if (!ent->map)
|
||||
{
|
||||
gi.dprintf("target_changelevel with no map at %s\n", vtos(ent->s.origin));
|
||||
G_FreeEdict (ent);
|
||||
return;
|
||||
}
|
||||
|
||||
// ugly hack because *SOMEBODY* screwed up their map
|
||||
if((Q_stricmp(level.mapname, "fact1") == 0) && (Q_stricmp(ent->map, "fact3") == 0))
|
||||
ent->map = "fact3$secret1";
|
||||
|
||||
ent->use = use_target_changelevel;
|
||||
ent->svflags = SVF_NOCLIENT;
|
||||
}
|
||||
|
||||
|
||||
//==========================================================
|
||||
|
||||
/*QUAKED target_splash (1 0 0) (-8 -8 -8) (8 8 8)
|
||||
Creates a particle splash effect when used.
|
||||
|
||||
Set "sounds" to one of the following:
|
||||
1) sparks
|
||||
2) blue water
|
||||
3) brown water
|
||||
4) slime
|
||||
5) lava
|
||||
6) blood
|
||||
|
||||
"count" how many pixels in the splash
|
||||
"dmg" if set, does a radius damage at this location when it splashes
|
||||
useful for lava/sparks
|
||||
*/
|
||||
|
||||
void use_target_splash (edict_t *self, edict_t *other, edict_t *activator)
|
||||
{
|
||||
gi.WriteByte (svc_temp_entity);
|
||||
gi.WriteByte (TE_SPLASH);
|
||||
gi.WriteByte (self->count);
|
||||
gi.WritePosition (self->s.origin);
|
||||
gi.WriteDir (self->movedir);
|
||||
gi.WriteByte (self->sounds);
|
||||
gi.multicast (self->s.origin, MULTICAST_PVS);
|
||||
|
||||
if (self->dmg)
|
||||
T_RadiusDamage (self, activator, self->dmg, NULL, self->dmg+40, MOD_SPLASH);
|
||||
}
|
||||
|
||||
void SP_target_splash (edict_t *self)
|
||||
{
|
||||
self->use = use_target_splash;
|
||||
G_SetMovedir (self->s.angles, self->movedir);
|
||||
|
||||
if (!self->count)
|
||||
self->count = 32;
|
||||
|
||||
self->svflags = SVF_NOCLIENT;
|
||||
}
|
||||
|
||||
|
||||
//==========================================================
|
||||
|
||||
/*QUAKED target_spawner (1 0 0) (-8 -8 -8) (8 8 8)
|
||||
Set target to the type of entity you want spawned.
|
||||
Useful for spawning monsters and gibs in the factory levels.
|
||||
|
||||
For monsters:
|
||||
Set direction to the facing you want it to have.
|
||||
|
||||
For gibs:
|
||||
Set direction if you want it moving and
|
||||
speed how fast it should be moving otherwise it
|
||||
will just be dropped
|
||||
*/
|
||||
void ED_CallSpawn (edict_t *ent);
|
||||
|
||||
void use_target_spawner (edict_t *self, edict_t *other, edict_t *activator)
|
||||
{
|
||||
edict_t *ent;
|
||||
|
||||
ent = G_Spawn();
|
||||
ent->classname = self->target;
|
||||
VectorCopy (self->s.origin, ent->s.origin);
|
||||
VectorCopy (self->s.angles, ent->s.angles);
|
||||
ED_CallSpawn (ent);
|
||||
gi.unlinkentity (ent);
|
||||
KillBox (ent);
|
||||
gi.linkentity (ent);
|
||||
if (self->speed)
|
||||
VectorCopy (self->movedir, ent->velocity);
|
||||
|
||||
ent->s.renderfx |= RF_IR_VISIBLE; //PGM
|
||||
}
|
||||
|
||||
void SP_target_spawner (edict_t *self)
|
||||
{
|
||||
self->use = use_target_spawner;
|
||||
self->svflags = SVF_NOCLIENT;
|
||||
if (self->speed)
|
||||
{
|
||||
G_SetMovedir (self->s.angles, self->movedir);
|
||||
VectorScale (self->movedir, self->speed, self->movedir);
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================
|
||||
|
||||
/*QUAKED target_blaster (1 0 0) (-8 -8 -8) (8 8 8) NOTRAIL NOEFFECTS
|
||||
Fires a blaster bolt in the set direction when triggered.
|
||||
|
||||
dmg default is 15
|
||||
speed default is 1000
|
||||
*/
|
||||
|
||||
void use_target_blaster (edict_t *self, edict_t *other, edict_t *activator)
|
||||
{
|
||||
int effect;
|
||||
|
||||
if (self->spawnflags & 2)
|
||||
effect = 0;
|
||||
else if (self->spawnflags & 1)
|
||||
effect = EF_HYPERBLASTER;
|
||||
else
|
||||
effect = EF_BLASTER;
|
||||
|
||||
fire_blaster (self, self->s.origin, self->movedir, self->dmg, self->speed, EF_BLASTER, MOD_TARGET_BLASTER);
|
||||
gi.sound (self, CHAN_VOICE, self->noise_index, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
void SP_target_blaster (edict_t *self)
|
||||
{
|
||||
self->use = use_target_blaster;
|
||||
G_SetMovedir (self->s.angles, self->movedir);
|
||||
self->noise_index = gi.soundindex ("weapons/laser2.wav");
|
||||
|
||||
if (!self->dmg)
|
||||
self->dmg = 15;
|
||||
if (!self->speed)
|
||||
self->speed = 1000;
|
||||
|
||||
self->svflags = SVF_NOCLIENT;
|
||||
}
|
||||
|
||||
|
||||
//==========================================================
|
||||
|
||||
/*QUAKED target_crosslevel_trigger (.5 .5 .5) (-8 -8 -8) (8 8 8) trigger1 trigger2 trigger3 trigger4 trigger5 trigger6 trigger7 trigger8
|
||||
Once this trigger is touched/used, any trigger_crosslevel_target with the same trigger number is automatically used when a level is started within the same unit. It is OK to check multiple triggers. Message, delay, target, and killtarget also work.
|
||||
*/
|
||||
void trigger_crosslevel_trigger_use (edict_t *self, edict_t *other, edict_t *activator)
|
||||
{
|
||||
game.serverflags |= self->spawnflags;
|
||||
G_FreeEdict (self);
|
||||
}
|
||||
|
||||
void SP_target_crosslevel_trigger (edict_t *self)
|
||||
{
|
||||
self->svflags = SVF_NOCLIENT;
|
||||
self->use = trigger_crosslevel_trigger_use;
|
||||
}
|
||||
|
||||
/*QUAKED target_crosslevel_target (.5 .5 .5) (-8 -8 -8) (8 8 8) trigger1 trigger2 trigger3 trigger4 trigger5 trigger6 trigger7 trigger8
|
||||
Triggered by a trigger_crosslevel elsewhere within a unit. If multiple triggers are checked, all must be true. Delay, target and
|
||||
killtarget also work.
|
||||
|
||||
"delay" delay before using targets if the trigger has been activated (default 1)
|
||||
*/
|
||||
void target_crosslevel_target_think (edict_t *self)
|
||||
{
|
||||
if (self->spawnflags == (game.serverflags & SFL_CROSS_TRIGGER_MASK & self->spawnflags))
|
||||
{
|
||||
G_UseTargets (self, self);
|
||||
G_FreeEdict (self);
|
||||
}
|
||||
}
|
||||
|
||||
void SP_target_crosslevel_target (edict_t *self)
|
||||
{
|
||||
if (! self->delay)
|
||||
self->delay = 1;
|
||||
self->svflags = SVF_NOCLIENT;
|
||||
|
||||
self->think = target_crosslevel_target_think;
|
||||
self->nextthink = level.time + self->delay;
|
||||
}
|
||||
|
||||
//==========================================================
|
||||
|
||||
/*QUAKED target_laser (0 .5 .8) (-8 -8 -8) (8 8 8) START_ON RED GREEN BLUE YELLOW ORANGE FAT WINDOWSTOP
|
||||
When triggered, fires a laser. You can either set a target
|
||||
or a direction.
|
||||
|
||||
WINDOWSTOP - stops at CONTENTS_WINDOW
|
||||
*/
|
||||
|
||||
//======
|
||||
// PGM
|
||||
#define LASER_ON 0x0001
|
||||
#define LASER_RED 0x0002
|
||||
#define LASER_GREEN 0x0004
|
||||
#define LASER_BLUE 0x0008
|
||||
#define LASER_YELLOW 0x0010
|
||||
#define LASER_ORANGE 0x0020
|
||||
#define LASER_FAT 0x0040
|
||||
#define LASER_STOPWINDOW 0x0080
|
||||
// PGM
|
||||
//======
|
||||
|
||||
void target_laser_think (edict_t *self)
|
||||
{
|
||||
edict_t *ignore;
|
||||
vec3_t start;
|
||||
vec3_t end;
|
||||
trace_t tr;
|
||||
vec3_t point;
|
||||
vec3_t last_movedir;
|
||||
int count;
|
||||
|
||||
if (self->spawnflags & 0x80000000)
|
||||
count = 8;
|
||||
else
|
||||
count = 4;
|
||||
|
||||
if (self->enemy)
|
||||
{
|
||||
VectorCopy (self->movedir, last_movedir);
|
||||
VectorMA (self->enemy->absmin, 0.5, self->enemy->size, point);
|
||||
VectorSubtract (point, self->s.origin, self->movedir);
|
||||
VectorNormalize (self->movedir);
|
||||
if (!VectorCompare(self->movedir, last_movedir))
|
||||
self->spawnflags |= 0x80000000;
|
||||
}
|
||||
|
||||
ignore = self;
|
||||
VectorCopy (self->s.origin, start);
|
||||
VectorMA (start, 2048, self->movedir, end);
|
||||
while(1)
|
||||
{
|
||||
//======
|
||||
// PGM
|
||||
if(self->spawnflags & LASER_STOPWINDOW)
|
||||
tr = gi.trace (start, NULL, NULL, end, ignore, MASK_SHOT);
|
||||
else
|
||||
tr = gi.trace (start, NULL, NULL, end, ignore, CONTENTS_SOLID|CONTENTS_MONSTER|CONTENTS_DEADMONSTER);
|
||||
// PGM
|
||||
//======
|
||||
|
||||
if (!tr.ent)
|
||||
break;
|
||||
|
||||
// hurt it if we can
|
||||
if ((tr.ent->takedamage) && !(tr.ent->flags & FL_IMMUNE_LASER))
|
||||
T_Damage (tr.ent, self, self->activator, self->movedir, tr.endpos, vec3_origin, self->dmg, 1, DAMAGE_ENERGY, MOD_TARGET_LASER);
|
||||
|
||||
// if we hit something that's not a monster or player or is immune to lasers, we're done
|
||||
// if (!(tr.ent->svflags & SVF_MONSTER) && (!tr.ent->client))
|
||||
//PMM added SVF_DAMAGEABLE
|
||||
if (!(tr.ent->svflags & SVF_MONSTER) && (!tr.ent->client) && !(tr.ent->svflags & SVF_DAMAGEABLE))
|
||||
{
|
||||
if (self->spawnflags & 0x80000000)
|
||||
{
|
||||
self->spawnflags &= ~0x80000000;
|
||||
gi.WriteByte (svc_temp_entity);
|
||||
gi.WriteByte (TE_LASER_SPARKS);
|
||||
gi.WriteByte (count);
|
||||
gi.WritePosition (tr.endpos);
|
||||
gi.WriteDir (tr.plane.normal);
|
||||
gi.WriteByte (self->s.skinnum);
|
||||
gi.multicast (tr.endpos, MULTICAST_PVS);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
ignore = tr.ent;
|
||||
VectorCopy (tr.endpos, start);
|
||||
}
|
||||
|
||||
VectorCopy (tr.endpos, self->s.old_origin);
|
||||
|
||||
self->nextthink = level.time + FRAMETIME;
|
||||
}
|
||||
|
||||
void target_laser_on (edict_t *self)
|
||||
{
|
||||
if (!self->activator)
|
||||
self->activator = self;
|
||||
self->spawnflags |= 0x80000001;
|
||||
self->svflags &= ~SVF_NOCLIENT;
|
||||
target_laser_think (self);
|
||||
}
|
||||
|
||||
void target_laser_off (edict_t *self)
|
||||
{
|
||||
self->spawnflags &= ~1;
|
||||
self->svflags |= SVF_NOCLIENT;
|
||||
self->nextthink = 0;
|
||||
}
|
||||
|
||||
void target_laser_use (edict_t *self, edict_t *other, edict_t *activator)
|
||||
{
|
||||
self->activator = activator;
|
||||
if (self->spawnflags & 1)
|
||||
target_laser_off (self);
|
||||
else
|
||||
target_laser_on (self);
|
||||
}
|
||||
|
||||
void target_laser_start (edict_t *self)
|
||||
{
|
||||
edict_t *ent;
|
||||
|
||||
self->movetype = MOVETYPE_NONE;
|
||||
self->solid = SOLID_NOT;
|
||||
self->s.renderfx |= RF_BEAM|RF_TRANSLUCENT;
|
||||
self->s.modelindex = 1; // must be non-zero
|
||||
|
||||
// set the beam diameter
|
||||
if (self->spawnflags & 64)
|
||||
self->s.frame = 16;
|
||||
else
|
||||
self->s.frame = 4;
|
||||
|
||||
// set the color
|
||||
if (self->spawnflags & 2)
|
||||
self->s.skinnum = 0xf2f2f0f0;
|
||||
else if (self->spawnflags & 4)
|
||||
self->s.skinnum = 0xd0d1d2d3;
|
||||
else if (self->spawnflags & 8)
|
||||
self->s.skinnum = 0xf3f3f1f1;
|
||||
else if (self->spawnflags & 16)
|
||||
self->s.skinnum = 0xdcdddedf;
|
||||
else if (self->spawnflags & 32)
|
||||
self->s.skinnum = 0xe0e1e2e3;
|
||||
|
||||
if (!self->enemy)
|
||||
{
|
||||
if (self->target)
|
||||
{
|
||||
ent = G_Find (NULL, FOFS(targetname), self->target);
|
||||
if (!ent)
|
||||
gi.dprintf ("%s at %s: %s is a bad target\n", self->classname, vtos(self->s.origin), self->target);
|
||||
self->enemy = ent;
|
||||
}
|
||||
else
|
||||
{
|
||||
G_SetMovedir (self->s.angles, self->movedir);
|
||||
}
|
||||
}
|
||||
self->use = target_laser_use;
|
||||
self->think = target_laser_think;
|
||||
|
||||
if (!self->dmg)
|
||||
self->dmg = 1;
|
||||
|
||||
VectorSet (self->mins, -8, -8, -8);
|
||||
VectorSet (self->maxs, 8, 8, 8);
|
||||
gi.linkentity (self);
|
||||
|
||||
if (self->spawnflags & 1)
|
||||
target_laser_on (self);
|
||||
else
|
||||
target_laser_off (self);
|
||||
}
|
||||
|
||||
void SP_target_laser (edict_t *self)
|
||||
{
|
||||
// let everything else get spawned before we start firing
|
||||
self->think = target_laser_start;
|
||||
self->nextthink = level.time + 1;
|
||||
}
|
||||
|
||||
//==========================================================
|
||||
|
||||
/*QUAKED target_lightramp (0 .5 .8) (-8 -8 -8) (8 8 8) TOGGLE
|
||||
speed How many seconds the ramping will take
|
||||
message two letters; starting lightlevel and ending lightlevel
|
||||
*/
|
||||
|
||||
void target_lightramp_think (edict_t *self)
|
||||
{
|
||||
char style[2];
|
||||
|
||||
style[0] = 'a' + self->movedir[0] + (level.time - self->timestamp) / FRAMETIME * self->movedir[2];
|
||||
style[1] = 0;
|
||||
gi.configstring (CS_LIGHTS+self->enemy->style, style);
|
||||
|
||||
if ((level.time - self->timestamp) < self->speed)
|
||||
{
|
||||
self->nextthink = level.time + FRAMETIME;
|
||||
}
|
||||
else if (self->spawnflags & 1)
|
||||
{
|
||||
char temp;
|
||||
|
||||
temp = self->movedir[0];
|
||||
self->movedir[0] = self->movedir[1];
|
||||
self->movedir[1] = temp;
|
||||
self->movedir[2] *= -1;
|
||||
}
|
||||
}
|
||||
|
||||
void target_lightramp_use (edict_t *self, edict_t *other, edict_t *activator)
|
||||
{
|
||||
if (!self->enemy)
|
||||
{
|
||||
edict_t *e;
|
||||
|
||||
// check all the targets
|
||||
e = NULL;
|
||||
while (1)
|
||||
{
|
||||
e = G_Find (e, FOFS(targetname), self->target);
|
||||
if (!e)
|
||||
break;
|
||||
if (strcmp(e->classname, "light") != 0)
|
||||
{
|
||||
gi.dprintf("%s at %s ", self->classname, vtos(self->s.origin));
|
||||
gi.dprintf("target %s (%s at %s) is not a light\n", self->target, e->classname, vtos(e->s.origin));
|
||||
}
|
||||
else
|
||||
{
|
||||
self->enemy = e;
|
||||
}
|
||||
}
|
||||
|
||||
if (!self->enemy)
|
||||
{
|
||||
gi.dprintf("%s target %s not found at %s\n", self->classname, self->target, vtos(self->s.origin));
|
||||
G_FreeEdict (self);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
self->timestamp = level.time;
|
||||
target_lightramp_think (self);
|
||||
}
|
||||
|
||||
void SP_target_lightramp (edict_t *self)
|
||||
{
|
||||
if (!self->message || strlen(self->message) != 2 || self->message[0] < 'a' || self->message[0] > 'z' || self->message[1] < 'a' || self->message[1] > 'z' || self->message[0] == self->message[1])
|
||||
{
|
||||
gi.dprintf("target_lightramp has bad ramp (%s) at %s\n", self->message, vtos(self->s.origin));
|
||||
G_FreeEdict (self);
|
||||
return;
|
||||
}
|
||||
|
||||
if (deathmatch->value)
|
||||
{
|
||||
G_FreeEdict (self);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!self->target)
|
||||
{
|
||||
gi.dprintf("%s with no target at %s\n", self->classname, vtos(self->s.origin));
|
||||
G_FreeEdict (self);
|
||||
return;
|
||||
}
|
||||
|
||||
self->svflags |= SVF_NOCLIENT;
|
||||
self->use = target_lightramp_use;
|
||||
self->think = target_lightramp_think;
|
||||
|
||||
self->movedir[0] = self->message[0] - 'a';
|
||||
self->movedir[1] = self->message[1] - 'a';
|
||||
self->movedir[2] = (self->movedir[1] - self->movedir[0]) / (self->speed / FRAMETIME);
|
||||
}
|
||||
|
||||
//==========================================================
|
||||
|
||||
/*QUAKED target_earthquake (1 0 0) (-8 -8 -8) (8 8 8) SILENT
|
||||
When triggered, this initiates a level-wide earthquake.
|
||||
All players and monsters are affected.
|
||||
"speed" severity of the quake (default:200)
|
||||
"count" duration of the quake (default:5)
|
||||
*/
|
||||
|
||||
void target_earthquake_think (edict_t *self)
|
||||
{
|
||||
int i;
|
||||
edict_t *e;
|
||||
|
||||
if(!(self->spawnflags & 1)) // PGM
|
||||
{ // PGM
|
||||
if (self->last_move_time < level.time)
|
||||
{
|
||||
gi.positioned_sound (self->s.origin, self, CHAN_AUTO, self->noise_index, 1.0, ATTN_NONE, 0);
|
||||
self->last_move_time = level.time + 0.5;
|
||||
}
|
||||
} // PGM
|
||||
|
||||
for (i=1, e=g_edicts+i; i < globals.num_edicts; i++,e++)
|
||||
{
|
||||
if (!e->inuse)
|
||||
continue;
|
||||
if (!e->client)
|
||||
continue;
|
||||
if (!e->groundentity)
|
||||
continue;
|
||||
|
||||
e->groundentity = NULL;
|
||||
e->velocity[0] += crandom()* 150;
|
||||
e->velocity[1] += crandom()* 150;
|
||||
e->velocity[2] = self->speed * (100.0 / e->mass);
|
||||
}
|
||||
|
||||
if (level.time < self->timestamp)
|
||||
self->nextthink = level.time + FRAMETIME;
|
||||
}
|
||||
|
||||
void target_earthquake_use (edict_t *self, edict_t *other, edict_t *activator)
|
||||
{
|
||||
// PGM
|
||||
// if(g_showlogic && g_showlogic->value)
|
||||
// gi.dprintf("earthquake: %0.1f\n", self->speed);
|
||||
// PGM
|
||||
|
||||
self->timestamp = level.time + self->count;
|
||||
self->nextthink = level.time + FRAMETIME;
|
||||
self->activator = activator;
|
||||
self->last_move_time = 0;
|
||||
}
|
||||
|
||||
void SP_target_earthquake (edict_t *self)
|
||||
{
|
||||
if (!self->targetname)
|
||||
gi.dprintf("untargeted %s at %s\n", self->classname, vtos(self->s.origin));
|
||||
|
||||
if (!self->count)
|
||||
self->count = 5;
|
||||
|
||||
if (!self->speed)
|
||||
self->speed = 200;
|
||||
|
||||
self->svflags |= SVF_NOCLIENT;
|
||||
self->think = target_earthquake_think;
|
||||
self->use = target_earthquake_use;
|
||||
|
||||
if(!( |