game: sync g_* rogue logic
This commit is contained in:
parent
1ec141c954
commit
30fa1d6b83
24
Makefile
24
Makefile
|
@ -1469,11 +1469,11 @@ ROGUE_OBJS_ = \
|
|||
src/rogue/g_chase.o \
|
||||
src/game/g_cmds.o \
|
||||
src/rogue/g_combat.o \
|
||||
src/rogue/g_func.o \
|
||||
src/game/g_func.o \
|
||||
src/rogue/g_items.o \
|
||||
src/game/g_main.o \
|
||||
src/rogue/g_misc.o \
|
||||
src/rogue/g_monster.o \
|
||||
src/game/g_misc.o \
|
||||
src/game/g_monster.o \
|
||||
src/game/g_newai.o \
|
||||
src/game/g_newdm.o \
|
||||
src/game/g_newfnc.o \
|
||||
|
@ -1481,13 +1481,13 @@ ROGUE_OBJS_ = \
|
|||
src/game/g_newtrig.o \
|
||||
src/game/g_newweap.o \
|
||||
src/game/g_phys.o \
|
||||
src/rogue/g_spawn.o \
|
||||
src/game/g_spawn.o \
|
||||
src/game/g_sphere.o \
|
||||
src/rogue/g_svcmds.o \
|
||||
src/rogue/g_target.o \
|
||||
src/rogue/g_trigger.o \
|
||||
src/rogue/g_turret.o \
|
||||
src/rogue/g_utils.o \
|
||||
src/game/g_svcmds.o \
|
||||
src/game/g_target.o \
|
||||
src/game/g_trigger.o \
|
||||
src/game/g_turret.o \
|
||||
src/game/g_utils.o \
|
||||
src/game/g_weapon.o \
|
||||
src/game/dm/ball.o \
|
||||
src/game/dm/tag.o \
|
||||
|
@ -1496,17 +1496,21 @@ ROGUE_OBJS_ = \
|
|||
src/game/monster/boss3/boss3.o \
|
||||
src/game/monster/boss3/boss31.o \
|
||||
src/game/monster/boss3/boss32.o \
|
||||
src/game/monster/boss5/boss5.o \
|
||||
src/rogue/monster/brain/brain.o \
|
||||
src/game/monster/carrier/carrier.o \
|
||||
src/rogue/monster/chick/chick.o \
|
||||
src/game/monster/chick/chick.o \
|
||||
src/rogue/monster/flipper/flipper.o \
|
||||
src/rogue/monster/float/float.o \
|
||||
src/game/monster/flyer/flyer.o \
|
||||
src/rogue/monster/gladiator/gladiator.o \
|
||||
src/game/monster/gladiator/gladb.o \
|
||||
src/game/monster/gekk/gekk.o \
|
||||
src/rogue/monster/gunner/gunner.o \
|
||||
src/rogue/monster/hover/hover.o \
|
||||
src/game/monster/infantry/infantry.o \
|
||||
src/rogue/monster/insane/insane.o \
|
||||
src/game/monster/fixbot/fixbot.o \
|
||||
src/rogue/monster/medic/medic.o \
|
||||
src/rogue/monster/misc/move.o \
|
||||
src/rogue/monster/mutant/mutant.o \
|
||||
|
|
1209
src/game/g_func.c
1209
src/game/g_func.c
File diff suppressed because it is too large
Load Diff
|
@ -46,6 +46,13 @@ void Weapon_Grenade(edict_t *ent);
|
|||
void Weapon_GrenadeLauncher(edict_t *ent);
|
||||
void Weapon_Railgun(edict_t *ent);
|
||||
void Weapon_BFG(edict_t *ent);
|
||||
void Weapon_ChainFist(edict_t *ent);
|
||||
void Weapon_Disintegrator(edict_t *ent);
|
||||
void Weapon_ETF_Rifle(edict_t *ent);
|
||||
void Weapon_Heatbeam(edict_t *ent);
|
||||
void Weapon_Prox(edict_t *ent);
|
||||
void Weapon_Tesla(edict_t *ent);
|
||||
void Weapon_ProxLauncher(edict_t *ent);
|
||||
|
||||
void Weapon_Ionripper(edict_t *ent);
|
||||
void Weapon_Phalanx(edict_t *ent);
|
||||
|
@ -167,6 +174,21 @@ DoRespawn(edict_t *ent)
|
|||
}
|
||||
}
|
||||
|
||||
if (randomrespawn && randomrespawn->value)
|
||||
{
|
||||
edict_t *newEnt;
|
||||
|
||||
newEnt = DoRandomRespawn(ent);
|
||||
|
||||
/* if we've changed entities, then do some sleight
|
||||
* of hand. otherwise, the old entity will respawn */
|
||||
if (newEnt)
|
||||
{
|
||||
G_FreeEdict(ent);
|
||||
ent = newEnt;
|
||||
}
|
||||
}
|
||||
|
||||
ent->svflags &= ~SVF_NOCLIENT;
|
||||
ent->solid = SOLID_TRIGGER;
|
||||
gi.linkentity(ent);
|
||||
|
@ -324,6 +346,19 @@ Pickup_Bandolier(edict_t *ent, edict_t *other)
|
|||
other->client->pers.max_magslug = 75;
|
||||
}
|
||||
|
||||
if (other->client->pers.max_flechettes < 250)
|
||||
{
|
||||
other->client->pers.max_flechettes = 250;
|
||||
}
|
||||
|
||||
if (g_disruptor->value)
|
||||
{
|
||||
if (other->client->pers.max_rounds < 150)
|
||||
{
|
||||
other->client->pers.max_rounds = 150;
|
||||
}
|
||||
}
|
||||
|
||||
item = FindItem("Bullets");
|
||||
|
||||
if (item)
|
||||
|
@ -408,6 +443,19 @@ Pickup_Pack(edict_t *ent, edict_t *other)
|
|||
other->client->pers.max_magslug = 100;
|
||||
}
|
||||
|
||||
if (other->client->pers.max_flechettes < 200)
|
||||
{
|
||||
other->client->pers.max_flechettes = 200;
|
||||
}
|
||||
|
||||
if (g_disruptor->value)
|
||||
{
|
||||
if (other->client->pers.max_rounds < 200)
|
||||
{
|
||||
other->client->pers.max_rounds = 200;
|
||||
}
|
||||
}
|
||||
|
||||
item = FindItem("Bullets");
|
||||
|
||||
if (item)
|
||||
|
@ -513,6 +561,36 @@ Pickup_Pack(edict_t *ent, edict_t *other)
|
|||
}
|
||||
}
|
||||
|
||||
item = FindItem("Flechettes");
|
||||
|
||||
if (item)
|
||||
{
|
||||
index = ITEM_INDEX(item);
|
||||
other->client->pers.inventory[index] += item->quantity;
|
||||
|
||||
if (other->client->pers.inventory[index] >
|
||||
other->client->pers.max_flechettes)
|
||||
{
|
||||
other->client->pers.inventory[index] =
|
||||
other->client->pers.max_flechettes;
|
||||
}
|
||||
}
|
||||
|
||||
item = FindItem("Rounds");
|
||||
|
||||
if (item)
|
||||
{
|
||||
index = ITEM_INDEX(item);
|
||||
other->client->pers.inventory[index] += item->quantity;
|
||||
|
||||
if (other->client->pers.inventory[index] >
|
||||
other->client->pers.max_rounds)
|
||||
{
|
||||
other->client->pers.inventory[index] =
|
||||
other->client->pers.max_rounds;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(ent->spawnflags & DROPPED_ITEM) && (deathmatch->value))
|
||||
{
|
||||
SetRespawn(ent, ent->item->quantity);
|
||||
|
@ -523,6 +601,294 @@ Pickup_Pack(edict_t *ent, edict_t *other)
|
|||
|
||||
/* ====================================================================== */
|
||||
|
||||
qboolean
|
||||
Pickup_Nuke(edict_t *ent, edict_t *other)
|
||||
{
|
||||
int quantity;
|
||||
|
||||
if (!ent || !other)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
quantity = other->client->pers.inventory[ITEM_INDEX(ent->item)];
|
||||
|
||||
if (quantity >= 1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((coop->value) && (ent->item->flags & IT_STAY_COOP))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
other->client->pers.inventory[ITEM_INDEX(ent->item)]++;
|
||||
|
||||
if (deathmatch->value)
|
||||
{
|
||||
if (!(ent->spawnflags & DROPPED_ITEM))
|
||||
{
|
||||
SetRespawn(ent, ent->item->quantity);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
Use_IR(edict_t *ent, gitem_t *item)
|
||||
{
|
||||
if (!ent || !item)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ent->client->pers.inventory[ITEM_INDEX(item)]--;
|
||||
ValidateSelectedItem(ent);
|
||||
|
||||
if (ent->client->ir_framenum > level.framenum)
|
||||
{
|
||||
ent->client->ir_framenum += 600;
|
||||
}
|
||||
else
|
||||
{
|
||||
ent->client->ir_framenum = level.framenum + 600;
|
||||
}
|
||||
|
||||
gi.sound(ent, CHAN_ITEM, gi.soundindex("misc/ir_start.wav"), 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
void
|
||||
Use_Double(edict_t *ent, gitem_t *item)
|
||||
{
|
||||
ent->client->pers.inventory[ITEM_INDEX(item)]--;
|
||||
ValidateSelectedItem(ent);
|
||||
|
||||
if (ent->client->double_framenum > level.framenum)
|
||||
{
|
||||
ent->client->double_framenum += 300;
|
||||
}
|
||||
else
|
||||
{
|
||||
ent->client->double_framenum = level.framenum + 300;
|
||||
}
|
||||
|
||||
gi.sound(ent, CHAN_ITEM, gi.soundindex("misc/ddamage1.wav"), 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
void
|
||||
Use_Compass(edict_t *ent, gitem_t *item)
|
||||
{
|
||||
int ang;
|
||||
|
||||
if (!ent || !item)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ang = (int)(ent->client->v_angle[1]);
|
||||
|
||||
if (ang < 0)
|
||||
{
|
||||
ang += 360;
|
||||
}
|
||||
|
||||
gi.cprintf(ent, PRINT_HIGH, "Origin: %0.0f,%0.0f,%0.0f Dir: %d\n",
|
||||
ent->s.origin[0], ent->s.origin[1], ent->s.origin[2], ang);
|
||||
}
|
||||
|
||||
void
|
||||
Use_Nuke(edict_t *ent, gitem_t *item)
|
||||
{
|
||||
vec3_t forward, right, start;
|
||||
float speed;
|
||||
|
||||
if (!ent || !item)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ent->client->pers.inventory[ITEM_INDEX(item)]--;
|
||||
ValidateSelectedItem(ent);
|
||||
|
||||
AngleVectors(ent->client->v_angle, forward, right, NULL);
|
||||
|
||||
VectorCopy(ent->s.origin, start);
|
||||
speed = 100;
|
||||
fire_nuke(ent, start, forward, speed);
|
||||
}
|
||||
|
||||
void
|
||||
Use_Doppleganger(edict_t *ent, gitem_t *item)
|
||||
{
|
||||
vec3_t forward, right;
|
||||
vec3_t createPt, spawnPt;
|
||||
vec3_t ang;
|
||||
|
||||
if (!ent || !item)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
VectorClear(ang);
|
||||
ang[YAW] = ent->client->v_angle[YAW];
|
||||
AngleVectors(ang, forward, right, NULL);
|
||||
|
||||
VectorMA(ent->s.origin, 48, forward, createPt);
|
||||
|
||||
if (!FindSpawnPoint(createPt, ent->mins, ent->maxs, spawnPt, 32))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!CheckGroundSpawnPoint(spawnPt, ent->mins, ent->maxs, 64, -1))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ent->client->pers.inventory[ITEM_INDEX(item)]--;
|
||||
ValidateSelectedItem(ent);
|
||||
|
||||
SpawnGrow_Spawn(spawnPt, 0);
|
||||
fire_doppleganger(ent, spawnPt, forward);
|
||||
}
|
||||
|
||||
qboolean
|
||||
Pickup_Doppleganger(edict_t *ent, edict_t *other)
|
||||
{
|
||||
int quantity;
|
||||
|
||||
if (!ent || !other)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(deathmatch->value))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
quantity = other->client->pers.inventory[ITEM_INDEX(ent->item)];
|
||||
|
||||
if (quantity >= 1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
other->client->pers.inventory[ITEM_INDEX(ent->item)]++;
|
||||
|
||||
if (!(ent->spawnflags & DROPPED_ITEM))
|
||||
{
|
||||
SetRespawn(ent, ent->item->quantity);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
qboolean
|
||||
Pickup_Sphere(edict_t *ent, edict_t *other)
|
||||
{
|
||||
int quantity;
|
||||
|
||||
if (!ent || !other)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (other->client && other->client->owned_sphere)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
quantity = other->client->pers.inventory[ITEM_INDEX(ent->item)];
|
||||
|
||||
if (((skill->value == SKILL_MEDIUM) &&
|
||||
(quantity >= 2)) || ((skill->value >= SKILL_HARD) && (quantity >= 1)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((coop->value) && (ent->item->flags & IT_STAY_COOP) && (quantity > 0))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
other->client->pers.inventory[ITEM_INDEX(ent->item)]++;
|
||||
|
||||
if (deathmatch->value)
|
||||
{
|
||||
if (!(ent->spawnflags & DROPPED_ITEM))
|
||||
{
|
||||
SetRespawn(ent, ent->item->quantity);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
Use_Defender(edict_t *ent, gitem_t *item)
|
||||
{
|
||||
if (!ent || !item)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (ent->client && ent->client->owned_sphere)
|
||||
{
|
||||
gi.cprintf(ent, PRINT_HIGH, "Only one sphere at a time!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
ent->client->pers.inventory[ITEM_INDEX(item)]--;
|
||||
ValidateSelectedItem(ent);
|
||||
|
||||
Defender_Launch(ent);
|
||||
}
|
||||
|
||||
void
|
||||
Use_Hunter(edict_t *ent, gitem_t *item)
|
||||
{
|
||||
if (!ent || !item)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (ent->client && ent->client->owned_sphere)
|
||||
{
|
||||
gi.cprintf(ent, PRINT_HIGH, "Only one sphere at a time!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
ent->client->pers.inventory[ITEM_INDEX(item)]--;
|
||||
ValidateSelectedItem(ent);
|
||||
|
||||
Hunter_Launch(ent);
|
||||
}
|
||||
|
||||
void
|
||||
Use_Vengeance(edict_t *ent, gitem_t *item)
|
||||
{
|
||||
if (!ent || !item)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (ent->client && ent->client->owned_sphere)
|
||||
{
|
||||
gi.cprintf(ent, PRINT_HIGH, "Only one sphere at a time!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
ent->client->pers.inventory[ITEM_INDEX(item)]--;
|
||||
ValidateSelectedItem(ent);
|
||||
|
||||
Vengeance_Launch(ent);
|
||||
}
|
||||
|
||||
/* ====================================================================== */
|
||||
|
||||
void
|
||||
Use_Quad(edict_t *ent, gitem_t *item)
|
||||
{
|
||||
|
@ -555,7 +921,8 @@ Use_Quad(edict_t *ent, gitem_t *item)
|
|||
ent->client->quad_framenum = level.framenum + timeout;
|
||||
}
|
||||
|
||||
gi.sound(ent, CHAN_ITEM, gi.soundindex("items/damage.wav"), 1, ATTN_NORM, 0);
|
||||
gi.sound(ent, CHAN_ITEM, gi.soundindex("items/damage.wav"), 1, ATTN_NORM,
|
||||
0);
|
||||
}
|
||||
|
||||
/* ===================================================================== */
|
||||
|
@ -772,8 +1139,25 @@ Add_Ammo(edict_t *ent, gitem_t *item, int count)
|
|||
{
|
||||
max = ent->client->pers.max_trap;
|
||||
}
|
||||
else if (item->tag == AMMO_FLECHETTES)
|
||||
{
|
||||
max = ent->client->pers.max_flechettes;
|
||||
}
|
||||
else if (item->tag == AMMO_PROX)
|
||||
{
|
||||
max = ent->client->pers.max_prox;
|
||||
}
|
||||
else if (item->tag == AMMO_TESLA)
|
||||
{
|
||||
max = ent->client->pers.max_tesla;
|
||||
}
|
||||
else if (item->tag == AMMO_DISRUPTOR)
|
||||
{
|
||||
max = ent->client->pers.max_rounds;
|
||||
}
|
||||
else
|
||||
{
|
||||
gi.dprintf("undefined ammo type\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -830,9 +1214,10 @@ Pickup_Ammo(edict_t *ent, edict_t *other)
|
|||
|
||||
if (weapon && !oldcount)
|
||||
{
|
||||
/* don't switch to tesla */
|
||||
if ((other->client->pers.weapon != ent->item) &&
|
||||
(!deathmatch->value ||
|
||||
(other->client->pers.weapon == FindItem("blaster"))))
|
||||
(!deathmatch->value || (other->client->pers.weapon == FindItem("blaster"))) &&
|
||||
(strcmp(ent->classname, "ammo_tesla")))
|
||||
{
|
||||
other->client->newweapon = ent->item;
|
||||
}
|
||||
|
@ -1316,6 +1701,10 @@ Touch_Item(edict_t *ent, edict_t *other, cplane_t *plane /* unused */, csurface_
|
|||
{
|
||||
ent->item->use(other, ent->item);
|
||||
}
|
||||
else
|
||||
{
|
||||
gi.dprintf("Powerup has no use function!\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1499,7 +1888,7 @@ droptofloor(edict_t *ent)
|
|||
{
|
||||
gi.setmodel(ent, ent->model);
|
||||
}
|
||||
else
|
||||
else if (ent->item->world_model)
|
||||
{
|
||||
gi.setmodel(ent, ent->item->world_model);
|
||||
}
|
||||
|
@ -1668,6 +2057,47 @@ PrecacheItem(gitem_t *it)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Create the item marked for spawn creation
|
||||
*/
|
||||
void
|
||||
Item_TriggeredSpawn(edict_t *self, edict_t *other /* unused */, edict_t *activator /* unused */)
|
||||
{
|
||||
self->svflags &= ~SVF_NOCLIENT;
|
||||
self->use = NULL;
|
||||
|
||||
if (strcmp(self->classname, "key_power_cube"))
|
||||
{
|
||||
self->spawnflags = 0;
|
||||
}
|
||||
|
||||
droptofloor(self);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set up an item to spawn in later.
|
||||
*/
|
||||
void
|
||||
SetTriggeredSpawn(edict_t *ent)
|
||||
{
|
||||
if (!ent)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* don't do anything on key_power_cubes. */
|
||||
if (!strcmp(ent->classname, "key_power_cube"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ent->think = NULL;
|
||||
ent->nextthink = 0;
|
||||
ent->use = Item_TriggeredSpawn;
|
||||
ent->svflags |= SVF_NOCLIENT;
|
||||
ent->solid = SOLID_NOT;
|
||||
}
|
||||
|
||||
/*
|
||||
* ============
|
||||
* Sets the clipping size and
|
||||
|
@ -1718,6 +2148,18 @@ SpawnItem(edict_t *ent, gitem_t *item)
|
|||
G_FreeEdict(ent);
|
||||
return;
|
||||
}
|
||||
|
||||
if (item->pickup == Pickup_Sphere)
|
||||
{
|
||||
G_FreeEdict(ent);
|
||||
return;
|
||||
}
|
||||
|
||||
if (item->pickup == Pickup_Doppleganger)
|
||||
{
|
||||
G_FreeEdict(ent);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ((int)dmflags->value & DF_NO_HEALTH)
|
||||
|
@ -1740,6 +2182,34 @@ SpawnItem(edict_t *ent, gitem_t *item)
|
|||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ((int)dmflags->value & DF_NO_MINES)
|
||||
{
|
||||
if (!strcmp(ent->classname, "ammo_prox") ||
|
||||
!strcmp(ent->classname, "ammo_tesla"))
|
||||
{
|
||||
G_FreeEdict(ent);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ((int)dmflags->value & DF_NO_NUKES)
|
||||
{
|
||||
if (!strcmp(ent->classname, "ammo_nuke"))
|
||||
{
|
||||
G_FreeEdict(ent);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ((int)dmflags->value & DF_NO_SPHERES)
|
||||
{
|
||||
if (item->pickup == Pickup_Sphere)
|
||||
{
|
||||
G_FreeEdict(ent);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (coop->value && !(ent->spawnflags & ITEM_NO_TOUCH) && (strcmp(ent->classname, "key_power_cube") == 0))
|
||||
|
@ -1764,6 +2234,11 @@ SpawnItem(edict_t *ent, gitem_t *item)
|
|||
{
|
||||
gi.modelindex(ent->model);
|
||||
}
|
||||
|
||||
if (ent->spawnflags & 1)
|
||||
{
|
||||
SetTriggeredSpawn(ent);
|
||||
}
|
||||
}
|
||||
|
||||
/* ====================================================================== */
|
||||
|
@ -1775,7 +2250,7 @@ static const gitem_t gameitemlist[] = {
|
|||
|
||||
|
||||
/*
|
||||
* QUAKED item_armor_body (.3 .3 1) (-16 -16 -16) (16 16 16)
|
||||
* QUAKED item_armor_body (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN
|
||||
*/
|
||||
{
|
||||
"item_armor_body",
|
||||
|
@ -2931,7 +3406,7 @@ static const gitem_t gameitemlist[] = {
|
|||
gitem_t itemlist[MAX_ITEMS];
|
||||
|
||||
/*
|
||||
* QUAKED item_health (.3 .3 1) (-16 -16 -16) (16 16 16)
|
||||
* QUAKED item_health (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN
|
||||
*/
|
||||
void
|
||||
SP_item_health(edict_t *self)
|
||||
|
@ -3075,3 +3550,73 @@ SetItemNames(void)
|
|||
power_screen_index = ITEM_INDEX(FindItem("Power Screen"));
|
||||
power_shield_index = ITEM_INDEX(FindItem("Power Shield"));
|
||||
}
|
||||
|
||||
void
|
||||
SP_xatrix_item(edict_t *self)
|
||||
{
|
||||
gitem_t *item;
|
||||
int i;
|
||||
char *spawnClass = NULL;
|
||||
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!self->classname)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!strcmp(self->classname, "ammo_magslug"))
|
||||
{
|
||||
spawnClass = "ammo_flechettes";
|
||||
}
|
||||
else if (!strcmp(self->classname, "ammo_trap"))
|
||||
{
|
||||
spawnClass = "weapon_proxlauncher";
|
||||
}
|
||||
else if (!strcmp(self->classname, "item_quadfire"))
|
||||
{
|
||||
float chance;
|
||||
|
||||
chance = random();
|
||||
|
||||
if (chance < 0.2)
|
||||
{
|
||||
spawnClass = "item_sphere_hunter";
|
||||
}
|
||||
else if (chance < 0.6)
|
||||
{
|
||||
spawnClass = "item_sphere_vengeance";
|
||||
}
|
||||
else
|
||||
{
|
||||
spawnClass = "item_sphere_defender";
|
||||
}
|
||||
}
|
||||
else if (!strcmp(self->classname, "weapon_boomer"))
|
||||
{
|
||||
spawnClass = "weapon_etf_rifle";
|
||||
}
|
||||
else if (!strcmp(self->classname, "weapon_phalanx"))
|
||||
{
|
||||
spawnClass = "weapon_plasmabeam";
|
||||
}
|
||||
|
||||
/* check item spawn functions */
|
||||
for (i = 0, item = itemlist; i < game.num_items; i++, item++)
|
||||
{
|
||||
if (!item->classname)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!strcmp(item->classname, spawnClass))
|
||||
{
|
||||
/* found it */
|
||||
SpawnItem(self, item);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,6 +30,8 @@
|
|||
int debristhisframe;
|
||||
int gibsthisframe;
|
||||
|
||||
extern void M_WorldEffects(edict_t *ent);
|
||||
|
||||
/*
|
||||
* QUAKED func_group (0 0 0) ?
|
||||
* Used to group brushes together just for editor convenience.
|
||||
|
@ -254,6 +256,7 @@ ThrowGib(edict_t *self, char *gibname, int damage, int type)
|
|||
|
||||
gib->think = G_FreeEdict;
|
||||
gib->nextthink = level.time + 10 + random() * 10;
|
||||
gib->s.renderfx |= RF_IR_VISIBLE;
|
||||
|
||||
gi.linkentity(gib);
|
||||
}
|
||||
|
@ -591,6 +594,7 @@ BecomeExplosion2(edict_t *self)
|
|||
|
||||
/*
|
||||
* QUAKED path_corner (.5 .3 0) (-8 -8 -8) (8 8 8) TELEPORT
|
||||
*
|
||||
* Target: next path corner
|
||||
* Pathtarget: gets used when an entity that has
|
||||
* this path_corner targeted touches it
|
||||
|
@ -802,6 +806,7 @@ SP_point_combat(edict_t *self)
|
|||
|
||||
/*
|
||||
* QUAKED viewthing (0 .5 .8) (-8 -8 -8) (8 8 8)
|
||||
*
|
||||
* Just for the debugging level. Don't use
|
||||
*/
|
||||
void
|
||||
|
@ -842,6 +847,7 @@ SP_viewthing(edict_t *ent)
|
|||
|
||||
/*
|
||||
* QUAKED info_null (0 0.5 0) (-4 -4 -4) (4 4 4)
|
||||
*
|
||||
* Used as a positional target for spotlights, etc.
|
||||
*/
|
||||
void
|
||||
|
@ -857,6 +863,7 @@ SP_info_null(edict_t *self)
|
|||
|
||||
/*
|
||||
* QUAKED info_notnull (0 0.5 0) (-4 -4 -4) (4 4 4)
|
||||
*
|
||||
* Used as a positional target for lighting.
|
||||
*/
|
||||
void
|
||||
|
@ -875,6 +882,7 @@ SP_info_notnull(edict_t *self)
|
|||
|
||||
/*
|
||||
* QUAKED light (0 1 0) (-8 -8 -8) (8 8 8) START_OFF
|
||||
*
|
||||
* Non-displayed light.
|
||||
* Default light value is 300.
|
||||
* Default style is 0.
|
||||
|
@ -947,6 +955,7 @@ SP_light(edict_t *self)
|
|||
* START_ON only valid for TRIGGER_SPAWN walls
|
||||
* the wall will initially be present
|
||||
*/
|
||||
|
||||
void
|
||||
func_wall_use(edict_t *self, edict_t *other /* unused */, edict_t *activator /* unused */)
|
||||
{
|
||||
|
@ -1039,8 +1048,10 @@ SP_func_wall(edict_t *self)
|
|||
|
||||
/*
|
||||
* QUAKED func_object (0 .5 .8) ? TRIGGER_SPAWN ANIMATED ANIMATED_FAST
|
||||
*
|
||||
* This is solid bmodel that will fall if it's support it removed.
|
||||
*/
|
||||
|
||||
void
|
||||
func_object_touch(edict_t *self, edict_t *other, cplane_t *plane,
|
||||
csurface_t *surf /* unused */)
|
||||
|
@ -1052,7 +1063,8 @@ func_object_touch(edict_t *self, edict_t *other, cplane_t *plane,
|
|||
return;
|
||||
}
|
||||
|
||||
if (plane->normal[2] < 1.0)
|
||||
/* only squash thing we fall on top of */
|
||||
if (plane && plane->normal[2] < 1.0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
@ -1148,13 +1160,17 @@ SP_func_object(edict_t *self)
|
|||
/* ===================================================== */
|
||||
|
||||
/*
|
||||
* QUAKED func_explosive (0 .5 .8) ? Trigger_Spawn ANIMATED ANIMATED_FAST
|
||||
* Any brush that you want to explode or break apart. If you want an
|
||||
* QUAKED func_explosive (0 .5 .8) ? Trigger_Spawn ANIMATED ANIMATED_FAST INACTIVE
|
||||
*
|
||||
* Any brush that you want to explode or break apart. If you want an
|
||||
* explosion, set dmg and it will do a radius explosion of that amount
|
||||
* at the center of the bursh.
|
||||
*
|
||||
* If targeted it will not be shootable.
|
||||
*
|
||||
* INACTIVE - specifies that the entity is not explodable until triggered. If you use this you must
|
||||
* target the entity you want to trigger it. This is the only entity approved to activate it.
|
||||
*
|
||||
* health defaults to 100.
|
||||
*
|
||||
* mass defaults to 75. This determines how much debris is emitted when
|
||||
|
@ -1170,6 +1186,7 @@ func_explosive_explode(edict_t *self, edict_t *inflictor, edict_t *attacker,
|
|||
vec3_t size;
|
||||
int count;
|
||||
int mass;
|
||||
edict_t *master;
|
||||
|
||||
if (!self || !inflictor || !attacker)
|
||||
{
|
||||
|
@ -1238,6 +1255,23 @@ func_explosive_explode(edict_t *self, edict_t *inflictor, edict_t *attacker,
|
|||
ThrowDebris(self, "models/objects/debris2/tris.md2", 2, chunkorigin);
|
||||
}
|
||||
|
||||
if (self->flags & FL_TEAMSLAVE)
|
||||
{
|
||||
master = self->teammaster;
|
||||
|
||||
/* because mappers (other than jim (usually)) are stupid.... */
|
||||
while (master)
|
||||
{
|
||||
if (master->teamchain == self)
|
||||
{
|
||||
master->teamchain = self->teamchain;
|
||||
break;
|
||||
}
|
||||
|
||||
master = master->teamchain;
|
||||
}
|
||||
}
|
||||
|
||||
G_UseTargets(self, attacker);
|
||||
|
||||
if (self->dmg)
|
||||
|
@ -1251,14 +1285,43 @@ func_explosive_explode(edict_t *self, edict_t *inflictor, edict_t *attacker,
|
|||
}
|
||||
|
||||
void
|
||||
func_explosive_use(edict_t *self, edict_t *other, edict_t *activator)
|
||||
func_explosive_use(edict_t *self, edict_t *other, edict_t *activator /* unused */)
|
||||
{
|
||||
if (!self || !other)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
func_explosive_explode(self, self, other, self->health, vec3_origin);
|
||||
}
|
||||
|
||||
void
|
||||
func_explosive_activate(edict_t *self, edict_t *other /* unused */, edict_t *activator /* unused */)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
self->use = func_explosive_use;
|
||||
|
||||
if (!self->health)
|
||||
{
|
||||
self->health = 100;
|
||||
}
|
||||
|
||||
self->die = func_explosive_explode;
|
||||
self->takedamage = DAMAGE_YES;
|
||||
}
|
||||
|
||||
void
|
||||
func_explosive_spawn(edict_t *self, edict_t *other, edict_t *activator)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
self->solid = SOLID_BSP;
|
||||
self->svflags &= ~SVF_NOCLIENT;
|
||||
self->use = NULL;
|
||||
|
@ -1294,6 +1357,15 @@ SP_func_explosive(edict_t *self)
|
|||
self->solid = SOLID_NOT;
|
||||
self->use = func_explosive_spawn;
|
||||
}
|
||||
else if (self->spawnflags & 8)
|
||||
{
|
||||
self->solid = SOLID_BSP;
|
||||
|
||||
if (self->targetname)
|
||||
{
|
||||
self->use = func_explosive_activate;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
self->solid = SOLID_BSP;
|
||||
|
@ -1314,7 +1386,8 @@ SP_func_explosive(edict_t *self)
|
|||
self->s.effects |= EF_ANIM_ALLFAST;
|
||||
}
|
||||
|
||||
if (self->use != func_explosive_use)
|
||||
if ((self->use != func_explosive_use) &&
|
||||
(self->use != func_explosive_activate))
|
||||
{
|
||||
if (!self->health)
|
||||
{
|
||||
|
@ -1332,6 +1405,7 @@ SP_func_explosive(edict_t *self)
|
|||
|
||||
/*
|
||||
* QUAKED misc_explobox (0 .5 .8) (-16 -16 0) (16 16 40)
|
||||
*
|
||||
* Large exploding box. You can override its mass (100),
|
||||
* health (80), and dmg (150).
|
||||
*/
|
||||
|
@ -1401,7 +1475,7 @@ barrel_explode(edict_t *self)
|
|||
ThrowDebris(self, "models/objects/debris3/tris.md2", spd, org);
|
||||
|
||||
/* a bunch of little chunks */
|
||||
spd = 2 * self->dmg / 200;
|
||||
spd = 2.0 * (float)self->dmg / 200.0;
|
||||
org[0] = self->s.origin[0] + crandom() * self->size[0];
|
||||
org[1] = self->s.origin[1] + crandom() * self->size[1];
|
||||
org[2] = self->s.origin[2] + crandom() * self->size[2];
|
||||
|
@ -1462,6 +1536,37 @@ barrel_delay(edict_t *self, edict_t *inflictor /* unused */, edict_t *attacker,
|
|||
self->activator = attacker;
|
||||
}
|
||||
|
||||
void
|
||||
barrel_think(edict_t *self)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* the think needs to be first since later stuff may override. */
|
||||
self->think = barrel_think;
|
||||
self->nextthink = level.time + FRAMETIME;
|
||||
|
||||
M_CatagorizePosition(self);
|
||||
self->flags |= FL_IMMUNE_SLIME;
|
||||
self->air_finished = level.time + 100;
|
||||
M_WorldEffects(self);
|
||||
}
|
||||
|
||||
void
|
||||
barrel_start(edict_t *self)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
M_droptofloor(self);
|
||||
self->think = barrel_think;
|
||||
self->nextthink = level.time + FRAMETIME;
|
||||
}
|
||||
|
||||
void
|
||||
SP_misc_explobox(edict_t *self)
|
||||
{
|
||||
|
@ -1509,8 +1614,7 @@ SP_misc_explobox(edict_t *self)
|
|||
self->monsterinfo.aiflags = AI_NOSTEP;
|
||||
|
||||
self->touch = barrel_touch;
|
||||
|
||||
self->think = M_droptofloor;
|
||||
self->think = barrel_start;
|
||||
self->nextthink = level.time + 2 * FRAMETIME;
|
||||
|
||||
gi.linkentity(self);
|
||||
|
@ -1575,7 +1679,7 @@ SP_misc_blackhole(edict_t *ent)
|
|||
ent->s.renderfx = RF_TRANSLUCENT;
|
||||
ent->use = misc_blackhole_use;
|
||||
ent->think = misc_blackhole_think;
|
||||
ent->prethink = misc_blackhole_transparent;
|
||||
ent->prethink = misc_blackhole_transparent;
|
||||
ent->nextthink = level.time + 2 * FRAMETIME;
|
||||
gi.linkentity(ent);
|
||||
}
|
||||
|
@ -1711,6 +1815,7 @@ SP_misc_easterchick2(edict_t *ent)
|
|||
|
||||
/*
|
||||
* QUAKED monster_commander_body (1 .5 0) (-32 -32 0) (32 32 48)
|
||||
*
|
||||
* Not really a monster, this is the Tank Commander's decapitated body.
|
||||
* There should be a item_commander_head that has this as it's target.
|
||||
*/
|
||||
|
@ -1836,6 +1941,7 @@ SP_monster_commander_body(edict_t *self)
|
|||
|
||||
/*
|
||||
* QUAKED misc_banner (1 .5 0) (-4 -4 -4) (4 4 4)
|
||||
*
|
||||
* The origin is the bottom of the banner.
|
||||
* The banner is 128 tall.
|
||||
*/
|
||||
|
@ -1873,6 +1979,7 @@ SP_misc_banner(edict_t *ent)
|
|||
|
||||
/*
|
||||
* QUAKED misc_deadsoldier (1 .5 0) (-16 -16 0) (16 16 16) ON_BACK ON_STOMACH BACK_DECAP FETAL_POS SIT_DECAP IMPALED
|
||||
*
|
||||
* This is the dead player model. Comes in 6 exciting different poses!
|
||||
*/
|
||||
void
|
||||
|
@ -1891,7 +1998,8 @@ misc_deadsoldier_die(edict_t *self, edict_t *inflictor /* unused */, edict_t *at
|
|||
return;
|
||||
}
|
||||
|
||||
gi.sound(self, CHAN_BODY, gi.soundindex("misc/udeath.wav"), 1, ATTN_NORM, 0);
|
||||
gi.sound(self, CHAN_BODY, gi.soundindex(
|
||||
"misc/udeath.wav"), 1, ATTN_NORM, 0);
|
||||
|
||||
for (n = 0; n < 4; n++)
|
||||
{
|
||||
|
@ -1964,6 +2072,7 @@ SP_misc_deadsoldier(edict_t *ent)
|
|||
|
||||
/*
|
||||
* QUAKED misc_viper (1 .5 0) (-16 -16 0) (16 16 32)
|
||||
*
|
||||
* This is the Viper for the flyby bombing.
|
||||
* It is trigger_spawned, so you must have something use it for it to show up.
|
||||
* There must be a path for it to follow once it is activated.
|
||||
|
@ -2064,11 +2173,17 @@ SP_misc_crashviper(edict_t *ent)
|
|||
|
||||
/*
|
||||
* QUAKED misc_bigviper (1 .5 0) (-176 -120 -24) (176 120 72)
|
||||
*
|
||||
* This is a large stationary viper as seen in Paul's intro
|
||||
*/
|
||||
void
|
||||
SP_misc_bigviper(edict_t *ent)
|
||||
{
|
||||
if (!ent)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ent->movetype = MOVETYPE_NONE;
|
||||
ent->solid = SOLID_BBOX;
|
||||
VectorSet(ent->mins, -176, -120, -24);
|
||||
|
@ -2081,6 +2196,7 @@ SP_misc_bigviper(edict_t *ent)
|
|||
|
||||
/*
|
||||
* QUAKED misc_viper_bomb (1 0 0) (-8 -8 -8) (8 8 8)
|
||||
*
|
||||
* "dmg" how much boom should the bomb make?
|
||||
*/
|
||||
void
|
||||
|
@ -2240,6 +2356,7 @@ SP_misc_viper_missile(edict_t *self)
|
|||
|
||||
/*
|
||||
* QUAKED misc_strogg_ship (1 .5 0) (-16 -16 0) (16 16 32)
|
||||
*
|
||||
* This is a Storgg ship for the flybys.
|
||||
* It is trigger_spawned, so you must have something use it for it to show up.
|
||||
* There must be a path for it to follow once it is activated.
|
||||
|
@ -2412,7 +2529,8 @@ SP_light_mine1(edict_t *ent)
|
|||
|
||||
ent->movetype = MOVETYPE_NONE;
|
||||
ent->solid = SOLID_BBOX;
|
||||
ent->s.modelindex = gi.modelindex("models/objects/minelite/light1/tris.md2");
|
||||
ent->s.modelindex =
|
||||
gi.modelindex("models/objects/minelite/light1/tris.md2");
|
||||
gi.linkentity(ent);
|
||||
}
|
||||
|
||||
|
@ -2429,7 +2547,8 @@ SP_light_mine2(edict_t *ent)
|
|||
|
||||
ent->movetype = MOVETYPE_NONE;
|
||||
ent->solid = SOLID_BBOX;
|
||||
ent->s.modelindex = gi.modelindex("models/objects/minelite/light2/tris.md2");
|
||||
ent->s.modelindex =
|
||||
gi.modelindex("models/objects/minelite/light2/tris.md2");
|
||||
gi.linkentity(ent);
|
||||
}
|
||||
|
||||
|
@ -2437,6 +2556,7 @@ SP_light_mine2(edict_t *ent)
|
|||
|
||||
/*
|
||||
* QUAKED misc_gib_arm (1 0 0) (-8 -8 -8) (8 8 8)
|
||||
*
|
||||
* Intended for use with the target_spawner
|
||||
*/
|
||||
void
|
||||
|
@ -2523,9 +2643,11 @@ SP_misc_gib_head(edict_t *ent)
|
|||
|
||||
/*
|
||||
* QUAKED target_character (0 0 1) ?
|
||||
*
|
||||
* used with target_string (must be on same "team")
|
||||
* "count" is position in the string (starts at 1)
|
||||
*/
|
||||
|
||||
void
|
||||
SP_target_character(edict_t *self)
|
||||
{
|
||||
|
@ -2600,6 +2722,11 @@ target_string_use(edict_t *self, edict_t *other /* unused */, edict_t *activator
|
|||
void
|
||||
SP_target_string(edict_t *self)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!self->message)
|
||||
{
|
||||
self->message = "";
|
||||
|
@ -2612,6 +2739,7 @@ SP_target_string(edict_t *self)
|
|||
|
||||
/*
|
||||
* QUAKED func_clock (0 0 1) (-8 -8 -8) (8 8 8) TIMER_UP TIMER_DOWN START_OFF MULTI_USE
|
||||
*
|
||||
* target a target_string with this
|
||||
*
|
||||
* The default is to be a time of day clock
|
||||
|
@ -2936,6 +3064,7 @@ teleporter_touch(edict_t *self, edict_t *other, cplane_t *plane /* unused */,
|
|||
|
||||
/*
|
||||
* QUAKED misc_teleporter (1 0 0) (-32 -32 -24) (32 32 -16)
|
||||
*
|
||||
* Stepping onto this disc will teleport players to the targeted misc_teleporter_dest object.
|
||||
*/
|
||||
void
|
||||
|
@ -2978,6 +3107,7 @@ SP_misc_teleporter(edict_t *ent)
|
|||
|
||||
/*
|
||||
* QUAKED misc_teleporter_dest (1 0 0) (-32 -32 -24) (32 32 -16)
|
||||
*
|
||||
* Point teleporters at these.
|
||||
*/
|
||||
void
|
||||
|
@ -3072,3 +3202,40 @@ SP_misc_nuke(edict_t *ent)
|
|||
|
||||
ent->use = use_nuke;
|
||||
}
|
||||
|
||||
void
|
||||
misc_nuke_core_use(edict_t *self, edict_t *other /* unused */, edict_t *activator /* unused */)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->svflags & SVF_NOCLIENT)
|
||||
{
|
||||
self->svflags &= ~SVF_NOCLIENT;
|
||||
}
|
||||
else
|
||||
{
|
||||
self->svflags |= SVF_NOCLIENT;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* QUAKED misc_nuke_core (1 0 0) (-16 -16 -16) (16 16 16)
|
||||
*
|
||||
* toggles visible/not visible. starts visible.
|
||||
*/
|
||||
void
|
||||
SP_misc_nuke_core(edict_t *ent)
|
||||
{
|
||||
if (!ent)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
gi.setmodel(ent, "models/objects/core/tris.md2");
|
||||
gi.linkentity(ent);
|
||||
|
||||
ent->use = misc_nuke_core_use;
|
||||
}
|
||||
|
|
|
@ -475,7 +475,7 @@ M_CheckGround(edict_t *ent)
|
|||
return;
|
||||
}
|
||||
|
||||
if (ent->velocity[2] > 100)
|
||||
if ((ent->velocity[2] * ent->gravityVector[2]) < -100)
|
||||
{
|
||||
ent->groundentity = NULL;
|
||||
return;
|
||||
|
@ -485,7 +485,7 @@ M_CheckGround(edict_t *ent)
|
|||
is solid the entity is on ground */
|
||||
point[0] = ent->s.origin[0];
|
||||
point[1] = ent->s.origin[1];
|
||||
point[2] = ent->s.origin[2] - 0.25;
|
||||
point[2] = ent->s.origin[2] + (0.25 * ent->gravityVector[2]);
|
||||
|
||||
trace = gi.trace(ent->s.origin, ent->mins, ent->maxs, point,
|
||||
ent, MASK_MONSTERSOLID);
|
||||
|
@ -689,9 +689,18 @@ M_droptofloor(edict_t *ent)
|
|||
return;
|
||||
}
|
||||
|
||||
ent->s.origin[2] += 1;
|
||||
VectorCopy(ent->s.origin, end);
|
||||
end[2] -= 256;
|
||||
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;
|
||||
}
|
||||
|
||||
trace = gi.trace(ent->s.origin, ent->mins, ent->maxs, end,
|
||||
ent, MASK_MONSTERSOLID);
|
||||
|
@ -1140,6 +1149,11 @@ monster_start(edict_t *self)
|
|||
self->monsterinfo.currentmove->firstframe + 1));
|
||||
}
|
||||
|
||||
self->monsterinfo.base_height = self->maxs[2];
|
||||
self->monsterinfo.quad_framenum = 0;
|
||||
self->monsterinfo.double_framenum = 0;
|
||||
self->monsterinfo.invincible_framenum = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -346,6 +346,43 @@ static spawn_t spawns[] = {
|
|||
{"turret_base", SP_turret_base},
|
||||
{"turret_driver", SP_turret_driver},
|
||||
|
||||
{"func_plat2", SP_func_plat2},
|
||||
{"func_door_secret2", SP_func_door_secret2},
|
||||
{"func_force_wall", SP_func_force_wall},
|
||||
{"trigger_teleport", SP_trigger_teleport},
|
||||
{"trigger_disguise", SP_trigger_disguise},
|
||||
{"info_teleport_destination", SP_info_teleport_destination},
|
||||
{"info_player_coop_lava", SP_info_player_coop_lava},
|
||||
{"monster_stalker", SP_monster_stalker},
|
||||
{"monster_turret", SP_monster_turret},
|
||||
{"target_steam", SP_target_steam},
|
||||
{"target_anger", SP_target_anger},
|
||||
{"target_killplayers", SP_target_killplayers},
|
||||
{"target_blacklight", SP_target_blacklight},
|
||||
{"target_orb", SP_target_orb},
|
||||
{"monster_daedalus", SP_monster_hover},
|
||||
{"hint_path", SP_hint_path},
|
||||
{"monster_carrier", SP_monster_carrier},
|
||||
{"monster_widow", SP_monster_widow},
|
||||
{"monster_widow2", SP_monster_widow2},
|
||||
{"monster_medic_commander", SP_monster_medic},
|
||||
{"dm_tag_token", SP_dm_tag_token},
|
||||
{"dm_dball_goal", SP_dm_dball_goal},
|
||||
{"dm_dball_ball", SP_dm_dball_ball},
|
||||
{"dm_dball_team1_start", SP_dm_dball_team1_start},
|
||||
{"dm_dball_team2_start", SP_dm_dball_team2_start},
|
||||
{"dm_dball_ball_start", SP_dm_dball_ball_start},
|
||||
{"dm_dball_speed_change", SP_dm_dball_speed_change},
|
||||
{"monster_kamikaze", SP_monster_kamikaze},
|
||||
{"turret_invisible_brain", SP_turret_invisible_brain},
|
||||
{"misc_nuke_core", SP_misc_nuke_core},
|
||||
|
||||
{"ammo_magslug", SP_xatrix_item},
|
||||
{"ammo_trap", SP_xatrix_item},
|
||||
{"item_quadfire", SP_xatrix_item},
|
||||
{"weapon_boomer", SP_xatrix_item},
|
||||
{"weapon_phalanx", SP_xatrix_item},
|
||||
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
|
@ -401,6 +438,25 @@ ED_CallSpawn(edict_t *ent)
|
|||
return;
|
||||
}
|
||||
|
||||
ent->gravityVector[0] = 0.0;
|
||||
ent->gravityVector[1] = 0.0;
|
||||
ent->gravityVector[2] = -1.0;
|
||||
|
||||
if (!strcmp(ent->classname, "weapon_nailgun"))
|
||||
{
|
||||
ent->classname = (FindItem("ETF Rifle"))->classname;
|
||||
}
|
||||
|
||||
if (!strcmp(ent->classname, "ammo_nails"))
|
||||
{
|
||||
ent->classname = (FindItem("Flechettes"))->classname;
|
||||
}
|
||||
|
||||
if (!strcmp(ent->classname, "weapon_heatbeam"))
|
||||
{
|
||||
ent->classname = (FindItem("Plasma Beam"))->classname;
|
||||
}
|
||||
|
||||
/* check item spawn functions */
|
||||
for (i = 0, item = itemlist; i < game.num_items; i++, item++)
|
||||
{
|
||||
|
@ -878,6 +934,30 @@ SpawnEntities(const char *mapname, char *entities, const char *spawnpoint)
|
|||
continue;
|
||||
}
|
||||
}
|
||||
else if (coop->value && !coop_baseq2->value)
|
||||
{
|
||||
if (ent->spawnflags & SPAWNFLAG_NOT_COOP)
|
||||
{
|
||||
G_FreeEdict(ent);
|
||||
inhibit++;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* stuff marked !easy & !med & !hard are coop only, all levels */
|
||||
if (!((ent->spawnflags & SPAWNFLAG_NOT_EASY) &&
|
||||
(ent->spawnflags & SPAWNFLAG_NOT_MEDIUM) &&
|
||||
(ent->spawnflags & SPAWNFLAG_NOT_HARD)))
|
||||
{
|
||||
if (((skill->value == SKILL_EASY) && (ent->spawnflags & SPAWNFLAG_NOT_EASY)) ||
|
||||
((skill->value == SKILL_MEDIUM) && (ent->spawnflags & SPAWNFLAG_NOT_MEDIUM)) ||
|
||||
(((skill->value == SKILL_HARD) || (skill->value == SKILL_HARDPLUS)) && (ent->spawnflags & SPAWNFLAG_NOT_HARD)))
|
||||
{
|
||||
G_FreeEdict(ent);
|
||||
inhibit++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Spawn_CheckCoop_MapHacks(ent) || (
|
||||
|
@ -902,7 +982,13 @@ SpawnEntities(const char *mapname, char *entities, const char *spawnpoint)
|
|||
SPAWNFLAG_NOT_COOP | SPAWNFLAG_NOT_DEATHMATCH);
|
||||
}
|
||||
|
||||
ent->gravityVector[0] = 0.0;
|
||||
ent->gravityVector[1] = 0.0;
|
||||
ent->gravityVector[2] = -1.0;
|
||||
|
||||
ED_CallSpawn(ent);
|
||||
|
||||
ent->s.renderfx |= RF_IR_VISIBLE;
|
||||
}
|
||||
|
||||
gi.dprintf("%i entities inhibited.\n", inhibit);
|
||||
|
@ -910,6 +996,26 @@ SpawnEntities(const char *mapname, char *entities, const char *spawnpoint)
|
|||
G_FindTeams();
|
||||
|
||||
PlayerTrail_Init();
|
||||
|
||||
if (deathmatch->value)
|
||||
{
|
||||
if (randomrespawn && randomrespawn->value)
|
||||
{
|
||||
PrecacheForRandomRespawn();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
InitHintPaths();
|
||||
}
|
||||
|
||||
if (deathmatch->value && gamerules && gamerules->value)
|
||||
{
|
||||
if (DMGame.PostInitSetup)
|
||||
{
|
||||
DMGame.PostInitSetup();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* =================================================================== */
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
#define VENGEANCE_LIFESPAN 30
|
||||
#define MINIMUM_FLY_TIME 15
|
||||
|
||||
extern char *ED_NewString(char *string);
|
||||
extern char *ED_NewString(const char *string);
|
||||
void LookAtKiller(edict_t *self, edict_t *inflictor, edict_t *attacker);
|
||||
|
||||
void defender_think(edict_t *self);
|
||||
|
|
|
@ -27,11 +27,22 @@
|
|||
|
||||
#include "header/local.h"
|
||||
|
||||
#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
|
||||
|
||||
void ED_CallSpawn(edict_t *ent);
|
||||
|
||||
/*
|
||||
* 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
|
||||
* Fire an origin based temp entity event to the clients.
|
||||
* "style" type byte
|
||||
*/
|
||||
void
|
||||
Use_Target_Tent(edict_t *ent, edict_t *other /* unused */, edict_t *activator /* unused */)
|
||||
|
@ -199,6 +210,7 @@ Use_Target_Help(edict_t *ent, edict_t *other /* unused */, edict_t *activator /*
|
|||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
@ -411,6 +423,7 @@ SP_target_explosion(edict_t *ent)
|
|||
|
||||
/*
|
||||
* QUAKED target_changelevel (1 0 0) (-8 -8 -8) (8 8 8)
|
||||
*
|
||||
* Changes level to "map" when fired
|
||||
*/
|
||||
void
|
||||
|
@ -588,6 +601,8 @@ use_target_spawner(edict_t *self, edict_t *other /* unused */, edict_t *activato
|
|||
{
|
||||
VectorCopy(self->movedir, ent->velocity);
|
||||
}
|
||||
|
||||
ent->s.renderfx |= RF_IR_VISIBLE; /* PGM */
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -672,10 +687,11 @@ SP_target_blaster(edict_t *self)
|
|||
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* 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 /* unused */,
|
||||
|
@ -705,6 +721,7 @@ SP_target_crosslevel_trigger(edict_t *self)
|
|||
|
||||
/*
|
||||
* 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.
|
||||
|
@ -750,9 +767,11 @@ SP_target_crosslevel_target(edict_t *self)
|
|||
/* ========================================================== */
|
||||
|
||||
/*
|
||||
* QUAKED target_laser (0 .5 .8) (-8 -8 -8) (8 8 8) START_ON RED GREEN BLUE YELLOW ORANGE FAT
|
||||
* 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
|
||||
*/
|
||||
void
|
||||
target_laser_think(edict_t *self)
|
||||
|
@ -798,8 +817,15 @@ target_laser_think(edict_t *self)
|
|||
|
||||
while (1)
|
||||
{
|
||||
tr = gi.trace(start, NULL, NULL, end, ignore,
|
||||
CONTENTS_SOLID | CONTENTS_MONSTER | CONTENTS_DEADMONSTER);
|
||||
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);
|
||||
}
|
||||
|
||||
if (!tr.ent)
|
||||
{
|
||||
|
@ -823,7 +849,8 @@ target_laser_think(edict_t *self)
|
|||
|
||||
/* 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))
|
||||
if (!(tr.ent->svflags & SVF_MONSTER) && (!tr.ent->client) &&
|
||||
!(tr.ent->svflags & SVF_DAMAGEABLE))
|
||||
{
|
||||
if (self->spawnflags & 0x80000000)
|
||||
{
|
||||
|
@ -1156,9 +1183,11 @@ SP_target_mal_laser(edict_t *self)
|
|||
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* speed How many seconds the ramping will take
|
||||
* message two letters; starting lightlevel and ending lightlevel
|
||||
*/
|
||||
|
||||
void
|
||||
target_lightramp_think(edict_t *self)
|
||||
{
|
||||
|
@ -1285,11 +1314,12 @@ SP_target_lightramp(edict_t *self)
|
|||
/* ========================================================== */
|
||||
|
||||
/*
|
||||
* QUAKED target_earthquake (1 0 0) (-8 -8 -8) (8 8 8)
|
||||
* 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)
|
||||
* "speed" severity of the quake (default:200)
|
||||
* "count" duration of the quake (default:5)
|
||||
*/
|
||||
void
|
||||
target_earthquake_think(edict_t *self)
|
||||
|
@ -1302,16 +1332,19 @@ target_earthquake_think(edict_t *self)
|
|||
return;
|
||||
}
|
||||
|
||||
if (self->last_move_time < level.time)
|
||||
if (!(self->spawnflags & 1))
|
||||
{
|
||||
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;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 1, e = g_edicts + i; i < globals.num_edicts; i++, e++)
|
||||
|
@ -1385,5 +1418,8 @@ SP_target_earthquake(edict_t *self)
|
|||
self->think = target_earthquake_think;
|
||||
self->use = target_earthquake_use;
|
||||
|
||||
self->noise_index = gi.soundindex("world/quake.wav");
|
||||
if (!(self->spawnflags & 1))
|
||||
{
|
||||
self->noise_index = gi.soundindex("world/quake.wav");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,12 +27,21 @@
|
|||
|
||||
#include "header/local.h"
|
||||
|
||||
#define PUSH_ONCE 1
|
||||
#define TRIGGER_MONSTER 0x01
|
||||
#define TRIGGER_NOT_PLAYER 0x02
|
||||
#define TRIGGER_TRIGGERED 0x04
|
||||
#define TRIGGER_TOGGLE 0x08
|
||||
|
||||
void trigger_push_active(edict_t *self);
|
||||
#define PUSH_ONCE 0x01
|
||||
#define PUSH_START_OFF 0x02
|
||||
#define PUSH_SILENT 0x04
|
||||
|
||||
static int windsound;
|
||||
|
||||
void trigger_push_active(edict_t *self);
|
||||
void hurt_touch(edict_t *self, edict_t *other, cplane_t *plane /* unused */,
|
||||
csurface_t *surf /* unused */);
|
||||
|
||||
void
|
||||
InitTrigger(edict_t *self)
|
||||
{
|
||||
|
@ -114,8 +123,24 @@ Use_Multi(edict_t *ent, edict_t *other /* unused */, edict_t *activator)
|
|||
return;
|
||||
}
|
||||
|
||||
ent->activator = activator;
|
||||
multi_trigger(ent);
|
||||
if (ent->spawnflags & TRIGGER_TOGGLE)
|
||||
{
|
||||
if (ent->solid == SOLID_TRIGGER)
|
||||
{
|
||||
ent->solid = SOLID_NOT;
|
||||
}
|
||||
else
|
||||
{
|
||||
ent->solid = SOLID_TRIGGER;
|
||||
}
|
||||
|
||||
gi.linkentity(ent);
|
||||
}
|
||||
else
|
||||
{
|
||||
ent->activator = activator;
|
||||
multi_trigger(ent);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -163,13 +188,15 @@ Touch_Multi(edict_t *self, edict_t *other, cplane_t *plane /* unused */,
|
|||
}
|
||||
|
||||
/*
|
||||
* QUAKED trigger_multiple (.5 .5 .5) ? MONSTER NOT_PLAYER TRIGGERED
|
||||
* QUAKED trigger_multiple (.5 .5 .5) ? MONSTER NOT_PLAYER TRIGGERED TOGGLE
|
||||
* Variable sized repeatable trigger. Must be targeted at one or more
|
||||
* entities. If "delay" is set, the trigger waits some time after
|
||||
* activating before firing.
|
||||
*
|
||||
* "wait" : Seconds between triggerings. (.2 default)
|
||||
*
|
||||
* TOGGLE - using this trigger will activate/deactivate it. trigger will begin inactive.
|
||||
*
|
||||
* sounds
|
||||
* 1) secret
|
||||
* 2) beep beep
|
||||
|
@ -222,7 +249,7 @@ SP_trigger_multiple(edict_t *ent)
|
|||
ent->movetype = MOVETYPE_NONE;
|
||||
ent->svflags |= SVF_NOCLIENT;
|
||||
|
||||
if (ent->spawnflags & 4)
|
||||
if (ent->spawnflags & (TRIGGER_TRIGGERED | TRIGGER_TOGGLE))
|
||||
{
|
||||
ent->solid = SOLID_NOT;
|
||||
ent->use = trigger_enable;
|
||||
|
@ -244,6 +271,7 @@ SP_trigger_multiple(edict_t *ent)
|
|||
|
||||
/*
|
||||
* QUAKED trigger_once (.5 .5 .5) ? x x TRIGGERED
|
||||
*
|
||||
* Triggers once, then removes itself.
|
||||
*
|
||||
* You must set the key "target" to the name of another
|
||||
|
@ -463,6 +491,7 @@ SP_trigger_key(edict_t *self)
|
|||
|
||||
/*
|
||||
* QUAKED trigger_counter (.5 .5 .5) ? nomessage
|
||||
*
|
||||
* Acts as an intermediary for an action that takes multiple inputs.
|
||||
*
|
||||
* If nomessage is not set, it will print "1 more.. " etc when
|
||||
|
@ -471,6 +500,7 @@ SP_trigger_key(edict_t *self)
|
|||
* After the counter has been triggered "count" times (default 2),
|
||||
* it will fire all of it's targets and remove itself.
|
||||
*/
|
||||
|
||||
void
|
||||
trigger_counter_use(edict_t *self, edict_t *other /* unused */,
|
||||
edict_t *activator)
|
||||
|
@ -530,6 +560,7 @@ SP_trigger_counter(edict_t *self)
|
|||
|
||||
/*
|
||||
* QUAKED trigger_always (.5 .5 .5) (-8 -8 -8) (8 8 8)
|
||||
*
|
||||
* This trigger will always fire. It is activated by the world.
|
||||
*/
|
||||
void
|
||||
|
@ -573,7 +604,8 @@ trigger_push_touch(edict_t *self, edict_t *other, cplane_t *plane /* unused */,
|
|||
immediately from this */
|
||||
VectorCopy(other->velocity, other->client->oldvelocity);
|
||||
|
||||
if (other->fly_sound_debounce_time < level.time)
|
||||
if (!(self->spawnflags & PUSH_SILENT) &&
|
||||
(other->fly_sound_debounce_time < level.time))
|
||||
{
|
||||
other->fly_sound_debounce_time = level.time + 1.5;
|
||||
gi.sound(other, CHAN_AUTO, windsound, 1, ATTN_NORM, 0);
|
||||
|
@ -664,6 +696,36 @@ trigger_push_active(edict_t *self)
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
trigger_push_use(edict_t *self, edict_t *other /* unused */, edict_t *activator /* unused */)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->solid == SOLID_NOT)
|
||||
{
|
||||
self->solid = SOLID_TRIGGER;
|
||||
}
|
||||
else
|
||||
{
|
||||
self->solid = SOLID_NOT;
|
||||
}
|
||||
|
||||
gi.linkentity(self);
|
||||
}
|
||||
|
||||
/*
|
||||
* QUAKED trigger_push (.5 .5 .5) ? PUSH_ONCE START_OFF SILENT
|
||||
* Pushes the player
|
||||
* "speed" defaults to 1000
|
||||
*
|
||||
* If targeted, it will toggle on and off when used.
|
||||
*
|
||||
* START_OFF - toggled trigger_push begins in off setting
|
||||
* SILENT - doesn't make wind noise
|
||||
*/
|
||||
void
|
||||
SP_trigger_push(edict_t *self)
|
||||
{
|
||||
|
@ -676,7 +738,12 @@ SP_trigger_push(edict_t *self)
|
|||
windsound = gi.soundindex("misc/windfly.wav");
|
||||
self->touch = trigger_push_touch;
|
||||
|
||||
if (self->spawnflags & 2)
|
||||
if (!self->speed)
|
||||
{
|
||||
self->speed = 1000;
|
||||
}
|
||||
|
||||
if (self->spawnflags & PUSH_START_OFF)
|
||||
{
|
||||
if (!self->wait)
|
||||
{
|
||||
|
@ -688,9 +755,22 @@ SP_trigger_push(edict_t *self)
|
|||
self->delay = self->nextthink + self->wait;
|
||||
}
|
||||
|
||||
if (!self->speed)
|
||||
if (self->targetname) /* toggleable */
|
||||
{
|
||||
self->speed = 1000;
|
||||
self->use = trigger_push_use;
|
||||
|
||||
if (self->spawnflags & PUSH_START_OFF)
|
||||
{
|
||||
self->solid = SOLID_NOT;
|
||||
}
|
||||
}
|
||||
else if (self->spawnflags & PUSH_START_OFF)
|
||||
{
|
||||
gi.dprintf("trigger_push is START_OFF but not targeted.\n");
|
||||
self->svflags = 0;
|
||||
self->touch = NULL;
|
||||
self->solid = SOLID_BSP;
|
||||
self->movetype = MOVETYPE_PUSH;
|
||||
}
|
||||
|
||||
gi.linkentity(self);
|
||||
|
@ -698,16 +778,56 @@ SP_trigger_push(edict_t *self)
|
|||
|
||||
/*
|
||||
* QUAKED trigger_hurt (.5 .5 .5) ? START_OFF TOGGLE SILENT NO_PROTECTION SLOW
|
||||
*
|
||||
* Any entity that touches this will be hurt.
|
||||
*
|
||||
* It does dmg points of damage each server frame
|
||||
*
|
||||
* SILENT supresses playing the sound
|
||||
* SLOW changes the damage rate to once per second
|
||||
* SLOW changes the damage rate to once per second
|
||||
* NO_PROTECTION *nothing* stops the damage
|
||||
*
|
||||
* "dmg" default 5 (whole numbers only)
|
||||
*
|
||||
*/
|
||||
void
|
||||
hurt_use(edict_t *self, edict_t *other /* unused */,
|
||||
edict_t *activator /* unused */)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->solid == SOLID_NOT)
|
||||
{
|
||||
int i, num;
|
||||
edict_t *touch[MAX_EDICTS], *hurtme;
|
||||
|
||||
self->solid = SOLID_TRIGGER;
|
||||
num = gi.BoxEdicts (self->absmin, self->absmax,
|
||||
touch, MAX_EDICTS, AREA_SOLID);
|
||||
|
||||
/* Check for idle monsters in
|
||||
trigger hurt */
|
||||
for (i = 0 ; i < num ; i++)
|
||||
{
|
||||
hurtme = touch[i];
|
||||
hurt_touch (self, hurtme, NULL, NULL);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
self->solid = SOLID_NOT;
|
||||
}
|
||||
|
||||
gi.linkentity(self);
|
||||
|
||||
if (!(self->spawnflags & 2))
|
||||
{
|
||||
self->use = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
hurt_touch(edict_t *self, edict_t *other, cplane_t *plane /* unused */,
|
||||
|
@ -760,45 +880,6 @@ hurt_touch(edict_t *self, edict_t *other, cplane_t *plane /* unused */,
|
|||
self->dmg, self->dmg, dflags, MOD_TRIGGER_HURT);
|
||||
}
|
||||
|
||||
void
|
||||
hurt_use(edict_t *self, edict_t *other /* unused */,
|
||||
edict_t *activator /* unused */)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->solid == SOLID_NOT)
|
||||
{
|
||||
int i, num;
|
||||
edict_t *touch[MAX_EDICTS], *hurtme;
|
||||
|
||||
self->solid = SOLID_TRIGGER;
|
||||
num = gi.BoxEdicts (self->absmin, self->absmax,
|
||||
touch, MAX_EDICTS, AREA_SOLID);
|
||||
|
||||
/* Check for idle monsters in
|
||||
trigger hurt */
|
||||
for (i = 0 ; i < num ; i++)
|
||||
{
|
||||
hurtme = touch[i];
|
||||
hurt_touch (self, hurtme, NULL, NULL);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
self->solid = SOLID_NOT;
|
||||
}
|
||||
|
||||
gi.linkentity(self);
|
||||
|
||||
if (!(self->spawnflags & 2))
|
||||
{
|
||||
self->use = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SP_trigger_hurt(edict_t *self)
|
||||
{
|
||||
|
@ -834,6 +915,26 @@ SP_trigger_hurt(edict_t *self)
|
|||
gi.linkentity(self);
|
||||
}
|
||||
|
||||
void
|
||||
trigger_gravity_use(edict_t *self, edict_t *other /* unused */, edict_t *activator /* unused */)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->solid == SOLID_NOT)
|
||||
{
|
||||
self->solid = SOLID_TRIGGER;
|
||||
}
|
||||
else
|
||||
{
|
||||
self->solid = SOLID_NOT;
|
||||
}
|
||||
|
||||
gi.linkentity(self);
|
||||
}
|
||||
|
||||
/*
|
||||
* QUAKED trigger_gravity (.5 .5 .5) ?
|
||||
* Changes the touching entites gravity to
|
||||
|
@ -852,6 +953,15 @@ trigger_gravity_touch(edict_t *self, edict_t *other, cplane_t *plane /* unused *
|
|||
other->gravity = self->gravity;
|
||||
}
|
||||
|
||||
/*
|
||||
* QUAKED trigger_gravity (.5 .5 .5) ? TOGGLE START_OFF
|
||||
* Changes the touching entites gravity to
|
||||
* the value of "gravity". 1.0 is standard
|
||||
* gravity for the level.
|
||||
*
|
||||
* TOGGLE - trigger_gravity can be turned on and off
|
||||
* START_OFF - trigger_gravity starts turned off (implies TOGGLE)
|
||||
*/
|
||||
void
|
||||
SP_trigger_gravity(edict_t *self)
|
||||
{
|
||||
|
@ -870,7 +980,20 @@ SP_trigger_gravity(edict_t *self)
|
|||
|
||||
InitTrigger(self);
|
||||
self->gravity = (int)strtol(st.gravity, (char **)NULL, 10);
|
||||
|
||||
if (self->spawnflags & 1) /* TOGGLE */
|
||||
{
|
||||
self->use = trigger_gravity_use;
|
||||
}
|
||||
|
||||
if (self->spawnflags & 2) /* START_OFF */
|
||||
{
|
||||
self->use = trigger_gravity_use;
|
||||
self->solid = SOLID_NOT;
|
||||
}
|
||||
|
||||
self->touch = trigger_gravity_touch;
|
||||
gi.linkentity(self);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -880,6 +1003,7 @@ SP_trigger_gravity(edict_t *self)
|
|||
* "speed" default to 200, the speed thrown forward
|
||||
* "height" default to 200, the speed thrown upwards
|
||||
*/
|
||||
|
||||
void
|
||||
trigger_monsterjump_touch(edict_t *self, edict_t *other, cplane_t *plane /* unused */,
|
||||
csurface_t *surf /* unused */)
|
||||
|
|
|
@ -32,6 +32,7 @@ void infantry_die(edict_t *self, edict_t *inflictor, edict_t *attacker,
|
|||
void infantry_stand(edict_t *self);
|
||||
void monster_use(edict_t *self, edict_t *other, edict_t *activator);
|
||||
qboolean FindTarget(edict_t *self);
|
||||
void SpawnTargetingSystem(edict_t *turret);
|
||||
|
||||
void
|
||||
AnglesNormalize(vec3_t vec)
|
||||
|
@ -110,12 +111,13 @@ turret_blocked(edict_t *self, edict_t *other)
|
|||
* "speed" default 50
|
||||
* "dmg" default 10
|
||||
* "angle" point this forward
|
||||
* "target" point this at an info_notnull at the muzzle tip
|
||||
* "target" point this at an info_notnull at the muzzle tip
|
||||
* "minpitch" min acceptable pitch angle : default -30
|
||||
* "maxpitch" max acceptable pitch angle : default 30
|
||||
* "minyaw" min acceptable yaw angle : default 0
|
||||
* "maxyaw" max acceptable yaw angle : default 360
|
||||
* "minyaw" min acceptable yaw angle : default 0
|
||||
* "maxyaw" max acceptable yaw angle : default 360
|
||||
*/
|
||||
|
||||
void
|
||||
turret_breach_fire(edict_t *self)
|
||||
{
|
||||
|
@ -321,9 +323,17 @@ turret_breach_finish_init(edict_t *self)
|
|||
else
|
||||
{
|
||||
self->target_ent = G_PickTarget(self->target);
|
||||
VectorSubtract(self->target_ent->s.origin,
|
||||
self->s.origin, self->move_origin);
|
||||
G_FreeEdict(self->target_ent);
|
||||
|
||||
if (self->target_ent)
|
||||
{
|
||||
VectorSubtract(self->target_ent->s.origin,
|
||||
self->s.origin, self->move_origin);
|
||||
G_FreeEdict(self->target_ent);
|
||||
}
|
||||
else
|
||||
{
|
||||
gi.dprintf("could not find target entity for %s at %s\n", self->classname, vtos(self->s.origin));
|
||||
}
|
||||
}
|
||||
|
||||
self->teammaster->dmg = self->dmg;
|
||||
|
@ -388,6 +398,7 @@ SP_turret_breach(edict_t *self)
|
|||
* This portion of the turret changes yaw only.
|
||||
* MUST be teamed with a turret_breach.
|
||||
*/
|
||||
|
||||
void
|
||||
SP_turret_base(edict_t *self)
|
||||
{
|
||||
|
@ -607,3 +618,228 @@ SP_turret_driver(edict_t *self)
|
|||
|
||||
gi.linkentity(self);
|
||||
}
|
||||
|
||||
/*
|
||||
* invisible turret drivers so we can have unmanned turrets.
|
||||
* originally designed to shoot at func_trains and such, so they
|
||||
* fire at the center of the bounding box, rather than the entity's
|
||||
* origin. */
|
||||
|
||||
void
|
||||
turret_brain_think(edict_t *self)
|
||||
{
|
||||
vec3_t dir;
|
||||
vec3_t endpos;
|
||||
float reaction_time;
|
||||
trace_t trace;
|
||||
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
self->nextthink = level.time + FRAMETIME;
|
||||
|
||||
if (self->enemy)
|
||||
{
|
||||
if (!self->enemy->inuse)
|
||||
{
|
||||
self->enemy = NULL;
|
||||
}
|
||||
else if (self->enemy->takedamage && (self->enemy->health <= 0))
|
||||
{
|
||||
self->enemy = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!self->enemy)
|
||||
{
|
||||
if (!FindTarget(self))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
self->monsterinfo.trail_time = level.time;
|
||||
self->monsterinfo.aiflags &= ~AI_LOST_SIGHT;
|
||||
|
||||
VectorAdd(self->enemy->absmax, self->enemy->absmin, endpos);
|
||||
VectorScale(endpos, 0.5, endpos);
|
||||
}
|
||||
else
|
||||
{
|
||||
VectorAdd(self->enemy->absmax, self->enemy->absmin, endpos);
|
||||
VectorScale(endpos, 0.5, endpos);
|
||||
|
||||
trace = gi.trace(self->target_ent->s.origin, vec3_origin, vec3_origin,
|
||||
endpos, self->target_ent, MASK_SHOT);
|
||||
|
||||
if ((trace.fraction == 1) || (trace.ent == self->enemy))
|
||||
{
|
||||
if (self->monsterinfo.aiflags & AI_LOST_SIGHT)
|
||||
{
|
||||
self->monsterinfo.trail_time = level.time;
|
||||
self->monsterinfo.aiflags &= ~AI_LOST_SIGHT;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
self->monsterinfo.aiflags |= AI_LOST_SIGHT;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* let the turret know where we want it to aim */
|
||||
VectorSubtract(endpos, self->target_ent->s.origin, dir);
|
||||
vectoangles(dir, self->target_ent->move_angles);
|
||||
|
||||
/* decide if we should shoot */
|
||||
if (level.time < self->monsterinfo.attack_finished)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->delay)
|
||||
{
|
||||
reaction_time = self->delay;
|
||||
}
|
||||
else
|
||||
{
|
||||
reaction_time = (3 - skill->value) * 1.0;
|
||||
}
|
||||
|
||||
if ((level.time - self->monsterinfo.trail_time) < reaction_time)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
self->monsterinfo.attack_finished = level.time + reaction_time + 1.0;
|
||||
self->target_ent->spawnflags |= 65536;
|
||||
}
|
||||
|
||||
void
|
||||
turret_brain_link(edict_t *self)
|
||||
{
|
||||
vec3_t vec;
|
||||
edict_t *ent;
|
||||
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->killtarget)
|
||||
{
|
||||
self->enemy = G_PickTarget(self->killtarget);
|
||||
}
|
||||
|
||||
self->think = turret_brain_think;
|
||||
self->nextthink = level.time + FRAMETIME;
|
||||
|
||||
self->target_ent = G_PickTarget(self->target);
|
||||
self->target_ent->owner = self;
|
||||
self->target_ent->teammaster->owner = self;
|
||||
VectorCopy(self->target_ent->s.angles, self->s.angles);
|
||||
|
||||
vec[0] = self->target_ent->s.origin[0] - self->s.origin[0];
|
||||
vec[1] = self->target_ent->s.origin[1] - self->s.origin[1];
|
||||
vec[2] = 0;
|
||||
self->move_origin[0] = VectorLength(vec);
|
||||
|
||||
VectorSubtract(self->s.origin, self->target_ent->s.origin, vec);
|
||||
vectoangles(vec, vec);
|
||||
AnglesNormalize(vec);
|
||||
self->move_origin[1] = vec[1];
|
||||
|
||||
self->move_origin[2] = self->s.origin[2] - self->target_ent->s.origin[2];
|
||||
|
||||
/* add the driver to the end of them team chain */
|
||||
for (ent = self->target_ent->teammaster; ent->teamchain; ent = ent->teamchain)
|
||||
{
|
||||
}
|
||||
|
||||
ent->teamchain = self;
|
||||
self->teammaster = self->target_ent->teammaster;
|
||||
self->flags |= FL_TEAMSLAVE;
|
||||
}
|
||||
|
||||
void
|
||||
turret_brain_deactivate(edict_t *self, edict_t *other /* unused */, edict_t *activator /* unused */)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
self->think = NULL;
|
||||
self->nextthink = 0;
|
||||
}
|
||||
|
||||
void
|
||||
turret_brain_activate(edict_t *self, edict_t *other /* unused */, edict_t *activator)
|
||||
{
|
||||
if (!self || !activator)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!self->enemy)
|
||||
{
|
||||
self->enemy = activator;
|
||||
}
|
||||
|
||||
/* wait at least 3 seconds to fire. */
|
||||
self->monsterinfo.attack_finished = level.time + 3;
|
||||
self->use = turret_brain_deactivate;
|
||||
|
||||
self->think = turret_brain_link;
|
||||
self->nextthink = level.time + FRAMETIME;
|
||||
}
|
||||
|
||||
/*
|
||||
* QUAKED turret_invisible_brain (1 .5 0) (-16 -16 -16) (16 16 16)
|
||||
* Invisible brain to drive the turret.
|
||||
*
|
||||
* Does not search for targets. If targeted, can only be turned on once
|
||||
* and then off once. After that they are completely disabled.
|
||||
*
|
||||
* "delay" the delay between firing (default ramps for skill level)
|
||||
* "Target" the turret breach
|
||||
* "Killtarget" the item you want it to attack.
|
||||
* Target the brain if you want it activated later, instead of immediately. It will wait 3 seconds
|
||||
* before firing to acquire the target.
|
||||
*/
|
||||
void
|
||||
SP_turret_invisible_brain(edict_t *self)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!self->killtarget)
|
||||
{
|
||||
gi.dprintf("turret_invisible_brain with no killtarget!\n");
|
||||
G_FreeEdict(self);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!self->target)
|
||||
{
|
||||
gi.dprintf("turret_invisible_brain with no target!\n");
|
||||
G_FreeEdict(self);
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->targetname)
|
||||
{
|
||||
self->use = turret_brain_activate;
|
||||
}
|
||||
else
|
||||
{
|
||||
self->think = turret_brain_link;
|
||||
self->nextthink = level.time + FRAMETIME;
|
||||
}
|
||||
|
||||
self->movetype = MOVETYPE_PUSH;
|
||||
gi.linkentity(self);
|
||||
}
|
||||
|
|
|
@ -290,6 +290,7 @@ void
|
|||
G_UseTargets(edict_t *ent, edict_t *activator)
|
||||
{
|
||||
edict_t *t;
|
||||
edict_t *master;
|
||||
|
||||
if (!ent)
|
||||
{
|
||||
|
@ -345,6 +346,7 @@ G_UseTargets(edict_t *ent, edict_t *activator)
|
|||
{
|
||||
level.total_secrets--;
|
||||
}
|
||||
|
||||
/* same deal with target_goal, but also turn off CD music if applicable */
|
||||
else if (!Q_stricmp(t->classname,"target_goal"))
|
||||
{
|
||||
|
@ -356,6 +358,30 @@ G_UseTargets(edict_t *ent, edict_t *activator)
|
|||
}
|
||||
}
|
||||
|
||||
/* if this entity is part of a train, cleanly remove it */
|
||||
if (t->flags & FL_TEAMSLAVE)
|
||||
{
|
||||
master = t->teammaster;
|
||||
|
||||
while (master)
|
||||
{
|
||||
if (master->teamchain == t)
|
||||
{
|
||||
master->teamchain = t->teamchain;
|
||||
break;
|
||||
}
|
||||
|
||||
master = master->teamchain;
|
||||
}
|
||||
}
|
||||
|
||||
/* correct killcounter if a living monster gets killtargeted */
|
||||
if ((t->monsterinfo.checkattack || strcmp (t->classname, "turret_driver") == 0) &&
|
||||
!(t->monsterinfo.aiflags & (AI_GOOD_GUY|AI_DO_NOT_COUNT)) && t->deadflag != DEAD_DEAD)
|
||||
{
|
||||
level.killed_monsters++;
|
||||
}
|
||||
|
||||
G_FreeEdict(t);
|
||||
|
||||
if (!ent->inuse)
|
||||
|
|
|
@ -690,28 +690,105 @@ ChickRocket(edict_t *self)
|
|||
vec3_t start;
|
||||
vec3_t dir;
|
||||
vec3_t vec;
|
||||
trace_t trace; /* check target */
|
||||
int rocketSpeed;
|
||||
float dist;
|
||||
vec3_t target;
|
||||
qboolean blindfire = false;
|
||||
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->monsterinfo.aiflags & AI_MANUAL_STEERING)
|
||||
{
|
||||
blindfire = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
blindfire = false;
|
||||
}
|
||||
|
||||
if (!self->enemy || !self->enemy->inuse)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AngleVectors(self->s.angles, forward, right, NULL);
|
||||
G_ProjectSource(self->s.origin, monster_flash_offset[MZ2_CHICK_ROCKET_1],
|
||||
forward, right, start);
|
||||
|
||||
VectorCopy(self->enemy->s.origin, vec);
|
||||
vec[2] += self->enemy->viewheight;
|
||||
VectorSubtract(vec, start, dir);
|
||||
VectorNormalize(dir);
|
||||
rocketSpeed = 500 + (100 * skill->value); /* rock & roll.... :) */
|
||||
|
||||
if (!strcmp(self->classname, "monster_chick_heat"))
|
||||
if (blindfire)
|
||||
{
|
||||
monster_fire_heat(self, start, dir, 50, 500, MZ2_CHICK_ROCKET_1);
|
||||
VectorCopy(self->monsterinfo.blind_fire_target, target);
|
||||
}
|
||||
else
|
||||
{
|
||||
monster_fire_rocket(self, start, dir, 50, 500, MZ2_CHICK_ROCKET_1);
|
||||
VectorCopy(self->enemy->s.origin, target);
|
||||
}
|
||||
|
||||
/* blindfire shooting */
|
||||
if (blindfire)
|
||||
{
|
||||
VectorCopy(target, vec);
|
||||
VectorSubtract(vec, start, dir);
|
||||
}
|
||||
/* don't shoot at feet if they're above where i'm shooting from. */
|
||||
else if ((random() < 0.33) || (start[2] < self->enemy->absmin[2]))
|
||||
{
|
||||
VectorCopy(target, vec);
|
||||
vec[2] += self->enemy->viewheight;
|
||||
VectorSubtract(vec, start, dir);
|
||||
}
|
||||
else
|
||||
{
|
||||
VectorCopy(target, vec);
|
||||
vec[2] = self->enemy->absmin[2];
|
||||
VectorSubtract(vec, start, dir);
|
||||
}
|
||||
|
||||
/* lead target (not when blindfiring) */
|
||||
if ((!blindfire) && ((random() < (0.2 + ((3 - skill->value) * 0.15)))))
|
||||
{
|
||||
float time;
|
||||
|
||||
dist = VectorLength(dir);
|
||||
time = dist / rocketSpeed;
|
||||
VectorMA(vec, time, self->enemy->velocity, vec);
|
||||
VectorSubtract(vec, start, dir);
|
||||
}
|
||||
|
||||
VectorNormalize(dir);
|
||||
|
||||
if (blindfire)
|
||||
{
|
||||
/* blindfire has different fail criteria for the trace */
|
||||
if (!blind_rocket_ok(self, start, right, target, 10.0f, dir))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
trace = gi.trace(start, vec3_origin, vec3_origin, vec, self, MASK_SHOT);
|
||||
|
||||
if (((trace.ent != self->enemy) && (trace.ent != world)) ||
|
||||
((trace.fraction <= 0.5f) && !trace.ent->client))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!strcmp(self->classname, "monster_chick_heat"))
|
||||
{
|
||||
monster_fire_heat(self, start, dir, 50, rocketSpeed, MZ2_CHICK_ROCKET_1);
|
||||
}
|
||||
else
|
||||
{
|
||||
monster_fire_rocket(self, start, dir, 50, rocketSpeed, MZ2_CHICK_ROCKET_1);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1157,6 +1234,7 @@ SP_monster_chick(edict_t *self)
|
|||
self->monsterinfo.currentmove = &chick_move_stand;
|
||||
self->monsterinfo.scale = MODEL_SCALE;
|
||||
|
||||
self->monsterinfo.blindfire = true;
|
||||
walkmonster_start(self);
|
||||
}
|
||||
|
||||
|
|
|
@ -22,8 +22,9 @@
|
|||
*
|
||||
* Soldier aka "Guard". This is the most complex enemy in Quake 2, since
|
||||
* it uses all AI features (dodging, sight, crouching, etc) and comes
|
||||
* in a myriad of variants. In Xatrix it's even more complex due to
|
||||
* another 4 variants added.
|
||||
* in a myriad of variants.
|
||||
* In Rogue it's even more complex due to the blindfire stuff.
|
||||
* In Xatrix it's even more complex due to another 4 variants added.
|
||||
*
|
||||
* =======================================================================
|
||||
*/
|
||||
|
@ -47,8 +48,11 @@ static int sound_step2;
|
|||
static int sound_step3;
|
||||
static int sound_step4;
|
||||
|
||||
void soldier_duck_up(edict_t *self);
|
||||
void soldier_stand(edict_t *self);
|
||||
void soldier_run(edict_t *self);
|
||||
void soldier_fire(edict_t *self, int);
|
||||
void soldier_blind(edict_t *self);
|
||||
void soldierh_stand(edict_t *self);
|
||||
void soldierh_run(edict_t *self);
|
||||
extern void brain_dabeam(edict_t *self);
|
||||
|
@ -89,6 +93,27 @@ soldier_footstep(edict_t *self)
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
soldier_start_charge(edict_t *self)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
self->monsterinfo.aiflags |= AI_CHARGING;
|
||||
}
|
||||
|
||||
void
|
||||
soldier_stop_charge(edict_t *self)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
self->monsterinfo.aiflags &= ~AI_CHARGING;
|
||||
}
|
||||
|
||||
void
|
||||
soldier_idle(edict_t *self)
|
||||
|
@ -347,6 +372,20 @@ mmove_t soldier_move_start_run =
|
|||
soldier_run
|
||||
};
|
||||
|
||||
void
|
||||
soldier_fire_run(edict_t *self)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ((self->s.skinnum <= 1) && (self->enemy) && visible(self, self->enemy))
|
||||
{
|
||||
soldier_fire(self, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static mframe_t soldier_frames_run[] = {
|
||||
{ai_run, 10, NULL},
|
||||
{ai_run, 11, soldier_footstep},
|
||||
|
@ -372,6 +411,8 @@ soldier_run(edict_t *self)
|
|||
return;
|
||||
}
|
||||
|
||||
monster_done_dodge(self);
|
||||
|
||||
if (self->monsterinfo.aiflags & AI_STAND_GROUND)
|
||||
{
|
||||
self->monsterinfo.currentmove = &soldier_move_stand1;
|
||||
|
@ -498,6 +539,12 @@ soldier_pain(edict_t *self, edict_t *other /* unused */,
|
|||
self->s.skinnum |= 1;
|
||||
}
|
||||
|
||||
monster_done_dodge(self);
|
||||
soldier_stop_charge(self);
|
||||
|
||||
/* if we're blind firing, this needs to be turned off here */
|
||||
self->monsterinfo.aiflags &= ~AI_MANUAL_STEERING;
|
||||
|
||||
if (level.time < self->pain_debounce_time)
|
||||
{
|
||||
if ((self->velocity[2] > 100) &&
|
||||
|
@ -505,6 +552,12 @@ soldier_pain(edict_t *self, edict_t *other /* unused */,
|
|||
(self->monsterinfo.currentmove == &soldier_move_pain2) ||
|
||||
(self->monsterinfo.currentmove == &soldier_move_pain3)))
|
||||
{
|
||||
/* clear duck flag */
|
||||
if (self->monsterinfo.aiflags & AI_DUCKED)
|
||||
{
|
||||
monster_duck_up(self);
|
||||
}
|
||||
|
||||
self->monsterinfo.currentmove = &soldier_move_pain4;
|
||||
}
|
||||
|
||||
|
@ -530,6 +583,12 @@ soldier_pain(edict_t *self, edict_t *other /* unused */,
|
|||
|
||||
if (self->velocity[2] > 100)
|
||||
{
|
||||
/* clear duck flag */
|
||||
if (self->monsterinfo.aiflags & AI_DUCKED)
|
||||
{
|
||||
monster_duck_up(self);
|
||||
}
|
||||
|
||||
self->monsterinfo.currentmove = &soldier_move_pain4;
|
||||
return;
|
||||
}
|
||||
|
@ -553,6 +612,12 @@ soldier_pain(edict_t *self, edict_t *other /* unused */,
|
|||
{
|
||||
self->monsterinfo.currentmove = &soldier_move_pain3;
|
||||
}
|
||||
|
||||
/* clear duck flag */
|
||||
if (self->monsterinfo.aiflags & AI_DUCKED)
|
||||
{
|
||||
monster_duck_up(self);
|
||||
}
|
||||
}
|
||||
|
||||
static int blaster_flash[] =
|
||||
|
@ -592,7 +657,7 @@ static int machinegun_flash[] =
|
|||
};
|
||||
|
||||
void
|
||||
soldier_fire(edict_t *self, int flash_number)
|
||||
soldier_fire(edict_t *self, int in_flash_number)
|
||||
{
|
||||
vec3_t start;
|
||||
vec3_t forward, right, up;
|
||||
|
@ -601,12 +666,33 @@ soldier_fire(edict_t *self, int flash_number)
|
|||
vec3_t end;
|
||||
float r, u;
|
||||
int flash_index;
|
||||
int flash_number;
|
||||
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
vec3_t aim_norm;
|
||||
float angle;
|
||||
trace_t tr;
|
||||
vec3_t aim_good;
|
||||
|
||||
if ((!self->enemy) || (!self->enemy->inuse))
|
||||
{
|
||||
self->monsterinfo.aiflags &= ~AI_HOLD_FRAME;
|
||||
return;
|
||||
}
|
||||
|
||||
if (in_flash_number < 0)
|
||||
{
|
||||
flash_number = -1 * in_flash_number;
|
||||
}
|
||||
else
|
||||
{
|
||||
flash_number = in_flash_number;
|
||||
}
|
||||
|
||||
if (self->s.skinnum < 2)
|
||||
{
|
||||
flash_index = blaster_flash[flash_number];
|
||||
|
@ -624,7 +710,7 @@ soldier_fire(edict_t *self, int flash_number)
|
|||
G_ProjectSource(self->s.origin, monster_flash_offset[flash_index],
|
||||
forward, right, start);
|
||||
|
||||
if ((flash_number == 5) || (flash_number == 6))
|
||||
if ((flash_number == 5) || (flash_number == 6)) /* he's dead */
|
||||
{
|
||||
VectorCopy(forward, aim);
|
||||
}
|
||||
|
@ -633,11 +719,34 @@ soldier_fire(edict_t *self, int flash_number)
|
|||
VectorCopy(self->enemy->s.origin, end);
|
||||
end[2] += self->enemy->viewheight;
|
||||
VectorSubtract(end, start, aim);
|
||||
VectorCopy(end, aim_good);
|
||||
|
||||
if (in_flash_number < 0)
|
||||
{
|
||||
VectorCopy(aim, aim_norm);
|
||||
VectorNormalize(aim_norm);
|
||||
angle = DotProduct(aim_norm, forward);
|
||||
|
||||
if (angle < 0.9) /* ~25 degree angle */
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
vectoangles(aim, dir);
|
||||
AngleVectors(dir, forward, right, up);
|
||||
|
||||
r = crandom() * 1000;
|
||||
u = crandom() * 500;
|
||||
if (skill->value < SKILL_HARD)
|
||||
{
|
||||
r = crandom() * 1000;
|
||||
u = crandom() * 500;
|
||||
}
|
||||
else
|
||||
{
|
||||
r = crandom() * 500;
|
||||
u = crandom() * 250;
|
||||
}
|
||||
|
||||
VectorMA(start, 8192, forward, end);
|
||||
VectorMA(end, r, right, end);
|
||||
VectorMA(end, u, up, end);
|
||||
|
@ -646,6 +755,16 @@ soldier_fire(edict_t *self, int flash_number)
|
|||
VectorNormalize(aim);
|
||||
}
|
||||
|
||||
if (!((flash_number == 5) || (flash_number == 6))) /* he's dead */
|
||||
{
|
||||
tr = gi.trace(start, NULL, NULL, aim_good, self, MASK_SHOT);
|
||||
|
||||
if ((tr.ent != self->enemy) && (tr.ent != world))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (self->s.skinnum <= 1)
|
||||
{
|
||||
monster_fire_blaster(self, start, aim, 5, 600, flash_index, EF_BLASTER);
|
||||
|
@ -658,6 +777,7 @@ soldier_fire(edict_t *self, int flash_number)
|
|||
}
|
||||
else
|
||||
{
|
||||
/* changed to wait from pausetime to not interfere with dodge code */
|
||||
if (!(self->monsterinfo.aiflags & AI_HOLD_FRAME))
|
||||
{
|
||||
self->monsterinfo.pausetime = level.time + (3 + randk() % 8) * FRAMETIME;
|
||||
|
@ -698,6 +818,17 @@ soldier_attack1_refire1(edict_t *self)
|
|||
return;
|
||||
}
|
||||
|
||||
if (self->monsterinfo.aiflags & AI_MANUAL_STEERING)
|
||||
{
|
||||
self->monsterinfo.aiflags &= ~AI_MANUAL_STEERING;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!self->enemy)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->s.skinnum > 1)
|
||||
{
|
||||
return;
|
||||
|
@ -727,6 +858,11 @@ soldier_attack1_refire2(edict_t *self)
|
|||
return;
|
||||
}
|
||||
|
||||
if (!self->enemy)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->s.skinnum < 2)
|
||||
{
|
||||
return;
|
||||
|
@ -787,6 +923,11 @@ soldier_attack2_refire1(edict_t *self)
|
|||
return;
|
||||
}
|
||||
|
||||
if (!self->enemy)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->s.skinnum > 1)
|
||||
{
|
||||
return;
|
||||
|
@ -816,6 +957,11 @@ soldier_attack2_refire2(edict_t *self)
|
|||
return;
|
||||
}
|
||||
|
||||
if (!self->enemy)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->s.skinnum < 2)
|
||||
{
|
||||
return;
|
||||
|
@ -992,24 +1138,33 @@ soldier_attack6_refire(edict_t *self)
|
|||
return;
|
||||
}
|
||||
|
||||
/* make sure dodge & charge bits are cleared */
|
||||
monster_done_dodge(self);
|
||||
soldier_stop_charge(self);
|
||||
|
||||
if (!self->enemy)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->enemy->health <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (range(self, self->enemy) < RANGE_MID)
|
||||
if (range(self, self->enemy) < RANGE_NEAR)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (skill->value == SKILL_HARDPLUS)
|
||||
if ((skill->value == SKILL_HARDPLUS) || ((random() < (0.25 * ((float)skill->value)))))
|
||||
{
|
||||
self->monsterinfo.nextframe = FRAME_runs03;
|
||||
}
|
||||
}
|
||||
|
||||
static mframe_t soldier_frames_attack6[] = {
|
||||
{ai_charge, 10, NULL},
|
||||
{ai_charge, 10, soldier_start_charge},
|
||||
{ai_charge, 4, NULL},
|
||||
{ai_charge, 12, soldier_footstep},
|
||||
{ai_charge, 11, soldier_fire8},
|
||||
|
@ -1036,25 +1191,82 @@ mmove_t soldier_move_attack6 =
|
|||
void
|
||||
soldier_attack(edict_t *self)
|
||||
{
|
||||
float r, chance;
|
||||
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->s.skinnum < 4)
|
||||
monster_done_dodge(self);
|
||||
|
||||
/* blindfire! */
|
||||
if (self->monsterinfo.attack_state == AS_BLIND)
|
||||
{
|
||||
if (random() < 0.5)
|
||||
/* setup shot probabilities */
|
||||
if (self->monsterinfo.blind_fire_delay < 1.0)
|
||||
{
|
||||
self->monsterinfo.currentmove = &soldier_move_attack1;
|
||||
chance = 1.0;
|
||||
}
|
||||
else if (self->monsterinfo.blind_fire_delay < 7.5)
|
||||
{
|
||||
chance = 0.4;
|
||||
}
|
||||
else
|
||||
{
|
||||
self->monsterinfo.currentmove = &soldier_move_attack2;
|
||||
chance = 0.1;
|
||||
}
|
||||
|
||||
r = random();
|
||||
|
||||
/* minimum of 2 seconds, plus 0-3, after the shots are done */
|
||||
self->monsterinfo.blind_fire_delay += 2.1 + 2.0 + random() * 3.0;
|
||||
|
||||
/* don't shoot at the origin */
|
||||
if (VectorCompare(self->monsterinfo.blind_fire_target, vec3_origin))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* don't shoot if the dice say not to */
|
||||
if (r > chance)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* turn on manual steering to signal both manual steering and blindfire */
|
||||
self->monsterinfo.aiflags |= AI_MANUAL_STEERING;
|
||||
self->monsterinfo.currentmove = &soldier_move_attack1;
|
||||
self->monsterinfo.attack_finished = level.time + 1.5 + random();
|
||||
return;
|
||||
}
|
||||
|
||||
r = random();
|
||||
|
||||
if ((!(self->monsterinfo.aiflags & (AI_BLOCKED | AI_STAND_GROUND))) &&
|
||||
(range(self, self->enemy) >= RANGE_NEAR) &&
|
||||
((r < (skill->value * 0.25)) &&
|
||||
(self->s.skinnum <= 3)))
|
||||
{
|
||||
self->monsterinfo.currentmove = &soldier_move_attack6;
|
||||
}
|
||||
else
|
||||
{
|
||||
self->monsterinfo.currentmove = &soldier_move_attack4;
|
||||
if (self->s.skinnum < 4)
|
||||
{
|
||||
if (random() < 0.5)
|
||||
{
|
||||
self->monsterinfo.currentmove = &soldier_move_attack1;
|
||||
}
|
||||
else
|
||||
{
|
||||
self->monsterinfo.currentmove = &soldier_move_attack2;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
self->monsterinfo.currentmove = &soldier_move_attack4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1075,9 +1287,11 @@ soldier_sight(edict_t *self, edict_t *other /* unused */)
|
|||
gi.sound(self, CHAN_VOICE, sound_sight2, 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
if ((skill->value > SKILL_EASY) && (range(self, self->enemy) >= RANGE_MID))
|
||||
if ((skill->value > SKILL_EASY) && (self->enemy) &&
|
||||
(range(self, self->enemy) >= RANGE_NEAR))
|
||||
{
|
||||
if (random() > 0.5)
|
||||
/* don't let machinegunners run & shoot */
|
||||
if ((random() > 0.75) && (self->s.skinnum <= 3))
|
||||
{
|
||||
self->monsterinfo.currentmove = &soldier_move_attack6;
|
||||
}
|
||||
|
@ -1118,6 +1332,29 @@ mmove_t soldier_move_duck =
|
|||
soldier_run
|
||||
};
|
||||
|
||||
qboolean
|
||||
soldier_blocked(edict_t *self, float dist)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/* don't do anything if you're dodging */
|
||||
if ((self->monsterinfo.aiflags & AI_DODGING) ||
|
||||
(self->monsterinfo.aiflags & AI_DUCKED))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (blocked_checkplat(self, dist))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
soldier_dodge(edict_t *self, edict_t *attacker, float eta,
|
||||
trace_t *tr /* unused */)
|
||||
|
@ -1220,6 +1457,46 @@ soldier_dead(edict_t *self)
|
|||
gi.linkentity(self);
|
||||
}
|
||||
|
||||
void
|
||||
soldier_dead2(edict_t *self)
|
||||
{
|
||||
vec3_t tempmins, tempmaxs, temporg;
|
||||
trace_t tr;
|
||||
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
VectorCopy(self->s.origin, temporg);
|
||||
/* this is because location traces done at the
|
||||
floor are guaranteed to hit the floor (inside
|
||||
the sv_trace code it grows the bbox by 1 in
|
||||
all directions) */
|
||||
temporg[2] += 1;
|
||||
|
||||
VectorSet(tempmins, -32, -32, -24);
|
||||
VectorSet(tempmaxs, 32, 32, -8);
|
||||
|
||||
tr = gi.trace(temporg, tempmins, tempmaxs, temporg, self, MASK_SOLID);
|
||||
|
||||
if (tr.startsolid || tr.allsolid)
|
||||
{
|
||||
VectorSet(self->mins, -16, -16, -24);
|
||||
VectorSet(self->maxs, 16, 16, -8);
|
||||
}
|
||||
else
|
||||
{
|
||||
VectorCopy(tempmins, self->mins);
|
||||
VectorCopy(tempmaxs, self->maxs);
|
||||
}
|
||||
|
||||
self->movetype = MOVETYPE_TOSS;
|
||||
self->svflags |= SVF_DEADMONSTER;
|
||||
self->nextthink = 0;
|
||||
gi.linkentity(self);
|
||||
}
|
||||
|
||||
static mframe_t soldier_frames_death1[] = {
|
||||
{ai_move, 0, NULL},
|
||||
{ai_move, -10, NULL},
|
||||
|
@ -1445,7 +1722,7 @@ mmove_t soldier_move_death4 =
|
|||
FRAME_death401,
|
||||
FRAME_death453,
|
||||
soldier_frames_death4,
|
||||
soldier_dead
|
||||
soldier_dead2
|
||||
};
|
||||
|
||||
static mframe_t soldier_frames_death5[] = {
|
||||
|
@ -1591,6 +1868,117 @@ soldier_die(edict_t *self, edict_t *inflictor /* unused */,
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
soldier_sidestep(edict_t *self)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->s.skinnum <= 3)
|
||||
{
|
||||
if (self->monsterinfo.currentmove != &soldier_move_attack6)
|
||||
{
|
||||
self->monsterinfo.currentmove = &soldier_move_attack6;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (self->monsterinfo.currentmove != &soldier_move_start_run)
|
||||
{
|
||||
self->monsterinfo.currentmove = &soldier_move_start_run;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
soldier_duck(edict_t *self, float eta)
|
||||
{
|
||||
float r;
|
||||
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* has to be done immediately otherwise he can get stuck */
|
||||
monster_duck_down(self);
|
||||
|
||||
if (skill->value == SKILL_EASY)
|
||||
{
|
||||
self->monsterinfo.currentmove = &soldier_move_duck;
|
||||
self->monsterinfo.duck_wait_time = level.time + eta + 1;
|
||||
return;
|
||||
}
|
||||
|
||||
r = random();
|
||||
|
||||
if (r > (skill->value * 0.3))
|
||||
{
|
||||
self->monsterinfo.currentmove = &soldier_move_duck;
|
||||
self->monsterinfo.duck_wait_time = level.time + eta + (0.1 * (3 - skill->value));
|
||||
}
|
||||
else
|
||||
{
|
||||
self->monsterinfo.currentmove = &soldier_move_attack3;
|
||||
self->monsterinfo.duck_wait_time = level.time + eta + 1;
|
||||
}
|
||||
}
|
||||
|
||||
static mframe_t soldier_frames_blind[] = {
|
||||
{ai_move, 0, soldier_idle},
|
||||
{ai_move, 0, NULL},
|
||||
{ai_move, 0, NULL},
|
||||
{ai_move, 0, NULL},
|
||||
{ai_move, 0, NULL},
|
||||
{ai_move, 0, NULL},
|
||||
{ai_move, 0, NULL},
|
||||
{ai_move, 0, NULL},
|
||||
{ai_move, 0, NULL},
|
||||
{ai_move, 0, NULL},
|
||||
|
||||
{ai_move, 0, NULL},
|
||||
{ai_move, 0, NULL},
|
||||
{ai_move, 0, NULL},
|
||||
{ai_move, 0, NULL},
|
||||
{ai_move, 0, NULL},
|
||||
{ai_move, 0, NULL},
|
||||
{ai_move, 0, NULL},
|
||||
{ai_move, 0, NULL},
|
||||
{ai_move, 0, NULL},
|
||||
{ai_move, 0, NULL},
|
||||
|
||||
{ai_move, 0, NULL},
|
||||
{ai_move, 0, NULL},
|
||||
{ai_move, 0, NULL},
|
||||
{ai_move, 0, NULL},
|
||||
{ai_move, 0, NULL},
|
||||
{ai_move, 0, NULL},
|
||||
{ai_move, 0, NULL},
|
||||
{ai_move, 0, NULL},
|
||||
{ai_move, 0, NULL},
|
||||
{ai_move, 0, NULL}
|
||||
};
|
||||
|
||||
mmove_t soldier_move_blind = {
|
||||
FRAME_stand101,
|
||||
FRAME_stand130,
|
||||
soldier_frames_blind,
|
||||
soldier_blind
|
||||
};
|
||||
|
||||
void
|
||||
soldier_blind(edict_t *self)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
self->monsterinfo.currentmove = &soldier_move_blind;
|
||||
}
|
||||
|
||||
void
|
||||
SP_monster_soldier_x(edict_t *self)
|
||||
{
|
||||
|
@ -1631,6 +2019,16 @@ SP_monster_soldier_x(edict_t *self)
|
|||
self->monsterinfo.melee = NULL;
|
||||
self->monsterinfo.sight = soldier_sight;
|
||||
|
||||
self->monsterinfo.blocked = soldier_blocked;
|
||||
self->monsterinfo.duck = soldier_duck;
|
||||
self->monsterinfo.unduck = monster_duck_up;
|
||||
self->monsterinfo.sidestep = soldier_sidestep;
|
||||
|
||||
if (self->spawnflags & 8) /* blind */
|
||||
{
|
||||
self->monsterinfo.stand = soldier_blind;
|
||||
}
|
||||
|
||||
gi.linkentity(self);
|
||||
|
||||
self->monsterinfo.stand(self);
|
||||
|
@ -1639,7 +2037,9 @@ SP_monster_soldier_x(edict_t *self)
|
|||
}
|
||||
|
||||
/*
|
||||
* QUAKED monster_soldier_light (1 .5 0) (-16 -16 -24) (16 16 32) Ambush Trigger_Spawn Sight
|
||||
* QUAKED monster_soldier_light (1 .5 0) (-16 -16 -24) (16 16 32) Ambush Trigger_Spawn Sight Blind
|
||||
*
|
||||
* Blind - monster will just stand there until triggered
|
||||
*/
|
||||
void
|
||||
SP_monster_soldier_light(edict_t *self)
|
||||
|
@ -1671,10 +2071,13 @@ SP_monster_soldier_light(edict_t *self)
|
|||
gi.soundindex("soldier/solatck2.wav");
|
||||
|
||||
self->s.skinnum = 0;
|
||||
self->monsterinfo.blindfire = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* QUAKED monster_soldier (1 .5 0) (-16 -16 -24) (16 16 32) Ambush Trigger_Spawn Sight
|
||||
* QUAKED monster_soldier (1 .5 0) (-16 -16 -24) (16 16 32) Ambush Trigger_Spawn Sight Blind
|
||||
*
|
||||
* Blind - monster will just stand there until triggered
|
||||
*/
|
||||
void
|
||||
SP_monster_soldier(edict_t *self)
|
||||
|
@ -1707,7 +2110,9 @@ SP_monster_soldier(edict_t *self)
|
|||
}
|
||||
|
||||
/*
|
||||
* QUAKED monster_soldier_ss (1 .5 0) (-16 -16 -24) (16 16 32) Ambush Trigger_Spawn Sight
|
||||
* QUAKED monster_soldier_ss (1 .5 0) (-16 -16 -24) (16 16 32) Ambush Trigger_Spawn Sight Blind
|
||||
*
|
||||
* Blind - monster will just stand there until triggered
|
||||
*/
|
||||
void
|
||||
SP_monster_soldier_ss(edict_t *self)
|
||||
|
|
|
@ -1188,7 +1188,7 @@ extern void rotating_use ( edict_t * self , edict_t * other , edict_t * activato
|
|||
extern void rotating_touch ( edict_t * self , edict_t * other , cplane_t * plane , csurface_t * surf ) ;
|
||||
extern void rotating_blocked ( edict_t * self , edict_t * other ) ;
|
||||
extern void SP_func_plat ( edict_t * ent ) ;
|
||||
extern void plat_spawn_inside_trigger ( edict_t * ent ) ;
|
||||
extern edict_t * plat_spawn_inside_trigger ( edict_t * ent ) ;
|
||||
extern void Touch_Plat_Center ( edict_t * ent , edict_t * other , cplane_t * plane , csurface_t * surf ) ;
|
||||
extern void Use_Plat ( edict_t * ent , edict_t * other , edict_t * activator ) ;
|
||||
extern void plat_blocked ( edict_t * self , edict_t * other ) ;
|
||||
|
|
|
@ -1,9 +1,23 @@
|
|||
/*
|
||||
* Copyright (C) 1997-2001 Id Software, Inc.
|
||||
* Copyright (c) ZeniMax Media Inc.
|
||||
* Licensed under the GNU General Public License 2.0.
|
||||
*/
|
||||
|
||||
/*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||
* 02111-1307, USA.
|
||||
*
|
||||
* =======================================================================
|
||||
*
|
||||
* Level functions. Platforms, buttons, dooors and so on.
|
||||
|
@ -138,13 +152,13 @@ Move_Final(edict_t *ent)
|
|||
void
|
||||
Move_Begin(edict_t *ent)
|
||||
{
|
||||
float frames;
|
||||
|
||||
if (!ent)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
float frames;
|
||||
|
||||
if ((ent->moveinfo.speed * FRAMETIME) >= ent->moveinfo.remaining_distance)
|
||||
{
|
||||
Move_Final(ent);
|
||||
|
@ -152,9 +166,11 @@ Move_Begin(edict_t *ent)
|
|||
}
|
||||
|
||||
VectorScale(ent->moveinfo.dir, ent->moveinfo.speed, ent->velocity);
|
||||
frames = floor( (ent->moveinfo.remaining_distance /
|
||||
frames = floor(
|
||||
(ent->moveinfo.remaining_distance /
|
||||
ent->moveinfo.speed) / FRAMETIME);
|
||||
ent->moveinfo.remaining_distance -= frames * ent->moveinfo.speed * FRAMETIME;
|
||||
ent->moveinfo.remaining_distance -= frames * ent->moveinfo.speed *
|
||||
FRAMETIME;
|
||||
ent->nextthink = level.time + (frames * FRAMETIME);
|
||||
ent->think = Move_Final;
|
||||
}
|
||||
|
@ -195,8 +211,10 @@ Move_Calc(edict_t *ent, vec3_t dest, void (*func)(edict_t *))
|
|||
}
|
||||
}
|
||||
|
||||
/* Support routines for angular movement
|
||||
(changes in angle using avelocity) */
|
||||
/*
|
||||
* Support routines for angular movement
|
||||
* (changes in angle using avelocity)
|
||||
*/
|
||||
void
|
||||
AngleMove_Done(edict_t *ent)
|
||||
{
|
||||
|
@ -366,8 +384,12 @@ plat_CalcAcceleratedMove(moveinfo_t *moveinfo)
|
|||
{
|
||||
float f;
|
||||
|
||||
f = (moveinfo->accel + moveinfo->decel) / (moveinfo->accel * moveinfo->decel);
|
||||
moveinfo->move_speed = (-2 + sqrt(4 - 4 * f * (-2 * moveinfo->remaining_distance))) / (2 * f);
|
||||
f =
|
||||
(moveinfo->accel +
|
||||
moveinfo->decel) / (moveinfo->accel * moveinfo->decel);
|
||||
moveinfo->move_speed =
|
||||
(-2 +
|
||||
sqrt(4 - 4 * f * (-2 * moveinfo->remaining_distance))) / (2 * f);
|
||||
decel_dist = AccelerationDistance(moveinfo->move_speed, moveinfo->decel);
|
||||
}
|
||||
|
||||
|
@ -458,8 +480,10 @@ plat_Accelerate(moveinfo_t *moveinfo)
|
|||
p1_speed = (old_speed + moveinfo->move_speed) / 2.0;
|
||||
p2_distance = moveinfo->move_speed * (1.0 - (p1_distance / p1_speed));
|
||||
distance = p1_distance + p2_distance;
|
||||
moveinfo->current_speed = (p1_speed * (p1_distance /
|
||||
distance)) + (moveinfo->move_speed * (p2_distance / distance));
|
||||
moveinfo->current_speed =
|
||||
(p1_speed *
|
||||
(p1_distance /
|
||||
distance)) + (moveinfo->move_speed * (p2_distance / distance));
|
||||
moveinfo->next_speed = moveinfo->move_speed - moveinfo->decel *
|
||||
(p2_distance / distance);
|
||||
return;
|
||||
|
@ -469,6 +493,10 @@ plat_Accelerate(moveinfo_t *moveinfo)
|
|||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* The team has completed a frame of movement,
|
||||
* so change the speed for the next frame
|
||||
*/
|
||||
void
|
||||
Think_AccelMove(edict_t *ent)
|
||||
{
|
||||
|
@ -478,7 +506,12 @@ Think_AccelMove(edict_t *ent)
|
|||
}
|
||||
|
||||
ent->moveinfo.remaining_distance -= ent->moveinfo.current_speed;
|
||||
plat_CalcAcceleratedMove(&ent->moveinfo);
|
||||
|
||||
if (ent->moveinfo.current_speed == 0) /* starting or blocked */
|
||||
{
|
||||
plat_CalcAcceleratedMove(&ent->moveinfo);
|
||||
}
|
||||
|
||||
plat_Accelerate(&ent->moveinfo);
|
||||
|
||||
/* will the entire move complete on next frame? */
|
||||
|
@ -506,8 +539,8 @@ plat_hit_top(edict_t *ent)
|
|||
{
|
||||
if (ent->moveinfo.sound_end)
|
||||
{
|
||||
gi.sound(ent, CHAN_NO_PHS_ADD + CHAN_VOICE, ent->moveinfo.sound_end,
|
||||
1, ATTN_STATIC, 0);
|
||||
gi.sound(ent, CHAN_NO_PHS_ADD + CHAN_VOICE,
|
||||
ent->moveinfo.sound_end, 1, ATTN_STATIC, 0);
|
||||
}
|
||||
|
||||
ent->s.sound = 0;
|
||||
|
@ -698,7 +731,6 @@ wait_and_change(edict_t* ent, void (*afterwaitfunc)(edict_t *))
|
|||
else
|
||||
{
|
||||
afterwaitfunc(ent);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -729,7 +761,8 @@ Touch_Plat_Center(edict_t *ent, edict_t *other, cplane_t *plane /* unsed */,
|
|||
}
|
||||
else if (ent->moveinfo.state == STATE_TOP)
|
||||
{
|
||||
ent->nextthink = level.time + 1; /* the player is still on the plat, so delay going down */
|
||||
/* the player is still on the plat, so delay going down */
|
||||
ent->nextthink = level.time + 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1472,12 +1505,13 @@ SP_func_plat2(edict_t *ent)
|
|||
|
||||
/* ==================================================================== */
|
||||
|
||||
/* QUAKED func_rotating (0 .5 .8) ? START_ON REVERSE X_AXIS Y_AXIS TOUCH_PAIN STOP ANIMATED ANIMATED_FAST EAST MED HARD DM COOP ACCEL
|
||||
/*
|
||||
* QUAKED func_rotating (0 .5 .8) ? START_ON REVERSE X_AXIS Y_AXIS TOUCH_PAIN STOP ANIMATED ANIMATED_FAST EAST MED HARD DM COOP ACCEL
|
||||
*
|
||||
* You need to have an origin brush as part of this entity. The center
|
||||
* of that brush will bethe point around which it is rotated. It will
|
||||
* rotate around the Z axis by default. You can check either the
|
||||
* X_AXIS or Y_AXIS box to change that.
|
||||
* You need to have an origin brush as part of this entity.
|
||||
* The center of that brush will be the point around which it
|
||||
* is rotated. It will rotate around the Z axis by default.
|
||||
* You can check either the X_AXIS or Y_AXIS box to change that.
|
||||
*
|
||||
* func_rotating will use it's targets when it stops and starts.
|
||||
*
|
||||
|
@ -1667,7 +1701,6 @@ SP_func_rotating(edict_t *ent)
|
|||
}
|
||||
|
||||
ent->use = rotating_use;
|
||||
|
||||
ent->blocked = rotating_blocked;
|
||||
|
||||
if (ent->spawnflags & 1)
|
||||
|
@ -1949,7 +1982,6 @@ SP_func_button(edict_t *ent)
|
|||
gi.linkentity(ent);
|
||||
}
|
||||
|
||||
|
||||
/* ==================================================================== */
|
||||
|
||||
/*
|
||||
|
@ -2501,9 +2533,9 @@ door_blocked(edict_t *self, edict_t *other)
|
|||
return;
|
||||
}
|
||||
|
||||
/* if a door has a negative wait, it would never come
|
||||
back if blocked, so let it just squash the object
|
||||
to death real fast */
|
||||
/* if a door has a negative wait, it would never
|
||||
come back if blocked, so let it just squash the
|
||||
object to death real fast */
|
||||
if (self->moveinfo.wait >= 0)
|
||||
{
|
||||
if (self->moveinfo.state == STATE_DOWN)
|
||||
|
@ -2762,6 +2794,7 @@ Door_Activate(edict_t *self, edict_t *other /* unused */,
|
|||
* 3) medium
|
||||
* 4) heavy
|
||||
*/
|
||||
|
||||
void
|
||||
SP_func_door_rotating(edict_t *ent)
|
||||
{
|
||||
|
@ -3014,7 +3047,8 @@ SP_func_water(edict_t *self)
|
|||
self->speed = 25;
|
||||
}
|
||||
|
||||
self->moveinfo.accel = self->moveinfo.decel = self->moveinfo.speed = self->speed;
|
||||
self->moveinfo.accel = self->moveinfo.decel =
|
||||
self->moveinfo.speed = self->speed;
|
||||
|
||||
if (self->spawnflags & 2) /* smart water */
|
||||
{
|
||||
|
@ -3045,6 +3079,8 @@ SP_func_water(edict_t *self)
|
|||
gi.linkentity(self);
|
||||
}
|
||||
|
||||
/* ==================================================================== */
|
||||
|
||||
/*
|
||||
* QUAKED func_train (0 .5 .8) ? START_ON TOGGLE BLOCK_STOPS
|
||||
*
|
||||
|
@ -3135,6 +3171,7 @@ train_wait(edict_t *self)
|
|||
else if (self->spawnflags & TRAIN_TOGGLE)
|
||||
{
|
||||
self->target_ent = NULL;
|
||||
train_next(self);
|
||||
self->spawnflags &= ~TRAIN_START_ON;
|
||||
VectorClear(self->velocity);
|
||||
self->nextthink = 0;
|
||||
|
@ -3145,8 +3182,8 @@ train_wait(edict_t *self)
|
|||
if (self->moveinfo.sound_end)
|
||||
{
|
||||
gi.sound(self, CHAN_NO_PHS_ADD + CHAN_VOICE,
|
||||
self->moveinfo.sound_end,
|
||||
1, ATTN_STATIC, 0);
|
||||
self->moveinfo.sound_end, 1,
|
||||
ATTN_STATIC, 0);
|
||||
}
|
||||
|
||||
self->s.sound = 0;
|
||||
|
@ -3178,6 +3215,7 @@ train_next(edict_t *self)
|
|||
first = true;
|
||||
|
||||
again:
|
||||
|
||||
if (!self->target)
|
||||
{
|
||||
return;
|
||||
|
@ -3430,7 +3468,7 @@ SP_func_train(edict_t *self)
|
|||
if (self->target)
|
||||
{
|
||||
/* start trains on the second frame, to make
|
||||
sure their targets have had a chance to spawn */
|
||||
* sure their targets have had a chance to spawn */
|
||||
self->nextthink = level.time + FRAMETIME;
|
||||
self->think = func_train_find;
|
||||
}
|
||||
|
@ -3440,6 +3478,8 @@ SP_func_train(edict_t *self)
|
|||
}
|
||||
}
|
||||
|
||||
/* ==================================================================== */
|
||||
|
||||
/*
|
||||
* QUAKED trigger_elevator (0.3 0.1 0.6) (-8 -8 -8) (8 8 8)
|
||||
*/
|
||||
|
@ -3554,7 +3594,7 @@ func_timer_think(edict_t *self)
|
|||
void
|
||||
func_timer_use(edict_t *self, edict_t *other /* unused */, edict_t *activator)
|
||||
{
|
||||
if (!self)
|
||||
if (!self || !activator)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
@ -3853,16 +3893,16 @@ door_secret_die(edict_t *self, edict_t *inflictor /* unused */,
|
|||
void
|
||||
SP_func_door_secret(edict_t *ent)
|
||||
{
|
||||
if (!ent)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
vec3_t forward, right, up;
|
||||
float side;
|
||||
float width;
|
||||
float length;
|
||||
|
||||
if (!ent)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
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");
|
||||
|
@ -3979,3 +4019,231 @@ SP_func_killbox(edict_t *ent)
|
|||
ent->use = use_killbox;
|
||||
ent->svflags = SVF_NOCLIENT;
|
||||
}
|
||||
|
||||
/*
|
||||
* QUAKED rotating_light (0 .5 .8) (-8 -8 -8) (8 8 8) START_OFF ALARM
|
||||
* "health" if set, the light may be killed.
|
||||
*/
|
||||
|
||||
#define START_OFF 1
|
||||
|
||||
void
|
||||
rotating_light_alarm(edict_t *self)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->spawnflags & START_OFF)
|
||||
{
|
||||
self->think = NULL;
|
||||
self->nextthink = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
gi.sound(self, CHAN_NO_PHS_ADD + CHAN_VOICE,
|
||||
self->moveinfo.sound_start, 1,
|
||||
ATTN_STATIC, 0);
|
||||
self->nextthink = level.time + 1;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
rotating_light_killed(edict_t *self, edict_t *inflictor /* unused */,
|
||||
edict_t *attacker /* unused */, int damage /* unused */,
|
||||
vec3_t point /* unused */)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
gi.WriteByte(svc_temp_entity);
|
||||
gi.WriteByte(TE_WELDING_SPARKS);
|
||||
gi.WriteByte(30);
|
||||
gi.WritePosition(self->s.origin);
|
||||
gi.WriteDir(vec3_origin);
|
||||
gi.WriteByte(0xe0 + (rand() & 7));
|
||||
gi.multicast(self->s.origin, MULTICAST_PVS);
|
||||
|
||||
self->s.effects &= ~EF_SPINNINGLIGHTS;
|
||||
self->use = NULL;
|
||||
|
||||
self->think = G_FreeEdict;
|
||||
self->nextthink = level.time + 0.1;
|
||||
}
|
||||
|
||||
void
|
||||
rotating_light_use(edict_t *self, edict_t *other /* unused */,
|
||||
edict_t *activator /* unused */)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->spawnflags & START_OFF)
|
||||
{
|
||||
self->spawnflags &= ~START_OFF;
|
||||
self->s.effects |= EF_SPINNINGLIGHTS;
|
||||
|
||||
if (self->spawnflags & 2)
|
||||
{
|
||||
self->think = rotating_light_alarm;
|
||||
self->nextthink = level.time + 0.1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
self->spawnflags |= START_OFF;
|
||||
self->s.effects &= ~EF_SPINNINGLIGHTS;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SP_rotating_light(edict_t *self)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
self->movetype = MOVETYPE_STOP;
|
||||
self->solid = SOLID_BBOX;
|
||||
|
||||
self->s.modelindex = gi.modelindex("models/objects/light/tris.md2");
|
||||
|
||||
self->s.frame = 0;
|
||||
|
||||
self->use = rotating_light_use;
|
||||
|
||||
if (self->spawnflags & START_OFF)
|
||||
{
|
||||
self->s.effects &= ~EF_SPINNINGLIGHTS;
|
||||
}
|
||||
else
|
||||
{
|
||||
self->s.effects |= EF_SPINNINGLIGHTS;
|
||||
}
|
||||
|
||||
if (!self->speed)
|
||||
{
|
||||
self->speed = 32;
|
||||
}
|
||||
|
||||
if (!self->health)
|
||||
{
|
||||
self->health = 10;
|
||||
self->max_health = self->health;
|
||||
self->die = rotating_light_killed;
|
||||
self->takedamage = DAMAGE_YES;
|
||||
}
|
||||
else
|
||||
{
|
||||
self->max_health = self->health;
|
||||
self->die = rotating_light_killed;
|
||||
self->takedamage = DAMAGE_YES;
|
||||
}
|
||||
|
||||
if (self->spawnflags & 2)
|
||||
{
|
||||
self->moveinfo.sound_start = gi.soundindex("misc/alarm.wav");
|
||||
}
|
||||
|
||||
gi.linkentity(self);
|
||||
}
|
||||
|
||||
/*
|
||||
* QUAKED func_object_repair (1 .5 0) (-8 -8 -8) (8 8 8)
|
||||
* object to be repaired.
|
||||
* The default delay is 1 second
|
||||
* "delay" the delay in seconds for spark to occur
|
||||
*/
|
||||
void
|
||||
object_repair_fx(edict_t *ent)
|
||||
{
|
||||
if (!ent)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ent->nextthink = level.time + ent->delay;
|
||||
|
||||
if (ent->health <= 100)
|
||||
{
|
||||
ent->health++;
|
||||
}
|
||||
else
|
||||
{
|
||||
gi.WriteByte(svc_temp_entity);
|
||||
gi.WriteByte(TE_WELDING_SPARKS);
|
||||
gi.WriteByte(10);
|
||||
gi.WritePosition(ent->s.origin);
|
||||
gi.WriteDir(vec3_origin);
|
||||
gi.WriteByte(0xe0 + (rand() & 7));
|
||||
gi.multicast(ent->s.origin, MULTICAST_PVS);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
object_repair_dead(edict_t *ent)
|
||||
{
|
||||
if (!ent)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
G_UseTargets(ent, ent);
|
||||
ent->nextthink = level.time + 0.1;
|
||||
ent->think = object_repair_fx;
|
||||
}
|
||||
|
||||
void
|
||||
object_repair_sparks(edict_t *ent)
|
||||
{
|
||||
if (!ent)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (ent->health < 0)
|
||||
{
|
||||
ent->nextthink = level.time + 0.1;
|
||||
ent->think = object_repair_dead;
|
||||
return;
|
||||
}
|
||||
|
||||
ent->nextthink = level.time + ent->delay;
|
||||
|
||||
gi.WriteByte(svc_temp_entity);
|
||||
gi.WriteByte(TE_WELDING_SPARKS);
|
||||
gi.WriteByte(10);
|
||||
gi.WritePosition(ent->s.origin);
|
||||
gi.WriteDir(vec3_origin);
|
||||
gi.WriteByte(0xe0 + (rand() & 7));
|
||||
gi.multicast(ent->s.origin, MULTICAST_PVS);
|
||||
}
|
||||
|
||||
void
|
||||
SP_object_repair(edict_t *ent)
|
||||
{
|
||||
if (!ent)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ent->movetype = MOVETYPE_NONE;
|
||||
ent->solid = SOLID_BBOX;
|
||||
ent->classname = "object_repair";
|
||||
ent->think = object_repair_sparks;
|
||||
ent->nextthink = level.time + 1.0;
|
||||
ent->health = 100;
|
||||
|
||||
if (!ent->delay)
|
||||
{
|
||||
ent->delay = 1.0;
|
||||
}
|
||||
|
||||
gi.linkentity(ent);
|
||||
}
|
||||
|
|
|
@ -1,9 +1,23 @@
|
|||
/*
|
||||
* Copyright (C) 1997-2001 Id Software, Inc.
|
||||
* Copyright (c) ZeniMax Media Inc.
|
||||
* Licensed under the GNU General Public License 2.0.
|
||||
*/
|
||||
|
||||
/*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||
* 02111-1307, USA.
|
||||
*
|
||||
* =======================================================================
|
||||
*
|
||||
* Item handling and item definitions.
|
||||
|
@ -40,18 +54,25 @@ void Weapon_Prox(edict_t *ent);
|
|||
void Weapon_Tesla(edict_t *ent);
|
||||
void Weapon_ProxLauncher(edict_t *ent);
|
||||
|
||||
gitem_armor_t jacketarmor_info = {25, 50, .30, .00, ARMOR_JACKET};
|
||||
gitem_armor_t combatarmor_info = {50, 100, .60, .30, ARMOR_COMBAT};
|
||||
gitem_armor_t bodyarmor_info = {100, 200, .80, .60, ARMOR_BODY};
|
||||
void Weapon_Ionripper(edict_t *ent);
|
||||
void Weapon_Phalanx(edict_t *ent);
|
||||
void Weapon_Trap(edict_t *ent);
|
||||
|
||||
int jacket_armor_index;
|
||||
int combat_armor_index;
|
||||
int body_armor_index;
|
||||
static gitem_armor_t jacketarmor_info = {25, 50, .30, .00, ARMOR_JACKET};
|
||||
static gitem_armor_t combatarmor_info = {50, 100, .60, .30, ARMOR_COMBAT};
|
||||
static gitem_armor_t bodyarmor_info = {100, 200, .80, .60, ARMOR_BODY};
|
||||
|
||||
static int jacket_armor_index;
|
||||
static int combat_armor_index;
|
||||
static int body_armor_index;
|
||||
static int power_screen_index;
|
||||
static int power_shield_index;
|
||||
|
||||
void Use_Quad(edict_t *ent, gitem_t *item);
|
||||
void Use_QuadFire(edict_t *ent, gitem_t *item);
|
||||
|
||||
static int quad_drop_timeout_hack;
|
||||
static int quad_fire_drop_timeout_hack;
|
||||
|
||||
/* ====================================================================== */
|
||||
|
||||
|
@ -199,7 +220,7 @@ Pickup_Powerup(edict_t *ent, edict_t *other)
|
|||
{
|
||||
int quantity;
|
||||
|
||||
if (!ent || !other)
|
||||
if (!ent || !other)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -237,6 +258,7 @@ Drop_General(edict_t *ent, gitem_t *item)
|
|||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Drop_Item(ent, item);
|
||||
ent->client->pers.inventory[ITEM_INDEX(item)]--;
|
||||
ValidateSelectedItem(ent);
|
||||
|
@ -273,7 +295,7 @@ Pickup_Adrenaline(edict_t *ent, edict_t *other)
|
|||
qboolean
|
||||
Pickup_AncientHead(edict_t *ent, edict_t *other)
|
||||
{
|
||||
if (!ent || !other)
|
||||
if (!ent || !other)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -291,14 +313,14 @@ Pickup_AncientHead(edict_t *ent, edict_t *other)
|
|||
qboolean
|
||||
Pickup_Bandolier(edict_t *ent, edict_t *other)
|
||||
{
|
||||
if (!ent || !other)
|
||||
gitem_t *item;
|
||||
int index;
|
||||
|
||||
if (!ent || !other)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
gitem_t *item;
|
||||
int index;
|
||||
|
||||
if (other->client->pers.max_bullets < 250)
|
||||
{
|
||||
other->client->pers.max_bullets = 250;
|
||||
|
@ -319,6 +341,11 @@ Pickup_Bandolier(edict_t *ent, edict_t *other)
|
|||
other->client->pers.max_slugs = 75;
|
||||
}
|
||||
|
||||
if (other->client->pers.max_magslug < 75)
|
||||
{
|
||||
other->client->pers.max_magslug = 75;
|
||||
}
|
||||
|
||||
if (other->client->pers.max_flechettes < 250)
|
||||
{
|
||||
other->client->pers.max_flechettes = 250;
|
||||
|
@ -376,7 +403,7 @@ Pickup_Pack(edict_t *ent, edict_t *other)
|
|||
gitem_t *item;
|
||||
int index;
|
||||
|
||||
if (!ent || !other)
|
||||
if (!ent || !other)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -411,6 +438,11 @@ Pickup_Pack(edict_t *ent, edict_t *other)
|
|||
other->client->pers.max_slugs = 100;
|
||||
}
|
||||
|
||||
if (other->client->pers.max_magslug < 100)
|
||||
{
|
||||
other->client->pers.max_magslug = 100;
|
||||
}
|
||||
|
||||
if (other->client->pers.max_flechettes < 200)
|
||||
{
|
||||
other->client->pers.max_flechettes = 200;
|
||||
|
@ -514,6 +546,21 @@ Pickup_Pack(edict_t *ent, edict_t *other)
|
|||
}
|
||||
}
|
||||
|
||||
item = FindItem("Mag Slug");
|
||||
|
||||
if (item)
|
||||
{
|
||||
index = ITEM_INDEX(item);
|
||||
other->client->pers.inventory[index] += item->quantity;
|
||||
|
||||
if (other->client->pers.inventory[index] >
|
||||
other->client->pers.max_magslug)
|
||||
{
|
||||
other->client->pers.inventory[index] =
|
||||
other->client->pers.max_magslug;
|
||||
}
|
||||
}
|
||||
|
||||
item = FindItem("Flechettes");
|
||||
|
||||
if (item)
|
||||
|
@ -552,6 +599,8 @@ Pickup_Pack(edict_t *ent, edict_t *other)
|
|||
return true;
|
||||
}
|
||||
|
||||
/* ====================================================================== */
|
||||
|
||||
qboolean
|
||||
Pickup_Nuke(edict_t *ent, edict_t *other)
|
||||
{
|
||||
|
@ -872,8 +921,44 @@ Use_Quad(edict_t *ent, gitem_t *item)
|
|||
ent->client->quad_framenum = level.framenum + timeout;
|
||||
}
|
||||
|
||||
gi.sound(ent, CHAN_ITEM, gi.soundindex("items/damage.wav"), 1, ATTN_NORM,
|
||||
0);
|
||||
gi.sound(ent, CHAN_ITEM, gi.soundindex("items/damage.wav"), 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
/* ===================================================================== */
|
||||
|
||||
void
|
||||
Use_QuadFire(edict_t *ent, gitem_t *item)
|
||||
{
|
||||
int timeout;
|
||||
|
||||
if (!ent || !item)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ent->client->pers.inventory[ITEM_INDEX(item)]--;
|
||||
ValidateSelectedItem(ent);
|
||||
|
||||
if (quad_fire_drop_timeout_hack)
|
||||
{
|
||||
timeout = quad_fire_drop_timeout_hack;
|
||||
quad_fire_drop_timeout_hack = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
timeout = 300;
|
||||
}
|
||||
|
||||
if (ent->client->quadfire_framenum > level.framenum)
|
||||
{
|
||||
ent->client->quadfire_framenum += timeout;
|
||||
}
|
||||
else
|
||||
{
|
||||
ent->client->quadfire_framenum = level.framenum + timeout;
|
||||
}
|
||||
|
||||
gi.sound(ent, CHAN_ITEM, gi.soundindex("items/quadfire1.wav"), 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
/* ====================================================================== */
|
||||
|
@ -1021,7 +1106,6 @@ Add_Ammo(edict_t *ent, gitem_t *item, int count)
|
|||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (item->tag == AMMO_BULLETS)
|
||||
{
|
||||
max = ent->client->pers.max_bullets;
|
||||
|
@ -1046,6 +1130,14 @@ Add_Ammo(edict_t *ent, gitem_t *item, int count)
|
|||
{
|
||||
max = ent->client->pers.max_slugs;
|
||||
}
|
||||
else if (item->tag == AMMO_MAGSLUG)
|
||||
{
|
||||
max = ent->client->pers.max_magslug;
|
||||
}
|
||||
else if (item->tag == AMMO_TRAP)
|
||||
{
|
||||
max = ent->client->pers.max_trap;
|
||||
}
|
||||
else if (item->tag == AMMO_FLECHETTES)
|
||||
{
|
||||
max = ent->client->pers.max_flechettes;
|
||||
|
@ -1130,7 +1222,8 @@ Pickup_Ammo(edict_t *ent, edict_t *other)
|
|||
}
|
||||
}
|
||||
|
||||
if (!(ent->spawnflags & (DROPPED_ITEM | DROPPED_PLAYER_ITEM)) && (deathmatch->value))
|
||||
if (!(ent->spawnflags & (DROPPED_ITEM | DROPPED_PLAYER_ITEM)) &&
|
||||
(deathmatch->value))
|
||||
{
|
||||
SetRespawn(ent, 30);
|
||||
}
|
||||
|
@ -1333,7 +1426,7 @@ Pickup_Armor(edict_t *ent, edict_t *other)
|
|||
{
|
||||
oldinfo = &combatarmor_info;
|
||||
}
|
||||
else
|
||||
else /* (old_armor_index == body_armor_index) */
|
||||
{
|
||||
oldinfo = &bodyarmor_info;
|
||||
}
|
||||
|
@ -1342,7 +1435,8 @@ Pickup_Armor(edict_t *ent, edict_t *other)
|
|||
{
|
||||
/* calc new armor values */
|
||||
salvage = oldinfo->normal_protection / newinfo->normal_protection;
|
||||
salvagecount = salvage * other->client->pers.inventory[old_armor_index];
|
||||
salvagecount = salvage *
|
||||
other->client->pers.inventory[old_armor_index];
|
||||
newcount = newinfo->base_count + salvagecount;
|
||||
|
||||
if (newcount > newinfo->max_count)
|
||||
|
@ -1361,7 +1455,8 @@ Pickup_Armor(edict_t *ent, edict_t *other)
|
|||
/* calc new armor values */
|
||||
salvage = newinfo->normal_protection / oldinfo->normal_protection;
|
||||
salvagecount = salvage * newinfo->base_count;
|
||||
newcount = other->client->pers.inventory[old_armor_index] + salvagecount;
|
||||
newcount = other->client->pers.inventory[old_armor_index] +
|
||||
salvagecount;
|
||||
|
||||
if (newcount > oldinfo->max_count)
|
||||
{
|
||||
|
@ -1425,10 +1520,16 @@ Use_PowerArmor(edict_t *ent, gitem_t *item)
|
|||
{
|
||||
int index;
|
||||
|
||||
if (!ent || !item)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (ent->flags & FL_POWER_ARMOR)
|
||||
{
|
||||
ent->flags &= ~FL_POWER_ARMOR;
|
||||
gi.sound(ent, CHAN_AUTO, gi.soundindex("misc/power2.wav"), 1, ATTN_NORM, 0);
|
||||
gi.sound(ent, CHAN_AUTO, gi.soundindex(
|
||||
"misc/power2.wav"), 1, ATTN_NORM, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1441,7 +1542,8 @@ Use_PowerArmor(edict_t *ent, gitem_t *item)
|
|||
}
|
||||
|
||||
ent->flags |= FL_POWER_ARMOR;
|
||||
gi.sound(ent, CHAN_AUTO, gi.soundindex("misc/power1.wav"), 1, ATTN_NORM, 0);
|
||||
gi.sound(ent, CHAN_AUTO, gi.soundindex(
|
||||
"misc/power1.wav"), 1, ATTN_NORM, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1528,39 +1630,47 @@ Touch_Item(edict_t *ent, edict_t *other, cplane_t *plane /* unused */, csurface_
|
|||
other->client->bonus_alpha = 0.25;
|
||||
|
||||
/* show icon and name on status bar */
|
||||
other->client->ps.stats[STAT_PICKUP_ICON] = gi.imageindex(ent->item->icon);
|
||||
other->client->ps.stats[STAT_PICKUP_STRING] = CS_ITEMS + ITEM_INDEX(ent->item);
|
||||
other->client->ps.stats[STAT_PICKUP_ICON] =
|
||||
gi.imageindex(ent->item->icon);
|
||||
other->client->ps.stats[STAT_PICKUP_STRING] =
|
||||
CS_ITEMS + ITEM_INDEX(ent->item);
|
||||
other->client->pickup_msg_time = level.time + 3.0;
|
||||
|
||||
/* change selected item */
|
||||
if (ent->item->use)
|
||||
{
|
||||
other->client->pers.selected_item =
|
||||
other->client->ps.stats[STAT_SELECTED_ITEM] = ITEM_INDEX(ent->item);
|
||||
other->client->ps.stats[STAT_SELECTED_ITEM] =
|
||||
ITEM_INDEX(ent->item);
|
||||
}
|
||||
|
||||
if (ent->item->pickup == Pickup_Health)
|
||||
{
|
||||
if (ent->count == 2)
|
||||
{
|
||||
gi.sound(other, CHAN_ITEM, gi.soundindex("items/s_health.wav"), 1, ATTN_NORM, 0);
|
||||
gi.sound(other, CHAN_ITEM, gi.soundindex(
|
||||
"items/s_health.wav"), 1, ATTN_NORM, 0);
|
||||
}
|
||||
else if (ent->count == 10)
|
||||
{
|
||||
gi.sound(other, CHAN_ITEM, gi.soundindex("items/n_health.wav"), 1, ATTN_NORM, 0);
|
||||
gi.sound(other, CHAN_ITEM, gi.soundindex(
|
||||
"items/n_health.wav"), 1, ATTN_NORM, 0);
|
||||
}
|
||||
else if (ent->count == 25)
|
||||
{
|
||||
gi.sound(other, CHAN_ITEM, gi.soundindex("items/l_health.wav"), 1, ATTN_NORM, 0);
|
||||
gi.sound(other, CHAN_ITEM, gi.soundindex(
|
||||
"items/l_health.wav"), 1, ATTN_NORM, 0);
|
||||
}
|
||||
else /* (ent->count == 100) */
|
||||
{
|
||||
gi.sound(other, CHAN_ITEM, gi.soundindex("items/m_health.wav"), 1, ATTN_NORM, 0);
|
||||
gi.sound(other, CHAN_ITEM, gi.soundindex(
|
||||
"items/m_health.wav"), 1, ATTN_NORM, 0);
|
||||
}
|
||||
}
|
||||
else if (ent->item->pickup_sound)
|
||||
{
|
||||
gi.sound(other, CHAN_ITEM, gi.soundindex(ent->item->pickup_sound), 1, ATTN_NORM, 0);
|
||||
gi.sound(other, CHAN_ITEM, gi.soundindex(
|
||||
ent->item->pickup_sound), 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
/* activate item instantly if appropriate */
|
||||
|
@ -1602,7 +1712,8 @@ Touch_Item(edict_t *ent, edict_t *other, cplane_t *plane /* unused */, csurface_
|
|||
return;
|
||||
}
|
||||
|
||||
if (!((coop->value) && (ent->item->flags & IT_STAY_COOP)) ||
|
||||
if (!((coop->value) &&
|
||||
(ent->item->flags & IT_STAY_COOP)) ||
|
||||
(ent->spawnflags & (DROPPED_ITEM | DROPPED_PLAYER_ITEM)))
|
||||
{
|
||||
if (ent->flags & FL_RESPAWN)
|
||||
|
@ -1631,6 +1742,10 @@ drop_temp_touch(edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
|
|||
return;
|
||||
}
|
||||
|
||||
/* plane and surf are unused in Touch_Item
|
||||
but since the function is part of the
|
||||
game <-> client interface dropping
|
||||
them is too much pain. */
|
||||
Touch_Item(ent, other, plane, surf);
|
||||
}
|
||||
|
||||
|
@ -1684,7 +1799,8 @@ Drop_Item(edict_t *ent, gitem_t *item)
|
|||
|
||||
AngleVectors(ent->client->v_angle, forward, right, NULL);
|
||||
VectorSet(offset, 24, 0, -16);
|
||||
G_ProjectSource(ent->s.origin, offset, forward, right, dropped->s.origin);
|
||||
G_ProjectSource(ent->s.origin, offset, forward, right,
|
||||
dropped->s.origin);
|
||||
trace = gi.trace(ent->s.origin, dropped->mins, dropped->maxs,
|
||||
dropped->s.origin, ent, CONTENTS_SOLID);
|
||||
VectorCopy(trace.endpos, dropped->s.origin);
|
||||
|
@ -1736,15 +1852,15 @@ Use_Item(edict_t *ent, edict_t *other /* unused */, edict_t *activator /* unused
|
|||
void
|
||||
droptofloor(edict_t *ent)
|
||||
{
|
||||
trace_t tr;
|
||||
vec3_t dest;
|
||||
float *v;
|
||||
|
||||
if (!ent)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
trace_t tr;
|
||||
vec3_t dest;
|
||||
float *v;
|
||||
|
||||
v = tv(-15, -15, -15);
|
||||
VectorCopy(v, ent->mins);
|
||||
v = tv(15, 15, 15);
|
||||
|
@ -1770,10 +1886,19 @@ droptofloor(edict_t *ent)
|
|||
|
||||
if (tr.startsolid)
|
||||
{
|
||||
gi.dprintf("droptofloor: %s startsolid at %s\n", ent->classname,
|
||||
vtos(ent->s.origin));
|
||||
G_FreeEdict(ent);
|
||||
return;
|
||||
if (strcmp(ent->classname, "foodcube") == 0)
|
||||
{
|
||||
VectorCopy(ent->s.origin, tr.endpos);
|
||||
ent->velocity[2] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
gi.dprintf("droptofloor: %s startsolid at %s\n",
|
||||
ent->classname,
|
||||
vtos(ent->s.origin));
|
||||
G_FreeEdict(ent);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
VectorCopy(tr.endpos, ent->s.origin);
|
||||
|
@ -2127,12 +2252,15 @@ SpawnItem(edict_t *ent, gitem_t *item)
|
|||
|
||||
/* ====================================================================== */
|
||||
|
||||
gitem_t itemlist[] = {
|
||||
static const gitem_t gameitemlist[] = {
|
||||
{
|
||||
NULL
|
||||
}, /* leave index 0 alone */
|
||||
|
||||
/* QUAKED item_armor_body (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN */
|
||||
|
||||
/*
|
||||
* QUAKED item_armor_body (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN
|
||||
*/
|
||||
{
|
||||
"item_armor_body",
|
||||
Pickup_Armor,
|
||||
|
@ -3497,6 +3625,8 @@ gitem_t itemlist[] = {
|
|||
{NULL}
|
||||
};
|
||||
|
||||
gitem_t itemlist[MAX_ITEMS];
|
||||
|
||||
/*
|
||||
* QUAKED item_health (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN
|
||||
*/
|
||||
|
@ -3616,7 +3746,9 @@ SP_item_foodcube(edict_t *self)
|
|||
void
|
||||
InitItems(void)
|
||||
{
|
||||
game.num_items = sizeof(itemlist) / sizeof(itemlist[0]) - 1;
|
||||
memset(itemlist, 0, sizeof(itemlist));
|
||||
memcpy(itemlist, gameitemlist, sizeof(gameitemlist));
|
||||
game.num_items = sizeof(gameitemlist) / sizeof(gameitemlist[0]) - 1;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
2733
src/rogue/g_misc.c
2733
src/rogue/g_misc.c
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
1842
src/rogue/g_spawn.c
1842
src/rogue/g_spawn.c
File diff suppressed because it is too large
Load Diff
|
@ -1,354 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 1997-2001 Id Software, Inc.
|
||||
* Copyright (c) ZeniMax Media Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||
* 02111-1307, USA.
|
||||
*
|
||||
* =======================================================================
|
||||
*
|
||||
* Game side of server CMDs. At this time only the ipfilter.
|
||||
*
|
||||
* =======================================================================
|
||||
*/
|
||||
|
||||
#include "header/local.h"
|
||||
|
||||
#define MAX_IPFILTERS 1024
|
||||
|
||||
/*
|
||||
* ==============================================================================
|
||||
*
|
||||
* 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;
|
||||
|
||||
ipfilter_t ipfilters[MAX_IPFILTERS];
|
||||
int numipfilters;
|
||||
|
||||
void
|
||||
Svcmd_Test_f(void)
|
||||
{
|
||||
gi.cprintf(NULL, PRINT_HIGH, "Svcmd_Test_f()\n");
|
||||
}
|
||||
|
||||
qboolean
|
||||
StringToFilter(char *s, ipfilter_t *f)
|
||||
{
|
||||
char num[128];
|
||||
int i, j;
|
||||
byte b[4];
|
||||
byte m[4];
|
||||
|
||||
if (!s || !f)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
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++;
|
||||
}
|
||||
|
||||
/* PVS NOTE: maybe use memcpy here? */
|
||||
f->mask = *(unsigned *)m;
|
||||
f->compare = *(unsigned *)b;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
qboolean
|
||||
SV_FilterPacket(char *from)
|
||||
{
|
||||
int i;
|
||||
unsigned in;
|
||||
byte m[4];
|
||||
char *p;
|
||||
|
||||
if (!from)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
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++;
|
||||
}
|
||||
|
||||
/* PVS NOTE: maybe use memcpy here? */
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
void
|
||||
SVCmd_ListIP_f(void)
|
||||
{
|
||||
int i;
|
||||
byte b[4];
|
||||
|
||||
gi.cprintf(NULL, PRINT_HIGH, "Filter list:\n");
|
||||
|
||||
for (i = 0; i < numipfilters; i++)
|
||||
{
|
||||
/* PVS NOTE: maybe use memcpy here? */
|
||||
*(unsigned *)b = ipfilters[i].compare;
|
||||
gi.cprintf(NULL, PRINT_HIGH, "%3i.%3i.%3i.%3i\n", b[0], b[1], b[2], b[3]);
|
||||
}
|
||||
}
|
||||
|
||||
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++)
|
||||
{
|
||||
/* PVS NOTE: maybe use memcpy here? */
|
||||
*(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 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);
|
||||
}
|
||||
}
|
1220
src/rogue/g_target.c
1220
src/rogue/g_target.c
File diff suppressed because it is too large
Load Diff
|
@ -1,931 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 1997-2001 Id Software, Inc.
|
||||
* Copyright (c) ZeniMax Media Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||
* 02111-1307, USA.
|
||||
*
|
||||
* =======================================================================
|
||||
*
|
||||
* Trigger.
|
||||
*
|
||||
* =======================================================================
|
||||
*/
|
||||
|
||||
#include "header/local.h"
|
||||
|
||||
#define TRIGGER_MONSTER 0x01
|
||||
#define TRIGGER_NOT_PLAYER 0x02
|
||||
#define TRIGGER_TRIGGERED 0x04
|
||||
#define TRIGGER_TOGGLE 0x08
|
||||
|
||||
#define PUSH_ONCE 0x01
|
||||
#define PUSH_START_OFF 0x02
|
||||
#define PUSH_SILENT 0x04
|
||||
|
||||
static int windsound;
|
||||
|
||||
void
|
||||
InitTrigger(edict_t *self)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!VectorCompare(self->s.angles, vec3_origin))
|
||||
{
|
||||
G_SetMovedir(self->s.angles, self->movedir);
|
||||
}
|
||||
|
||||
self->solid = SOLID_TRIGGER;
|
||||
self->movetype = MOVETYPE_NONE;
|
||||
gi.setmodel(self, self->model);
|
||||
self->svflags = SVF_NOCLIENT;
|
||||
}
|
||||
|
||||
/*
|
||||
* the wait time has passed, so set
|
||||
* back up for another activation
|
||||
*/
|
||||
void
|
||||
multi_wait(edict_t *ent)
|
||||
{
|
||||
if (!ent)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ent->nextthink = 0;
|
||||
}
|
||||
|
||||
void
|
||||
multi_trigger(edict_t *ent)
|
||||
{
|
||||
if (!ent)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (ent->nextthink)
|
||||
{
|
||||
return; /* already been triggered */
|
||||
}
|
||||
|
||||
G_UseTargets(ent, ent->activator);
|
||||
|
||||
if (ent->wait > 0)
|
||||
{
|
||||
ent->think = multi_wait;
|
||||
ent->nextthink = level.time + ent->wait;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* we can't just remove (self) here, because
|
||||
this is a touch function called while looping
|
||||
through area links... */
|
||||
ent->touch = NULL;
|
||||
ent->nextthink = level.time + FRAMETIME;
|
||||
ent->think = G_FreeEdict;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Use_Multi(edict_t *ent, edict_t *other /* unused */, edict_t *activator)
|
||||
{
|
||||
if (!ent || !activator)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (ent->spawnflags & TRIGGER_TOGGLE)
|
||||
{
|
||||
if (ent->solid == SOLID_TRIGGER)
|
||||
{
|
||||
ent->solid = SOLID_NOT;
|
||||
}
|
||||
else
|
||||
{
|
||||
ent->solid = SOLID_TRIGGER;
|
||||
}
|
||||
|
||||
gi.linkentity(ent);
|
||||
}
|
||||
else
|
||||
{
|
||||
ent->activator = activator;
|
||||
multi_trigger(ent);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Touch_Multi(edict_t *self, edict_t *other, cplane_t *plane /* unused */, csurface_t *surf /* unused */)
|
||||
{
|
||||
if (!self || !other)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (other->client)
|
||||
{
|
||||
if (self->spawnflags & 2)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (other->svflags & SVF_MONSTER)
|
||||
{
|
||||
if (!(self->spawnflags & 1))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!VectorCompare(self->movedir, vec3_origin))
|
||||
{
|
||||
vec3_t forward;
|
||||
|
||||
AngleVectors(other->s.angles, forward, NULL, NULL);
|
||||
|
||||
if (_DotProduct(forward, self->movedir) < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
self->activator = other;
|
||||
multi_trigger(self);
|
||||
}
|
||||
|
||||
/*
|
||||
* QUAKED trigger_multiple (.5 .5 .5) ? MONSTER NOT_PLAYER TRIGGERED TOGGLE
|
||||
* Variable sized repeatable trigger. Must be targeted at one or more entities.
|
||||
* If "delay" is set, the trigger waits some time after activating before firing.
|
||||
* "wait" : Seconds between triggerings. (.2 default)
|
||||
*
|
||||
* TOGGLE - using this trigger will activate/deactivate it. trigger will begin inactive.
|
||||
*
|
||||
* sounds
|
||||
* 1) secret
|
||||
* 2) beep beep
|
||||
* 3) large switch
|
||||
* 4)
|
||||
* set "message" to text string
|
||||
*/
|
||||
void
|
||||
trigger_enable(edict_t *self, edict_t *other /* unused */, edict_t *activator /* unused */)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
self->solid = SOLID_TRIGGER;
|
||||
self->use = Use_Multi;
|
||||
gi.linkentity(self);
|
||||
}
|
||||
|
||||
void
|
||||
SP_trigger_multiple(edict_t *ent)
|
||||
{
|
||||
if (!ent)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (ent->sounds == 1)
|
||||
{
|
||||
ent->noise_index = gi.soundindex("misc/secret.wav");
|
||||
}
|
||||
else if (ent->sounds == 2)
|
||||
{
|
||||
ent->noise_index = gi.soundindex("misc/talk.wav");
|
||||
}
|
||||
else if (ent->sounds == 3)
|
||||
{
|
||||
ent->noise_index = gi.soundindex("misc/trigger1.wav");
|
||||
}
|
||||
|
||||
if (!ent->wait)
|
||||
{
|
||||
ent->wait = 0.2;
|
||||
}
|
||||
|
||||
ent->touch = Touch_Multi;
|
||||
ent->movetype = MOVETYPE_NONE;
|
||||
ent->svflags |= SVF_NOCLIENT;
|
||||
|
||||
if (ent->spawnflags & (TRIGGER_TRIGGERED | TRIGGER_TOGGLE))
|
||||
{
|
||||
ent->solid = SOLID_NOT;
|
||||
ent->use = trigger_enable;
|
||||
}
|
||||
else
|
||||
{
|
||||
ent->solid = SOLID_TRIGGER;
|
||||
ent->use = Use_Multi;
|
||||
}
|
||||
|
||||
if (!VectorCompare(ent->s.angles, vec3_origin))
|
||||
{
|
||||
G_SetMovedir(ent->s.angles, ent->movedir);
|
||||
}
|
||||
|
||||
gi.setmodel(ent, ent->model);
|
||||
gi.linkentity(ent);
|
||||
}
|
||||
|
||||
/*
|
||||
* QUAKED trigger_once (.5 .5 .5) ? x x TRIGGERED
|
||||
*
|
||||
* Triggers once, then removes itself.
|
||||
* You must set the key "target" to the name of another object in the level that has a matching "targetname".
|
||||
*
|
||||
* If TRIGGERED, this trigger must be triggered before it is live.
|
||||
*
|
||||
* sounds
|
||||
* 1) secret
|
||||
* 2) beep beep
|
||||
* 3) large switch
|
||||
* 4)
|
||||
*
|
||||
* "message" string to be displayed when triggered
|
||||
*/
|
||||
|
||||
void
|
||||
SP_trigger_once(edict_t *ent)
|
||||
{
|
||||
if (!ent)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* make old maps work because I messed up on flag assignments here */
|
||||
if (ent->spawnflags & 1)
|
||||
{
|
||||
vec3_t v;
|
||||
|
||||
VectorMA(ent->mins, 0.5, ent->size, v);
|
||||
ent->spawnflags &= ~1;
|
||||
ent->spawnflags |= 4;
|
||||
gi.dprintf("fixed TRIGGERED flag on %s at %s\n", ent->classname, vtos(v));
|
||||
}
|
||||
|
||||
ent->wait = -1;
|
||||
SP_trigger_multiple(ent);
|
||||
}
|
||||
|
||||
/*
|
||||
* QUAKED trigger_relay (.5 .5 .5) (-8 -8 -8) (8 8 8)
|
||||
*
|
||||
* This fixed size trigger cannot be touched, it can only be fired by other events.
|
||||
*/
|
||||
void
|
||||
trigger_relay_use(edict_t *self, edict_t *other /* unused */, edict_t *activator)
|
||||
{
|
||||
if (!self || !activator)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
G_UseTargets(self, activator);
|
||||
}
|
||||
|
||||
void
|
||||
SP_trigger_relay(edict_t *self)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
self->use = trigger_relay_use;
|
||||
}
|
||||
|
||||
/*
|
||||
* QUAKED trigger_key (.5 .5 .5) (-8 -8 -8) (8 8 8)
|
||||
*
|
||||
* A relay trigger that only fires it's targets if player has the proper key.
|
||||
* Use "item" to specify the required key, for example "key_data_cd"
|
||||
*/
|
||||
void
|
||||
trigger_key_use(edict_t *self, edict_t *other /* unused */, edict_t *activator)
|
||||
{
|
||||
int index;
|
||||
|
||||
if (!self|| !activator)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!self->item)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!activator->client)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
index = ITEM_INDEX(self->item);
|
||||
|
||||
if (!activator->client->pers.inventory[index])
|
||||
{
|
||||
if (level.time < self->touch_debounce_time)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
self->touch_debounce_time = level.time + 5.0;
|
||||
gi.centerprintf(activator, "You need the %s", self->item->pickup_name);
|
||||
gi.sound(activator, CHAN_AUTO, gi.soundindex("misc/keytry.wav"), 1, ATTN_NORM, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
gi.sound(activator, CHAN_AUTO, gi.soundindex("misc/keyuse.wav"), 1, ATTN_NORM, 0);
|
||||
|
||||
if (coop->value)
|
||||
{
|
||||
int player;
|
||||
edict_t *ent;
|
||||
|
||||
if (strcmp(self->item->classname, "key_power_cube") == 0)
|
||||
{
|
||||
int cube;
|
||||
|
||||
for (cube = 0; cube < 8; cube++)
|
||||
{
|
||||
if (activator->client->pers.power_cubes & (1 << cube))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (player = 1; player <= game.maxclients; player++)
|
||||
{
|
||||
ent = &g_edicts[player];
|
||||
|
||||
if (!ent->inuse)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ent->client)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ent->client->pers.power_cubes & (1 << cube))
|
||||
{
|
||||
ent->client->pers.inventory[index]--;
|
||||
ent->client->pers.power_cubes &= ~(1 << cube);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (player = 1; player <= game.maxclients; player++)
|
||||
{
|
||||
ent = &g_edicts[player];
|
||||
|
||||
if (!ent->inuse)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ent->client)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ent->client->pers.inventory[index] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
activator->client->pers.inventory[index]--;
|
||||
}
|
||||
|
||||
G_UseTargets(self, activator);
|
||||
|
||||
self->use = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
SP_trigger_key(edict_t *self)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!st.item)
|
||||
{
|
||||
gi.dprintf("no key item for trigger_key at %s\n", vtos(self->s.origin));
|
||||
return;
|
||||
}
|
||||
|
||||
self->item = FindItemByClassname(st.item);
|
||||
|
||||
if (!self->item)
|
||||
{
|
||||
gi.dprintf("item %s not found for trigger_key at %s\n", st.item,
|
||||
vtos(self->s.origin));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!self->target)
|
||||
{
|
||||
gi.dprintf("%s at %s has no target\n", self->classname,
|
||||
vtos(self->s.origin));
|
||||
return;
|
||||
}
|
||||
|
||||
gi.soundindex("misc/keytry.wav");
|
||||
gi.soundindex("misc/keyuse.wav");
|
||||
|
||||
self->use = trigger_key_use;
|
||||
}
|
||||
|
||||
/*
|
||||
* QUAKED trigger_counter (.5 .5 .5) ? nomessage
|
||||
*
|
||||
* Acts as an intermediary for an action that takes multiple inputs.
|
||||
*
|
||||
* If nomessage is not set, t will print "1 more.. " etc when triggered
|
||||
* and "sequence complete" when finished.
|
||||
*
|
||||
* After the counter has been triggered "count" times (default 2),
|
||||
* it will fire all of it's targets and remove itself.
|
||||
*/
|
||||
|
||||
void
|
||||
trigger_counter_use(edict_t *self, edict_t *other /* unused */, edict_t *activator)
|
||||
{
|
||||
if (!self || !activator)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
self->count--;
|
||||
|
||||
if (self->count)
|
||||
{
|
||||
if (!(self->spawnflags & 1))
|
||||
{
|
||||
gi.centerprintf(activator, "%i more to go...", self->count);
|
||||
gi.sound(activator, CHAN_AUTO, gi.soundindex("misc/talk1.wav"), 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(self->spawnflags & 1))
|
||||
{
|
||||
gi.centerprintf(activator, "Sequence completed!");
|
||||
gi.sound(activator, CHAN_AUTO, gi.soundindex("misc/talk1.wav"), 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
self->activator = activator;
|
||||
multi_trigger(self);
|
||||
}
|
||||
|
||||
void
|
||||
SP_trigger_counter(edict_t *self)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
self->wait = -1;
|
||||
|
||||
if (!self->count)
|
||||
{
|
||||
self->count = 2;
|
||||
}
|
||||
|
||||
self->use = trigger_counter_use;
|
||||
}
|
||||
|
||||
/*
|
||||
* QUAKED trigger_always (.5 .5 .5) (-8 -8 -8) (8 8 8)
|
||||
*
|
||||
* This trigger will always fire. It is activated by the world.
|
||||
*/
|
||||
void
|
||||
SP_trigger_always(edict_t *ent)
|
||||
{
|
||||
if (!ent)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* we must have some delay to make sure our use targets are present */
|
||||
if (ent->delay < 0.2)
|
||||
{
|
||||
ent->delay = 0.2;
|
||||
}
|
||||
|
||||
G_UseTargets(ent, ent);
|
||||
}
|
||||
|
||||
void
|
||||
trigger_push_touch(edict_t *self, edict_t *other, cplane_t *plane /* unused */, csurface_t *surf /* unused */)
|
||||
{
|
||||
if (!self || !other)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcmp(other->classname, "grenade") == 0)
|
||||
{
|
||||
VectorScale(self->movedir, self->speed * 10, other->velocity);
|
||||
}
|
||||
else if (other->health > 0)
|
||||
{
|
||||
VectorScale(self->movedir, self->speed * 10, other->velocity);
|
||||
|
||||
if (other->client)
|
||||
{
|
||||
/* don't take falling damage immediately from this */
|
||||
VectorCopy(other->velocity, other->client->oldvelocity);
|
||||
|
||||
if (!(self->spawnflags & PUSH_SILENT) &&
|
||||
(other->fly_sound_debounce_time < level.time))
|
||||
{
|
||||
other->fly_sound_debounce_time = level.time + 1.5;
|
||||
gi.sound(other, CHAN_AUTO, windsound, 1, ATTN_NORM, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (self->spawnflags & PUSH_ONCE)
|
||||
{
|
||||
G_FreeEdict(self);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
trigger_push_use(edict_t *self, edict_t *other /* unused */, edict_t *activator /* unused */)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->solid == SOLID_NOT)
|
||||
{
|
||||
self->solid = SOLID_TRIGGER;
|
||||
}
|
||||
else
|
||||
{
|
||||
self->solid = SOLID_NOT;
|
||||
}
|
||||
|
||||
gi.linkentity(self);
|
||||
}
|
||||
|
||||
/*
|
||||
* QUAKED trigger_push (.5 .5 .5) ? PUSH_ONCE START_OFF SILENT
|
||||
* Pushes the player
|
||||
* "speed" defaults to 1000
|
||||
*
|
||||
* If targeted, it will toggle on and off when used.
|
||||
*
|
||||
* START_OFF - toggled trigger_push begins in off setting
|
||||
* SILENT - doesn't make wind noise
|
||||
*/
|
||||
void
|
||||
SP_trigger_push(edict_t *self)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
InitTrigger(self);
|
||||
windsound = gi.soundindex("misc/windfly.wav");
|
||||
self->touch = trigger_push_touch;
|
||||
|
||||
if (!self->speed)
|
||||
{
|
||||
self->speed = 1000;
|
||||
}
|
||||
|
||||
if (self->targetname) /* toggleable */
|
||||
{
|
||||
self->use = trigger_push_use;
|
||||
|
||||
if (self->spawnflags & PUSH_START_OFF)
|
||||
{
|
||||
self->solid = SOLID_NOT;
|
||||
}
|
||||
}
|
||||
else if (self->spawnflags & PUSH_START_OFF)
|
||||
{
|
||||
gi.dprintf("trigger_push is START_OFF but not targeted.\n");
|
||||
self->svflags = 0;
|
||||
self->touch = NULL;
|
||||
self->solid = SOLID_BSP;
|
||||
self->movetype = MOVETYPE_PUSH;
|
||||
}
|
||||
|
||||
gi.linkentity(self);
|
||||
}
|
||||
|
||||
/*
|
||||
* QUAKED trigger_hurt (.5 .5 .5) ? START_OFF TOGGLE SILENT NO_PROTECTION SLOW
|
||||
*
|
||||
* Any entity that touches this will be hurt.
|
||||
*
|
||||
* It does dmg points of damage each server frame
|
||||
*
|
||||
* SILENT supresses playing the sound
|
||||
* SLOW changes the damage rate to once per second
|
||||
* NO_PROTECTION *nothing* stops the damage
|
||||
*
|
||||
* "dmg" default 5 (whole numbers only)
|
||||
*
|
||||
*/
|
||||
void
|
||||
hurt_use(edict_t *self, edict_t *other /* unused */, edict_t *activator /* unused */)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->solid == SOLID_NOT)
|
||||
{
|
||||
self->solid = SOLID_TRIGGER;
|
||||
}
|
||||
else
|
||||
{
|
||||
self->solid = SOLID_NOT;
|
||||
}
|
||||
|
||||
gi.linkentity(self);
|
||||
|
||||
if (!(self->spawnflags & 2))
|
||||
{
|
||||
self->use = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
hurt_touch(edict_t *self, edict_t *other, cplane_t *plane /* unused */, csurface_t *surf /* unused */)
|
||||
{
|
||||
int dflags;
|
||||
|
||||
if (!self || !other)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!other->takedamage)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->timestamp > level.time)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->spawnflags & 16)
|
||||
{
|
||||
self->timestamp = level.time + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
self->timestamp = level.time + FRAMETIME;
|
||||
}
|
||||
|
||||
if (!(self->spawnflags & 4))
|
||||
{
|
||||
if ((level.framenum % 10) == 0)
|
||||
{
|
||||
gi.sound(other, CHAN_AUTO, self->noise_index, 1, ATTN_NORM, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (self->spawnflags & 8)
|
||||
{
|
||||
dflags = DAMAGE_NO_PROTECTION;
|
||||
}
|
||||
else
|
||||
{
|
||||
dflags = 0;
|
||||
}
|
||||
|
||||
T_Damage(other, self, self, vec3_origin, other->s.origin, vec3_origin,
|
||||
self->dmg, self->dmg, dflags, MOD_TRIGGER_HURT);
|
||||
}
|
||||
|
||||
void
|
||||
SP_trigger_hurt(edict_t *self)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
InitTrigger(self);
|
||||
|
||||
self->noise_index = gi.soundindex("world/electro.wav");
|
||||
self->touch = hurt_touch;
|
||||
|
||||
if (!self->dmg)
|
||||
{
|
||||
self->dmg = 5;
|
||||
}
|
||||
|
||||
if (self->spawnflags & 1)
|
||||
{
|
||||
self->solid = SOLID_NOT;
|
||||
}
|
||||
else
|
||||
{
|
||||
self->solid = SOLID_TRIGGER;
|
||||
}
|
||||
|
||||
if (self->spawnflags & 2)
|
||||
{
|
||||
self->use = hurt_use;
|
||||
}
|
||||
|
||||
gi.linkentity(self);
|
||||
}
|
||||
|
||||
void
|
||||
trigger_gravity_use(edict_t *self, edict_t *other /* unused */, edict_t *activator /* unused */)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->solid == SOLID_NOT)
|
||||
{
|
||||
self->solid = SOLID_TRIGGER;
|
||||
}
|
||||
else
|
||||
{
|
||||
self->solid = SOLID_NOT;
|
||||
}
|
||||
|
||||
gi.linkentity(self);
|
||||
}
|
||||
|
||||
/* PGM */
|
||||
|
||||
void
|
||||
trigger_gravity_touch(edict_t *self, edict_t *other, cplane_t *plane /* unused */, csurface_t *surf /* unused */)
|
||||
{
|
||||
if (!self || !other)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
other->gravity = self->gravity;
|
||||
}
|
||||
|
||||
/*
|
||||
* QUAKED trigger_gravity (.5 .5 .5) ? TOGGLE START_OFF
|
||||
* Changes the touching entites gravity to
|
||||
* the value of "gravity". 1.0 is standard
|
||||
* gravity for the level.
|
||||
*
|
||||
* TOGGLE - trigger_gravity can be turned on and off
|
||||
* START_OFF - trigger_gravity starts turned off (implies TOGGLE)
|
||||
*/
|
||||
void
|
||||
SP_trigger_gravity(edict_t *self)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (st.gravity == 0)
|
||||
{
|
||||
gi.dprintf("trigger_gravity without gravity set at %s\n", vtos(self->s.origin));
|
||||
G_FreeEdict(self);
|
||||
return;
|
||||
}
|
||||
|
||||
InitTrigger(self);
|
||||
|
||||
self->gravity = strtof(st.gravity, (char **)NULL);
|
||||
|
||||
if (self->spawnflags & 1) /* TOGGLE */
|
||||
{
|
||||
self->use = trigger_gravity_use;
|
||||
}
|
||||
|
||||
if (self->spawnflags & 2) /* START_OFF */
|
||||
{
|
||||
self->use = trigger_gravity_use;
|
||||
self->solid = SOLID_NOT;
|
||||
}
|
||||
|
||||
self->touch = trigger_gravity_touch;
|
||||
gi.linkentity(self);
|
||||
}
|
||||
|
||||
/*
|
||||
* QUAKED trigger_monsterjump (.5 .5 .5) ?
|
||||
*
|
||||
* Walking monsters that touch this will jump in the direction of the trigger's angle
|
||||
* "speed" default to 200, the speed thrown forward
|
||||
* "height" default to 200, the speed thrown upwards
|
||||
*/
|
||||
|
||||
void
|
||||
trigger_monsterjump_touch(edict_t *self, edict_t *other, cplane_t *plane /* unused */, csurface_t *surf /* unused */)
|
||||
{
|
||||
if (!self || !other)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (other->flags & (FL_FLY | FL_SWIM))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (other->svflags & SVF_DEADMONSTER)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(other->svflags & SVF_MONSTER))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* set XY even if not on ground, so the jump will clear lips */
|
||||
other->velocity[0] = self->movedir[0] * self->speed;
|
||||
other->velocity[1] = self->movedir[1] * self->speed;
|
||||
|
||||
if (!other->groundentity)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
other->groundentity = NULL;
|
||||
other->velocity[2] = self->movedir[2];
|
||||
}
|
||||
|
||||
void
|
||||
SP_trigger_monsterjump(edict_t *self)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!self->speed)
|
||||
{
|
||||
self->speed = 200;
|
||||
}
|
||||
|
||||
if (!st.height)
|
||||
{
|
||||
st.height = 200;
|
||||
}
|
||||
|
||||
if (self->s.angles[YAW] == 0)
|
||||
{
|
||||
self->s.angles[YAW] = 360;
|
||||
}
|
||||
|
||||
InitTrigger(self);
|
||||
self->touch = trigger_monsterjump_touch;
|
||||
self->movedir[2] = st.height;
|
||||
}
|
|
@ -1,834 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 1997-2001 Id Software, Inc.
|
||||
* Copyright (c) ZeniMax Media Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||
* 02111-1307, USA.
|
||||
*
|
||||
* =======================================================================
|
||||
*
|
||||
* Turrets aka big cannons with a driver.
|
||||
*
|
||||
* =======================================================================
|
||||
*/
|
||||
|
||||
#include "header/local.h"
|
||||
|
||||
qboolean FindTarget(edict_t *self);
|
||||
void infantry_die(edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point);
|
||||
void infantry_stand(edict_t *self);
|
||||
void monster_use(edict_t *self, edict_t *other, edict_t *activator);
|
||||
void SpawnTargetingSystem(edict_t *turret);
|
||||
|
||||
void
|
||||
AnglesNormalize(vec3_t vec)
|
||||
{
|
||||
while (vec[0] > 360)
|
||||
{
|
||||
vec[0] -= 360;
|
||||
}
|
||||
|
||||
while (vec[0] < 0)
|
||||
{
|
||||
vec[0] += 360;
|
||||
}
|
||||
|
||||
while (vec[1] > 360)
|
||||
{
|
||||
vec[1] -= 360;
|
||||
}
|
||||
|
||||
while (vec[1] < 0)
|
||||
{
|
||||
vec[1] += 360;
|
||||
}
|
||||
}
|
||||
|
||||
float
|
||||
SnapToEights(float x)
|
||||
{
|
||||
x *= 8.0;
|
||||
|
||||
if (x > 0.0)
|
||||
{
|
||||
x += 0.5;
|
||||
}
|
||||
else
|
||||
{
|
||||
x -= 0.5;
|
||||
}
|
||||
|
||||
return 0.125 * (int)x;
|
||||
}
|
||||
|
||||
void
|
||||
turret_blocked(edict_t *self, edict_t *other)
|
||||
{
|
||||
if (!self || !other)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
edict_t *attacker;
|
||||
|
||||
if (other->takedamage)
|
||||
{
|
||||
if (self->teammaster->owner)
|
||||
{
|
||||
attacker = self->teammaster->owner;
|
||||
}
|
||||
else
|
||||
{
|
||||
attacker = self->teammaster;
|
||||
}
|
||||
|
||||
T_Damage(other, self, attacker, vec3_origin, other->s.origin,
|
||||
vec3_origin, self->teammaster->dmg, 10, 0, MOD_CRUSH);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* QUAKED turret_breach (0 0 0) ?
|
||||
* This portion of the turret can change both pitch and yaw.
|
||||
* The model should be made with a flat pitch.
|
||||
* It (and the associated base) need to be oriented towards 0.
|
||||
* Use "angle" to set the starting angle.
|
||||
*
|
||||
* "speed" default 50
|
||||
* "dmg" default 10
|
||||
* "angle" point this forward
|
||||
* "target" point this at an info_notnull at the muzzle tip
|
||||
* "minpitch" min acceptable pitch angle : default -30
|
||||
* "maxpitch" max acceptable pitch angle : default 30
|
||||
* "minyaw" min acceptable yaw angle : default 0
|
||||
* "maxyaw" max acceptable yaw angle : default 360
|
||||
*/
|
||||
|
||||
void
|
||||
turret_breach_fire(edict_t *self)
|
||||
{
|
||||
vec3_t f, r, u;
|
||||
vec3_t start;
|
||||
int damage;
|
||||
int speed;
|
||||
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AngleVectors(self->s.angles, f, r, u);
|
||||
VectorMA(self->s.origin, self->move_origin[0], f, start);
|
||||
VectorMA(start, self->move_origin[1], r, start);
|
||||
VectorMA(start, self->move_origin[2], u, start);
|
||||
|
||||
damage = 100 + random() * 50;
|
||||
speed = 550 + 50 * skill->value;
|
||||
fire_rocket(self->teammaster->owner, start, f, damage, speed, 150, damage);
|
||||
gi.positioned_sound(start, self, CHAN_WEAPON, gi.soundindex("weapons/rocklf1a.wav"), 1, ATTN_NORM, 0);
|
||||
}
|
||||
|
||||
void
|
||||
turret_breach_think(edict_t *self)
|
||||
{
|
||||
edict_t *ent;
|
||||
vec3_t current_angles;
|
||||
vec3_t delta;
|
||||
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
VectorCopy(self->s.angles, current_angles);
|
||||
AnglesNormalize(current_angles);
|
||||
|
||||
AnglesNormalize(self->move_angles);
|
||||
|
||||
if (self->move_angles[PITCH] > 180)
|
||||
{
|
||||
self->move_angles[PITCH] -= 360;
|
||||
}
|
||||
|
||||
/* clamp angles to mins & maxs */
|
||||
if (self->move_angles[PITCH] > self->pos1[PITCH])
|
||||
{
|
||||
self->move_angles[PITCH] = self->pos1[PITCH];
|
||||
}
|
||||
else if (self->move_angles[PITCH] < self->pos2[PITCH])
|
||||
{
|
||||
self->move_angles[PITCH] = self->pos2[PITCH];
|
||||
}
|
||||
|
||||
if ((self->move_angles[YAW] < self->pos1[YAW]) ||
|
||||
(self->move_angles[YAW] > self->pos2[YAW]))
|
||||
{
|
||||
float dmin, dmax;
|
||||
|
||||
dmin = fabs(self->pos1[YAW] - self->move_angles[YAW]);
|
||||
|
||||
if (dmin < -180)
|
||||
{
|
||||
dmin += 360;
|
||||
}
|
||||
else if (dmin > 180)
|
||||
{
|
||||
dmin -= 360;
|
||||
}
|
||||
|
||||
dmax = fabs(self->pos2[YAW] - self->move_angles[YAW]);
|
||||
|
||||
if (dmax < -180)
|
||||
{
|
||||
dmax += 360;
|
||||
}
|
||||
else if (dmax > 180)
|
||||
{
|
||||
dmax -= 360;
|
||||
}
|
||||
|
||||
if (fabs(dmin) < fabs(dmax))
|
||||
{
|
||||
self->move_angles[YAW] = self->pos1[YAW];
|
||||
}
|
||||
else
|
||||
{
|
||||
self->move_angles[YAW] = self->pos2[YAW];
|
||||
}
|
||||
}
|
||||
|
||||
VectorSubtract(self->move_angles, current_angles, delta);
|
||||
|
||||
if (delta[0] < -180)
|
||||
{
|
||||
delta[0] += 360;
|
||||
}
|
||||
else if (delta[0] > 180)
|
||||
{
|
||||
delta[0] -= 360;
|
||||
}
|
||||
|
||||
if (delta[1] < -180)
|
||||
{
|
||||
delta[1] += 360;
|
||||
}
|
||||
else if (delta[1] > 180)
|
||||
{
|
||||
delta[1] -= 360;
|
||||
}
|
||||
|
||||
delta[2] = 0;
|
||||
|
||||
if (delta[0] > self->speed * FRAMETIME)
|
||||
{
|
||||
delta[0] = self->speed * FRAMETIME;
|
||||
}
|
||||
|
||||
if (delta[0] < -1 * self->speed * FRAMETIME)
|
||||
{
|
||||
delta[0] = -1 * self->speed * FRAMETIME;
|
||||
}
|
||||
|
||||
if (delta[1] > self->speed * FRAMETIME)
|
||||
{
|
||||
delta[1] = self->speed * FRAMETIME;
|
||||
}
|
||||
|
||||
if (delta[1] < -1 * self->speed * FRAMETIME)
|
||||
{
|
||||
delta[1] = -1 * self->speed * FRAMETIME;
|
||||
}
|
||||
|
||||
VectorScale(delta, 1.0 / FRAMETIME, self->avelocity);
|
||||
|
||||
self->nextthink = level.time + FRAMETIME;
|
||||
|
||||
for (ent = self->teammaster; ent; ent = ent->teamchain)
|
||||
{
|
||||
ent->avelocity[1] = self->avelocity[1];
|
||||
}
|
||||
|
||||
/* if we have adriver, adjust his velocities */
|
||||
if (self->owner)
|
||||
{
|
||||
float angle;
|
||||
float target_z;
|
||||
float diff;
|
||||
vec3_t target;
|
||||
vec3_t dir;
|
||||
|
||||
/* angular is easy, just copy ours */
|
||||
self->owner->avelocity[0] = self->avelocity[0];
|
||||
self->owner->avelocity[1] = self->avelocity[1];
|
||||
|
||||
/* x & y */
|
||||
angle = self->s.angles[1] + self->owner->move_origin[1];
|
||||
angle *= (M_PI * 2 / 360);
|
||||
target[0] = SnapToEights(self->s.origin[0] + cos(angle) * self->owner->move_origin[0]);
|
||||
target[1] = SnapToEights(self->s.origin[1] + sin(angle) * self->owner->move_origin[0]);
|
||||
target[2] = self->owner->s.origin[2];
|
||||
|
||||
VectorSubtract(target, self->owner->s.origin, dir);
|
||||
self->owner->velocity[0] = dir[0] * 1.0 / FRAMETIME;
|
||||
self->owner->velocity[1] = dir[1] * 1.0 / FRAMETIME;
|
||||
|
||||
/* z */
|
||||
angle = self->s.angles[PITCH] * (M_PI * 2 / 360);
|
||||
target_z = SnapToEights(self->s.origin[2] + self->owner->move_origin[0] * tan(angle) + self->owner->move_origin[2]);
|
||||
|
||||
diff = target_z - self->owner->s.origin[2];
|
||||
self->owner->velocity[2] = diff * 1.0 / FRAMETIME;
|
||||
|
||||
if (self->spawnflags & 65536)
|
||||
{
|
||||
turret_breach_fire(self);
|
||||
self->spawnflags &= ~65536;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
turret_breach_finish_init(edict_t *self)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* get and save info for muzzle location */
|
||||
if (!self->target)
|
||||
{
|
||||
gi.dprintf("%s at %s needs a target\n", self->classname, vtos(self->s.origin));
|
||||
}
|
||||
else
|
||||
{
|
||||
self->target_ent = G_PickTarget(self->target);
|
||||
|
||||
if (self->target_ent)
|
||||
{
|
||||
VectorSubtract(self->target_ent->s.origin, self->s.origin, self->move_origin);
|
||||
G_FreeEdict(self->target_ent);
|
||||
}
|
||||
else
|
||||
{
|
||||
gi.dprintf("could not find target entity for %s at %s\n", self->classname, vtos(self->s.origin));
|
||||
}
|
||||
}
|
||||
|
||||
self->teammaster->dmg = self->dmg;
|
||||
self->think = turret_breach_think;
|
||||
self->think(self);
|
||||
}
|
||||
|
||||
void
|
||||
SP_turret_breach(edict_t *self)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
self->solid = SOLID_BSP;
|
||||
self->movetype = MOVETYPE_PUSH;
|
||||
gi.setmodel(self, self->model);
|
||||
|
||||
if (!self->speed)
|
||||
{
|
||||
self->speed = 50;
|
||||
}
|
||||
|
||||
if (!self->dmg)
|
||||
{
|
||||
self->dmg = 10;
|
||||
}
|
||||
|
||||
if (!st.minpitch)
|
||||
{
|
||||
st.minpitch = -30;
|
||||
}
|
||||
|
||||
if (!st.maxpitch)
|
||||
{
|
||||
st.maxpitch = 30;
|
||||
}
|
||||
|
||||
if (!st.maxyaw)
|
||||
{
|
||||
st.maxyaw = 360;
|
||||
}
|
||||
|
||||
self->pos1[PITCH] = -1 * st.minpitch;
|
||||
self->pos1[YAW] = st.minyaw;
|
||||
self->pos2[PITCH] = -1 * st.maxpitch;
|
||||
self->pos2[YAW] = st.maxyaw;
|
||||
|
||||
self->ideal_yaw = self->s.angles[YAW];
|
||||
self->move_angles[YAW] = self->ideal_yaw;
|
||||
|
||||
self->blocked = turret_blocked;
|
||||
|
||||
self->think = turret_breach_finish_init;
|
||||
self->nextthink = level.time + FRAMETIME;
|
||||
gi.linkentity(self);
|
||||
}
|
||||
|
||||
/*
|
||||
* QUAKED turret_base (0 0 0) ?
|
||||
* This portion of the turret changes yaw only.
|
||||
* MUST be teamed with a turret_breach.
|
||||
*/
|
||||
|
||||
void
|
||||
SP_turret_base(edict_t *self)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
self->solid = SOLID_BSP;
|
||||
self->movetype = MOVETYPE_PUSH;
|
||||
gi.setmodel(self, self->model);
|
||||
self->blocked = turret_blocked;
|
||||
gi.linkentity(self);
|
||||
}
|
||||
|
||||
/*
|
||||
* QUAKED turret_driver (1 .5 0) (-16 -16 -24) (16 16 32)
|
||||
* Must NOT be on the team with the rest of the turret parts.
|
||||
* Instead it must target the turret_breach.
|
||||
*/
|
||||
void
|
||||
turret_driver_die(edict_t *self, edict_t *inflictor, edict_t *attacker,
|
||||
int damage, vec3_t point)
|
||||
{
|
||||
edict_t *ent;
|
||||
|
||||
if (!self || !inflictor || !attacker)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* level the gun */
|
||||
self->target_ent->move_angles[0] = 0;
|
||||
|
||||
/* remove the driver from the end of them team chain */
|
||||
for (ent = self->target_ent->teammaster;
|
||||
ent->teamchain != self;
|
||||
ent = ent->teamchain)
|
||||
{
|
||||
}
|
||||
|
||||
ent->teamchain = NULL;
|
||||
self->teammaster = NULL;
|
||||
self->flags &= ~FL_TEAMSLAVE;
|
||||
|
||||
self->target_ent->owner = NULL;
|
||||
self->target_ent->teammaster->owner = NULL;
|
||||
|
||||
infantry_die(self, inflictor, attacker, damage, point);
|
||||
}
|
||||
|
||||
void
|
||||
turret_driver_think(edict_t *self)
|
||||
{
|
||||
vec3_t target;
|
||||
vec3_t dir;
|
||||
float reaction_time;
|
||||
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
self->nextthink = level.time + FRAMETIME;
|
||||
|
||||
if (self->enemy && (!self->enemy->inuse || (self->enemy->health <= 0)))
|
||||
{
|
||||
self->enemy = NULL;
|
||||
}
|
||||
|
||||
if (!self->enemy)
|
||||
{
|
||||
if (!FindTarget(self))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
self->monsterinfo.trail_time = level.time;
|
||||
self->monsterinfo.aiflags &= ~AI_LOST_SIGHT;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (visible(self, self->enemy))
|
||||
{
|
||||
if (self->monsterinfo.aiflags & AI_LOST_SIGHT)
|
||||
{
|
||||
self->monsterinfo.trail_time = level.time;
|
||||
self->monsterinfo.aiflags &= ~AI_LOST_SIGHT;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
self->monsterinfo.aiflags |= AI_LOST_SIGHT;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* let the turret know where we want it to aim */
|
||||
VectorCopy(self->enemy->s.origin, target);
|
||||
target[2] += self->enemy->viewheight;
|
||||
VectorSubtract(target, self->target_ent->s.origin, dir);
|
||||
vectoangles(dir, self->target_ent->move_angles);
|
||||
|
||||
/* decide if we should shoot */
|
||||
if (level.time < self->monsterinfo.attack_finished)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
reaction_time = (3 - skill->value) * 1.0;
|
||||
|
||||
if ((level.time - self->monsterinfo.trail_time) < reaction_time)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
self->monsterinfo.attack_finished = level.time + reaction_time + 1.0;
|
||||
self->target_ent->spawnflags |= 65536;
|
||||
}
|
||||
|
||||
void
|
||||
turret_driver_link(edict_t *self)
|
||||
{
|
||||
vec3_t vec;
|
||||
edict_t *ent;
|
||||
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
self->think = turret_driver_think;
|
||||
self->nextthink = level.time + FRAMETIME;
|
||||
|
||||
self->target_ent = G_PickTarget(self->target);
|
||||
self->target_ent->owner = self;
|
||||
self->target_ent->teammaster->owner = self;
|
||||
VectorCopy(self->target_ent->s.angles, self->s.angles);
|
||||
|
||||
vec[0] = self->target_ent->s.origin[0] - self->s.origin[0];
|
||||
vec[1] = self->target_ent->s.origin[1] - self->s.origin[1];
|
||||
vec[2] = 0;
|
||||
self->move_origin[0] = VectorLength(vec);
|
||||
|
||||
VectorSubtract(self->s.origin, self->target_ent->s.origin, vec);
|
||||
vectoangles(vec, vec);
|
||||
AnglesNormalize(vec);
|
||||
self->move_origin[1] = vec[1];
|
||||
|
||||
self->move_origin[2] = self->s.origin[2] - self->target_ent->s.origin[2];
|
||||
|
||||
/* add the driver to the end of them team chain */
|
||||
for (ent = self->target_ent->teammaster; ent->teamchain; ent = ent->teamchain)
|
||||
{
|
||||
}
|
||||
|
||||
ent->teamchain = self;
|
||||
self->teammaster = self->target_ent->teammaster;
|
||||
self->flags |= FL_TEAMSLAVE;
|
||||
}
|
||||
|
||||
void
|
||||
SP_turret_driver(edict_t *self)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (deathmatch->value)
|
||||
{
|
||||
G_FreeEdict(self);
|
||||
return;
|
||||
}
|
||||
|
||||
self->movetype = MOVETYPE_PUSH;
|
||||
self->solid = SOLID_BBOX;
|
||||
self->s.modelindex = gi.modelindex("models/monsters/infantry/tris.md2");
|
||||
VectorSet(self->mins, -16, -16, -24);
|
||||
VectorSet(self->maxs, 16, 16, 32);
|
||||
|
||||
self->health = 100;
|
||||
self->gib_health = 0;
|
||||
self->mass = 200;
|
||||
self->viewheight = 24;
|
||||
|
||||
self->die = turret_driver_die;
|
||||
self->monsterinfo.stand = infantry_stand;
|
||||
|
||||
self->flags |= FL_NO_KNOCKBACK;
|
||||
|
||||
level.total_monsters++;
|
||||
|
||||
self->svflags |= SVF_MONSTER;
|
||||
self->s.renderfx |= RF_FRAMELERP;
|
||||
self->takedamage = DAMAGE_AIM;
|
||||
self->use = monster_use;
|
||||
self->clipmask = MASK_MONSTERSOLID;
|
||||
VectorCopy(self->s.origin, self->s.old_origin);
|
||||
self->monsterinfo.aiflags |= AI_STAND_GROUND | AI_DUCKED;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
self->think = turret_driver_link;
|
||||
self->nextthink = level.time + FRAMETIME;
|
||||
|
||||
gi.linkentity(self);
|
||||
}
|
||||
|
||||
/*
|
||||
* invisible turret drivers so we can have unmanned turrets.
|
||||
* originally designed to shoot at func_trains and such, so they
|
||||
* fire at the center of the bounding box, rather than the entity's
|
||||
* origin. */
|
||||
|
||||
void
|
||||
turret_brain_think(edict_t *self)
|
||||
{
|
||||
vec3_t dir;
|
||||
vec3_t endpos;
|
||||
float reaction_time;
|
||||
trace_t trace;
|
||||
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
self->nextthink = level.time + FRAMETIME;
|
||||
|
||||
if (self->enemy)
|
||||
{
|
||||
if (!self->enemy->inuse)
|
||||
{
|
||||
self->enemy = NULL;
|
||||
}
|
||||
else if (self->enemy->takedamage && (self->enemy->health <= 0))
|
||||
{
|
||||
self->enemy = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!self->enemy)
|
||||
{
|
||||
if (!FindTarget(self))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
self->monsterinfo.trail_time = level.time;
|
||||
self->monsterinfo.aiflags &= ~AI_LOST_SIGHT;
|
||||
|
||||
VectorAdd(self->enemy->absmax, self->enemy->absmin, endpos);
|
||||
VectorScale(endpos, 0.5, endpos);
|
||||
}
|
||||
else
|
||||
{
|
||||
VectorAdd(self->enemy->absmax, self->enemy->absmin, endpos);
|
||||
VectorScale(endpos, 0.5, endpos);
|
||||
|
||||
trace = gi.trace(self->target_ent->s.origin, vec3_origin, vec3_origin,
|
||||
endpos, self->target_ent, MASK_SHOT);
|
||||
|
||||
if ((trace.fraction == 1) || (trace.ent == self->enemy))
|
||||
{
|
||||
if (self->monsterinfo.aiflags & AI_LOST_SIGHT)
|
||||
{
|
||||
self->monsterinfo.trail_time = level.time;
|
||||
self->monsterinfo.aiflags &= ~AI_LOST_SIGHT;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
self->monsterinfo.aiflags |= AI_LOST_SIGHT;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* let the turret know where we want it to aim */
|
||||
VectorSubtract(endpos, self->target_ent->s.origin, dir);
|
||||
vectoangles(dir, self->target_ent->move_angles);
|
||||
|
||||
/* decide if we should shoot */
|
||||
if (level.time < self->monsterinfo.attack_finished)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->delay)
|
||||
{
|
||||
reaction_time = self->delay;
|
||||
}
|
||||
else
|
||||
{
|
||||
reaction_time = (3 - skill->value) * 1.0;
|
||||
}
|
||||
|
||||
if ((level.time - self->monsterinfo.trail_time) < reaction_time)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
self->monsterinfo.attack_finished = level.time + reaction_time + 1.0;
|
||||
self->target_ent->spawnflags |= 65536;
|
||||
}
|
||||
|
||||
void
|
||||
turret_brain_link(edict_t *self)
|
||||
{
|
||||
vec3_t vec;
|
||||
edict_t *ent;
|
||||
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->killtarget)
|
||||
{
|
||||
self->enemy = G_PickTarget(self->killtarget);
|
||||
}
|
||||
|
||||
self->think = turret_brain_think;
|
||||
self->nextthink = level.time + FRAMETIME;
|
||||
|
||||
self->target_ent = G_PickTarget(self->target);
|
||||
self->target_ent->owner = self;
|
||||
self->target_ent->teammaster->owner = self;
|
||||
VectorCopy(self->target_ent->s.angles, self->s.angles);
|
||||
|
||||
vec[0] = self->target_ent->s.origin[0] - self->s.origin[0];
|
||||
vec[1] = self->target_ent->s.origin[1] - self->s.origin[1];
|
||||
vec[2] = 0;
|
||||
self->move_origin[0] = VectorLength(vec);
|
||||
|
||||
VectorSubtract(self->s.origin, self->target_ent->s.origin, vec);
|
||||
vectoangles(vec, vec);
|
||||
AnglesNormalize(vec);
|
||||
self->move_origin[1] = vec[1];
|
||||
|
||||
self->move_origin[2] = self->s.origin[2] - self->target_ent->s.origin[2];
|
||||
|
||||
/* add the driver to the end of them team chain */
|
||||
for (ent = self->target_ent->teammaster; ent->teamchain; ent = ent->teamchain)
|
||||
{
|
||||
}
|
||||
|
||||
ent->teamchain = self;
|
||||
self->teammaster = self->target_ent->teammaster;
|
||||
self->flags |= FL_TEAMSLAVE;
|
||||
}
|
||||
|
||||
void
|
||||
turret_brain_deactivate(edict_t *self, edict_t *other /* unused */, edict_t *activator /* unused */)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
self->think = NULL;
|
||||
self->nextthink = 0;
|
||||
}
|
||||
|
||||
void
|
||||
turret_brain_activate(edict_t *self, edict_t *other /* unused */, edict_t *activator)
|
||||
{
|
||||
if (!self || !activator)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!self->enemy)
|
||||
{
|
||||
self->enemy = activator;
|
||||
}
|
||||
|
||||
/* wait at least 3 seconds to fire. */
|
||||
self->monsterinfo.attack_finished = level.time + 3;
|
||||
self->use = turret_brain_deactivate;
|
||||
|
||||
self->think = turret_brain_link;
|
||||
self->nextthink = level.time + FRAMETIME;
|
||||
}
|
||||
|
||||
/*
|
||||
* QUAKED turret_invisible_brain (1 .5 0) (-16 -16 -16) (16 16 16)
|
||||
* Invisible brain to drive the turret.
|
||||
*
|
||||
* Does not search for targets. If targeted, can only be turned on once
|
||||
* and then off once. After that they are completely disabled.
|
||||
*
|
||||
* "delay" the delay between firing (default ramps for skill level)
|
||||
* "Target" the turret breach
|
||||
* "Killtarget" the item you want it to attack.
|
||||
* Target the brain if you want it activated later, instead of immediately. It will wait 3 seconds
|
||||
* before firing to acquire the target.
|
||||
*/
|
||||
void
|
||||
SP_turret_invisible_brain(edict_t *self)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!self->killtarget)
|
||||
{
|
||||
gi.dprintf("turret_invisible_brain with no killtarget!\n");
|
||||
G_FreeEdict(self);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!self->target)
|
||||
{
|
||||
gi.dprintf("turret_invisible_brain with no target!\n");
|
||||
G_FreeEdict(self);
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->targetname)
|
||||
{
|
||||
self->use = turret_brain_activate;
|
||||
}
|
||||
else
|
||||
{
|
||||
self->think = turret_brain_link;
|
||||
self->nextthink = level.time + FRAMETIME;
|
||||
}
|
||||
|
||||
self->movetype = MOVETYPE_PUSH;
|
||||
gi.linkentity(self);
|
||||
}
|
|
@ -1,916 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 1997-2001 Id Software, Inc.
|
||||
* Copyright (c) ZeniMax Media Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||
* 02111-1307, USA.
|
||||
*
|
||||
* =======================================================================
|
||||
*
|
||||
* Misc. utility functions for the game logic.
|
||||
*
|
||||
* =======================================================================
|
||||
*/
|
||||
|
||||
#include "header/local.h"
|
||||
|
||||
#define MAXCHOICES 8
|
||||
|
||||
static vec3_t VEC_UP = {0, -1, 0};
|
||||
static vec3_t MOVEDIR_UP = {0, 0, 1};
|
||||
static vec3_t VEC_DOWN = {0, -2, 0};
|
||||
static vec3_t MOVEDIR_DOWN = {0, 0, -1};
|
||||
|
||||
void
|
||||
G_ProjectSource(vec3_t point, vec3_t distance, vec3_t forward,
|
||||
vec3_t right, vec3_t result)
|
||||
{
|
||||
result[0] = point[0] + forward[0] * distance[0] + right[0] * distance[1];
|
||||
result[1] = point[1] + forward[1] * distance[0] + right[1] * distance[1];
|
||||
result[2] = point[2] + forward[2] * distance[0] + right[2] * distance[1] +
|
||||
distance[2];
|
||||
}
|
||||
|
||||
void
|
||||
G_ProjectSource2(vec3_t point, vec3_t distance, vec3_t forward,
|
||||
vec3_t right, vec3_t up, vec3_t result)
|
||||
{
|
||||
result[0] = point[0] + forward[0] * distance[0] + right[0] * distance[1] +
|
||||
up[0] * distance[2];
|
||||
result[1] = point[1] + forward[1] * distance[0] + right[1] * distance[1] +
|
||||
up[1] * distance[2];
|
||||
result[2] = point[2] + forward[2] * distance[0] + right[2] * distance[1] +
|
||||
up[2] * distance[2];
|
||||
}
|
||||
|
||||
/*
|
||||
* Searches all active entities for the next
|
||||
* one that holds the matching string at fieldofs
|
||||
* (use the FOFS() macro) in the structure.
|
||||
*
|
||||
* Searches beginning at the edict after from, or
|
||||
* the beginning. If NULL, NULL will be returned
|
||||
* if the end of the list is reached.
|
||||
*/
|
||||
edict_t *
|
||||
G_Find(edict_t *from, int fieldofs, char *match)
|
||||
{
|
||||
char *s;
|
||||
|
||||
if (!match)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!from)
|
||||
{
|
||||
from = g_edicts;
|
||||
}
|
||||
else
|
||||
{
|
||||
from++;
|
||||
}
|
||||
|
||||
for ( ; from < &g_edicts[globals.num_edicts]; from++)
|
||||
{
|
||||
if (!from->inuse)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
s = *(char **)((byte *)from + fieldofs);
|
||||
|
||||
if (!s)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!Q_stricmp(s, match))
|
||||
{
|
||||
return from;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns entities that have origins
|
||||
* within a spherical area
|
||||
*/
|
||||
edict_t *
|
||||
findradius(edict_t *from, vec3_t org, float rad)
|
||||
{
|
||||
vec3_t eorg;
|
||||
int j;
|
||||
|
||||
if (!from)
|
||||
{
|
||||
from = g_edicts;
|
||||
}
|
||||
else
|
||||
{
|
||||
from++;
|
||||
}
|
||||
|
||||
for ( ; from < &g_edicts[globals.num_edicts]; from++)
|
||||
{
|
||||
if (!from->inuse)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (from->solid == SOLID_NOT)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (j = 0; j < 3; j++)
|
||||
{
|
||||
eorg[j] = org[j] - (from->s.origin[j] +
|
||||
(from->mins[j] + from->maxs[j]) * 0.5);
|
||||
}
|
||||
|
||||
if (VectorLength(eorg) > rad)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
return from;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns entities that have origins within a spherical area
|
||||
*/
|
||||
edict_t *
|
||||
findradius2(edict_t *from, vec3_t org, float rad)
|
||||
{
|
||||
/* rad must be positive */
|
||||
vec3_t eorg;
|
||||
int j;
|
||||
|
||||
if (!from)
|
||||
{
|
||||
from = g_edicts;
|
||||
}
|
||||
else
|
||||
{
|
||||
from++;
|
||||
}
|
||||
|
||||
for ( ; from < &g_edicts[globals.num_edicts]; from++)
|
||||
{
|
||||
if (!from->inuse)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (from->solid == SOLID_NOT)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!from->takedamage)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!(from->svflags & SVF_DAMAGEABLE))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (j = 0; j < 3; j++)
|
||||
{
|
||||
eorg[j] = org[j] - (from->s.origin[j] + (from->mins[j] + from->maxs[j]) * 0.5);
|
||||
}
|
||||
|
||||
if (VectorLength(eorg) > rad)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
return from;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Searches all active entities for
|
||||
* the next one that holds the matching
|
||||
* string at fieldofs (use the FOFS() macro)
|
||||
* in the structure.
|
||||
*
|
||||
* Searches beginning at the edict after from,
|
||||
* or the beginning. If NULL, NULL will be
|
||||
* returned if the end of the list is reached.
|
||||
*/
|
||||
edict_t *
|
||||
G_PickTarget(char *targetname)
|
||||
{
|
||||
edict_t *ent = NULL;
|
||||
int num_choices = 0;
|
||||
edict_t *choice[MAXCHOICES];
|
||||
|
||||
if (!targetname)
|
||||
{
|
||||
gi.dprintf("G_PickTarget called with NULL targetname\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
while (1)
|
||||
{
|
||||
ent = G_Find(ent, FOFS(targetname), targetname);
|
||||
|
||||
if (!ent)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
choice[num_choices++] = ent;
|
||||
|
||||
if (num_choices == MAXCHOICES)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!num_choices)
|
||||
{
|
||||
gi.dprintf("G_PickTarget: target %s not found\n", targetname);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return choice[randk() % num_choices];
|
||||
}
|
||||
|
||||
void
|
||||
Think_Delay(edict_t *ent)
|
||||
{
|
||||
if (!ent)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
G_UseTargets(ent, ent->activator);
|
||||
G_FreeEdict(ent);
|
||||
}
|
||||
|
||||
/*
|
||||
* The global "activator" should be set to
|
||||
* the entity that initiated the firing.
|
||||
*
|
||||
* If self.delay is set, a DelayedUse entity
|
||||
* will be created that will actually do the
|
||||
* SUB_UseTargets after that many seconds have passed.
|
||||
*
|
||||
* Centerprints any self.message to the activator.
|
||||
*
|
||||
* Search for (string)targetname in all entities that
|
||||
* match (string)self.target and call their .use function
|
||||
*/
|
||||
void
|
||||
G_UseTargets(edict_t *ent, edict_t *activator)
|
||||
{
|
||||
edict_t *t;
|
||||
edict_t *master;
|
||||
|
||||
if (!ent)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* check for a delay */
|
||||
if (ent->delay)
|
||||
{
|
||||
/* create a temp object to fire at a later time */
|
||||
t = G_Spawn();
|
||||
t->classname = "DelayedUse";
|
||||
t->nextthink = level.time + ent->delay;
|
||||
t->think = Think_Delay;
|
||||
t->activator = activator;
|
||||
|
||||
if (!activator)
|
||||
{
|
||||
gi.dprintf("Think_Delay with no activator\n");
|
||||
}
|
||||
|
||||
t->message = ent->message;
|
||||
t->target = ent->target;
|
||||
t->killtarget = ent->killtarget;
|
||||
return;
|
||||
}
|
||||
|
||||
/* print the message */
|
||||
if (activator && (ent->message) && !(activator->svflags & SVF_MONSTER))
|
||||
{
|
||||
gi.centerprintf(activator, "%s", ent->message);
|
||||
|
||||
if (ent->noise_index)
|
||||
{
|
||||
gi.sound(activator, CHAN_AUTO, ent->noise_index, 1, ATTN_NORM, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
gi.sound(activator, CHAN_AUTO, gi.soundindex(
|
||||
"misc/talk1.wav"), 1, ATTN_NORM, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* kill killtargets */
|
||||
if (ent->killtarget)
|
||||
{
|
||||
t = NULL;
|
||||
|
||||
while ((t = G_Find(t, FOFS(targetname), ent->killtarget)))
|
||||
{
|
||||
/* if this entity is part of a train, cleanly remove it */
|
||||
if (t->flags & FL_TEAMSLAVE)
|
||||
{
|
||||
master = t->teammaster;
|
||||
|
||||
while (master)
|
||||
{
|
||||
if (master->teamchain == t)
|
||||
{
|
||||
master->teamchain = t->teamchain;
|
||||
break;
|
||||
}
|
||||
|
||||
master = master->teamchain;
|
||||
}
|
||||
}
|
||||
|
||||
/* correct killcounter if a living monster gets killtargeted */
|
||||
if ((t->monsterinfo.checkattack || strcmp (t->classname, "turret_driver") == 0) &&
|
||||
!(t->monsterinfo.aiflags & (AI_GOOD_GUY|AI_DO_NOT_COUNT)) && t->deadflag != DEAD_DEAD)
|
||||
{
|
||||
level.killed_monsters++;
|
||||
}
|
||||
|
||||
G_FreeEdict(t);
|
||||
|
||||
if (!ent->inuse)
|
||||
{
|
||||
gi.dprintf("entity was removed while using killtargets\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* fire targets */
|
||||
if (ent->target)
|
||||
{
|
||||
t = NULL;
|
||||
|
||||
while ((t = G_Find(t, FOFS(targetname), ent->target)))
|
||||
{
|
||||
/* doors fire area portals in a specific way */
|
||||
if (!Q_stricmp(t->classname, "func_areaportal") &&
|
||||
(!Q_stricmp(ent->classname, "func_door") ||
|
||||
!Q_stricmp(ent->classname, "func_door_rotating")))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (t == ent)
|
||||
{
|
||||
gi.dprintf("WARNING: Entity used itself.\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (t->use)
|
||||
{
|
||||
t->use(t, ent, activator);
|
||||
}
|
||||
}
|
||||
|
||||
if (!ent->inuse)
|
||||
{
|
||||
gi.dprintf("entity was removed while using targets\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This is just a convenience function
|
||||
* for making temporary vectors for function calls
|
||||
*/
|
||||
float *
|
||||
tv(float x, float y, float z)
|
||||
{
|
||||
static int index;
|
||||
static vec3_t vecs[8];
|
||||
float *v;
|
||||
|
||||
/* use an array so that multiple
|
||||
tempvectors won't collide
|
||||
for a while */
|
||||
v = vecs[index];
|
||||
index = (index + 1) & 7;
|
||||
|
||||
v[0] = x;
|
||||
v[1] = y;
|
||||
v[2] = z;
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is just a convenience function
|
||||
* for printing vectors
|
||||
*/
|
||||
char *
|
||||
vtos(vec3_t v)
|
||||
{
|
||||
static int index;
|
||||
static char str[8][32];
|
||||
char *s;
|
||||
|
||||
/* use an array so that multiple vtos won't collide */
|
||||
s = str[index];
|
||||
index = (index + 1) & 7;
|
||||
|
||||
Com_sprintf(s, 32, "(%i %i %i)", (int)v[0], (int)v[1], (int)v[2]);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
void
|
||||
get_normal_vector(const cplane_t *p, vec3_t normal)
|
||||
{
|
||||
if (p)
|
||||
{
|
||||
VectorCopy(p->normal, normal);
|
||||
}
|
||||
else
|
||||
{
|
||||
VectorCopy(vec3_origin, normal);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
G_SetMovedir(vec3_t angles, vec3_t movedir)
|
||||
{
|
||||
if (VectorCompare(angles, VEC_UP))
|
||||
{
|
||||
VectorCopy(MOVEDIR_UP, movedir);
|
||||
}
|
||||
else if (VectorCompare(angles, VEC_DOWN))
|
||||
{
|
||||
VectorCopy(MOVEDIR_DOWN, movedir);
|
||||
}
|
||||
else
|
||||
{
|
||||
AngleVectors(angles, movedir, NULL, NULL);
|
||||
}
|
||||
|
||||
VectorClear(angles);
|
||||
}
|
||||
|
||||
float
|
||||
vectoyaw(vec3_t vec)
|
||||
{
|
||||
float yaw;
|
||||
|
||||
if (vec[PITCH] == 0)
|
||||
{
|
||||
yaw = 0;
|
||||
|
||||
if (vec[YAW] > 0)
|
||||
{
|
||||
yaw = 90;
|
||||
}
|
||||
else if (vec[YAW] < 0)
|
||||
{
|
||||
yaw = -90;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
yaw = (int)(atan2(vec[YAW], vec[PITCH]) * 180 / M_PI);
|
||||
|
||||
if (yaw < 0)
|
||||
{
|
||||
yaw += 360;
|
||||
}
|
||||
}
|
||||
|
||||
return yaw;
|
||||
}
|
||||
|
||||
float
|
||||
vectoyaw2(vec3_t vec)
|
||||
{
|
||||
float yaw;
|
||||
|
||||
if (vec[PITCH] == 0)
|
||||
{
|
||||
if (vec[YAW] == 0)
|
||||
{
|
||||
yaw = 0;
|
||||
}
|
||||
else if (vec[YAW] > 0)
|
||||
{
|
||||
yaw = 90;
|
||||
}
|
||||
else
|
||||
{
|
||||
yaw = 270;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
yaw = (atan2(vec[YAW], vec[PITCH]) * 180 / M_PI);
|
||||
|
||||
if (yaw < 0)
|
||||
{
|
||||
yaw += 360;
|
||||
}
|
||||
}
|
||||
|
||||
return yaw;
|
||||
}
|
||||
|
||||
void
|
||||
vectoangles(vec3_t value1, vec3_t angles)
|
||||
{
|
||||
float forward;
|
||||
float yaw, pitch;
|
||||
|
||||
if ((value1[1] == 0) && (value1[0] == 0))
|
||||
{
|
||||
yaw = 0;
|
||||
|
||||
if (value1[2] > 0)
|
||||
{
|
||||
pitch = 90;
|
||||
}
|
||||
else
|
||||
{
|
||||
pitch = 270;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (value1[0])
|
||||
{
|
||||
yaw = (int)(atan2(value1[1], value1[0]) * 180 / M_PI);
|
||||
}
|
||||
else if (value1[1] > 0)
|
||||
{
|
||||
yaw = 90;
|
||||
}
|
||||
else
|
||||
{
|
||||
yaw = -90;
|
||||
}
|
||||
|
||||
if (yaw < 0)
|
||||
{
|
||||
yaw += 360;
|
||||
}
|
||||
|
||||
forward = sqrt(value1[0] * value1[0] + value1[1] * value1[1]);
|
||||
pitch = (int)(atan2(value1[2], forward) * 180 / M_PI);
|
||||
|
||||
if (pitch < 0)
|
||||
{
|
||||
pitch += 360;
|
||||
}
|
||||
}
|
||||
|
||||
angles[PITCH] = -pitch;
|
||||
angles[YAW] = yaw;
|
||||
angles[ROLL] = 0;
|
||||
}
|
||||
|
||||
void
|
||||
vectoangles2(vec3_t value1, vec3_t angles)
|
||||
{
|
||||
float forward;
|
||||
float yaw, pitch;
|
||||
|
||||
if ((value1[1] == 0) && (value1[0] == 0))
|
||||
{
|
||||
yaw = 0;
|
||||
|
||||
if (value1[2] > 0)
|
||||
{
|
||||
pitch = 90;
|
||||
}
|
||||
else
|
||||
{
|
||||
pitch = 270;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (value1[0])
|
||||
{
|
||||
yaw = (atan2(value1[1], value1[0]) * 180 / M_PI);
|
||||
}
|
||||
else if (value1[1] > 0)
|
||||
{
|
||||
yaw = 90;
|
||||
}
|
||||
else
|
||||
{
|
||||
yaw = 270;
|
||||
}
|
||||
|
||||
if (yaw < 0)
|
||||
{
|
||||
yaw += 360;
|
||||
}
|
||||
|
||||
forward = sqrt(value1[0] * value1[0] + value1[1] * value1[1]);
|
||||
pitch = (atan2(value1[2], forward) * 180 / M_PI);
|
||||
|
||||
if (pitch < 0)
|
||||
{
|
||||
pitch += 360;
|
||||
}
|
||||
}
|
||||
|
||||
angles[PITCH] = -pitch;
|
||||
angles[YAW] = yaw;
|
||||
angles[ROLL] = 0;
|
||||
}
|
||||
|
||||
char *
|
||||
G_CopyString(char *in)
|
||||
{
|
||||
char *out;
|
||||
|
||||
if (!in)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
out = gi.TagMalloc(strlen(in) + 1, TAG_LEVEL);
|
||||
strcpy(out, in);
|
||||
return out;
|
||||
}
|
||||
|
||||
void
|
||||
G_InitEdict(edict_t *e)
|
||||
{
|
||||
if (!e)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (e->nextthink)
|
||||
{
|
||||
e->nextthink = 0;
|
||||
}
|
||||
|
||||
e->inuse = true;
|
||||
e->classname = "noclass";
|
||||
e->gravity = 1.0;
|
||||
e->s.number = e - g_edicts;
|
||||
|
||||
e->gravityVector[0] = 0.0;
|
||||
e->gravityVector[1] = 0.0;
|
||||
e->gravityVector[2] = -1.0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Either finds a free edict, or allocates a
|
||||
* new one. Try to avoid reusing an entity
|
||||
* that was recently freed, because it can
|
||||
* cause the client to think the entity
|
||||
* morphed into something else instead of
|
||||
* being removed and recreated, which can
|
||||
* cause interpolated angles and bad trails.
|
||||
*/
|
||||
#define POLICY_DEFAULT 0
|
||||
#define POLICY_DESPERATE 1
|
||||
|
||||
static edict_t *
|
||||
G_FindFreeEdict(int policy)
|
||||
{
|
||||
edict_t *e;
|
||||
|
||||
for (e = g_edicts + game.maxclients + 1 ; e < &g_edicts[globals.num_edicts] ; e++)
|
||||
{
|
||||
/* the first couple seconds of server time can involve a lot of
|
||||
freeing and allocating, so relax the replacement policy
|
||||
*/
|
||||
if (!e->inuse && (policy == POLICY_DESPERATE || e->freetime < 2.0f || (level.time - e->freetime) > 0.5f))
|
||||
{
|
||||
G_InitEdict (e);
|
||||
return e;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
edict_t *
|
||||
G_SpawnOptional(void)
|
||||
{
|
||||
edict_t *e = G_FindFreeEdict (POLICY_DEFAULT);
|
||||
|
||||
if (e)
|
||||
{
|
||||
return e;
|
||||
}
|
||||
|
||||
if (globals.num_edicts >= game.maxentities)
|
||||
{
|
||||
return G_FindFreeEdict (POLICY_DESPERATE);
|
||||
}
|
||||
|
||||
e = &g_edicts[globals.num_edicts++];
|
||||
G_InitEdict (e);
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
edict_t *
|
||||
G_Spawn(void)
|
||||
{
|
||||
edict_t *e = G_SpawnOptional();
|
||||
|
||||
if (!e)
|
||||
gi.error ("ED_Alloc: no free edicts");
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
/*
|
||||
* Marks the edict as free
|
||||
*/
|
||||
void
|
||||
G_FreeEdict(edict_t *ed)
|
||||
{
|
||||
if (!ed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
gi.unlinkentity(ed); /* unlink from world */
|
||||
|
||||
if (deathmatch->value || coop->value)
|
||||
{
|
||||
if ((ed - g_edicts) <= (maxclients->value + BODY_QUEUE_SIZE))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((ed - g_edicts) <= maxclients->value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
memset(ed, 0, sizeof(*ed));
|
||||
ed->classname = "freed";
|
||||
ed->freetime = level.time;
|
||||
ed->inuse = false;
|
||||
}
|
||||
|
||||
void
|
||||
G_TouchTriggers(edict_t *ent)
|
||||
{
|
||||
int i, num;
|
||||
edict_t *touch[MAX_EDICTS], *hit;
|
||||
|
||||
if (!ent)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* dead things don't activate triggers! */
|
||||
if ((ent->client || (ent->svflags & SVF_MONSTER)) && (ent->health <= 0))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
num = gi.BoxEdicts(ent->absmin, ent->absmax, touch,
|
||||
MAX_EDICTS, AREA_TRIGGERS);
|
||||
|
||||
/* be careful, it is possible to have an entity in this
|
||||
list removed before we get to it (killtriggered) */
|
||||
for (i = 0; i < num; i++)
|
||||
{
|
||||
hit = touch[i];
|
||||
|
||||
if (!hit->inuse)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!hit->touch)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
hit->touch(hit, ent, NULL, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Call after linking a new trigger
|
||||
* in during gameplay to force all
|
||||
* entities it covers to immediately
|
||||
* touch it
|
||||
*/
|
||||
void
|
||||
G_TouchSolids(edict_t *ent)
|
||||
{
|
||||
int i, num;
|
||||
edict_t *touch[MAX_EDICTS], *hit;
|
||||
|
||||
if (!ent)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
num = gi.BoxEdicts(ent->absmin, ent->absmax, touch,
|
||||
MAX_EDICTS, AREA_SOLID);
|
||||
|
||||
/* be careful, it is possible to have an entity in this
|
||||
list removed before we get to it (killtriggered) */
|
||||
for (i = 0; i < num; i++)
|
||||
{
|
||||
hit = touch[i];
|
||||
|
||||
if (!hit->inuse)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ent->touch)
|
||||
{
|
||||
ent->touch(hit, ent, NULL, NULL);
|
||||
}
|
||||
|
||||
if (!ent->inuse)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Kills all entities that would touch the
|
||||
* proposed new positioning of ent. Ent s
|
||||
* hould be unlinked before calling this!
|
||||
*/
|
||||
qboolean
|
||||
KillBox(edict_t *ent)
|
||||
{
|
||||
trace_t tr;
|
||||
|
||||
if (!ent)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
while (1)
|
||||
{
|
||||
tr = gi.trace(ent->s.origin, ent->mins, ent->maxs, ent->s.origin,
|
||||
NULL, MASK_PLAYERSOLID);
|
||||
|
||||
if (!tr.ent)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
/* nail it */
|
||||
T_Damage(tr.ent, ent, ent, vec3_origin, ent->s.origin, vec3_origin,
|
||||
100000, 0, DAMAGE_NO_PROTECTION, MOD_TELEFRAG);
|
||||
|
||||
/* if we didn't kill it, fail */
|
||||
if (tr.ent->solid)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true; /* all clear */
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,505 @@
|
|||
/*
|
||||
* Copyright (C) 1997-2001 Id Software, Inc.
|
||||
* Copyright (c) ZeniMax Media Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||
* 02111-1307, USA.
|
||||
*
|
||||
* =======================================================================
|
||||
*
|
||||
* Soldier aka "Guard" animations. This is the new model added in
|
||||
* Xatrix, used for the new variants of the enemy.
|
||||
*
|
||||
* =======================================================================
|
||||
*/
|
||||
|
||||
#define FRAME_attak101 0
|
||||
#define FRAME_attak102 1
|
||||
#define FRAME_attak103 2
|
||||
#define FRAME_attak104 3
|
||||
#define FRAME_attak105 4
|
||||
#define FRAME_attak106 5
|
||||
#define FRAME_attak107 6
|
||||
#define FRAME_attak108 7
|
||||
#define FRAME_attak109 8
|
||||
#define FRAME_attak110 9
|
||||
#define FRAME_attak111 10
|
||||
#define FRAME_attak112 11
|
||||
#define FRAME_attak201 12
|
||||
#define FRAME_attak202 13
|
||||
#define FRAME_attak203 14
|
||||
#define FRAME_attak204 15
|
||||
#define FRAME_attak205 16
|
||||
#define FRAME_attak206 17
|
||||
#define FRAME_attak207 18
|
||||
#define FRAME_attak208 19
|
||||
#define FRAME_attak209 20
|
||||
#define FRAME_attak210 21
|
||||
#define FRAME_attak211 22
|
||||
#define FRAME_attak212 23
|
||||
#define FRAME_attak213 24
|
||||
#define FRAME_attak214 25
|
||||
#define FRAME_attak215 26
|
||||
#define FRAME_attak216 27
|
||||
#define FRAME_attak217 28
|
||||
#define FRAME_attak218 29
|
||||
#define FRAME_attak301 30
|
||||
#define FRAME_attak302 31
|
||||
#define FRAME_attak303 32
|
||||
#define FRAME_attak304 33
|
||||
#define FRAME_attak305 34
|
||||
#define FRAME_attak306 35
|
||||
#define FRAME_attak307 36
|
||||
#define FRAME_attak308 37
|
||||
#define FRAME_attak309 38
|
||||
#define FRAME_attak401 39
|
||||
#define FRAME_attak402 40
|
||||
#define FRAME_attak403 41
|
||||
#define FRAME_attak404 42
|
||||
#define FRAME_attak405 43
|
||||
#define FRAME_attak406 44
|
||||
#define FRAME_duck01 45
|
||||
#define FRAME_duck02 46
|
||||
#define FRAME_duck03 47
|
||||
#define FRAME_duck04 48
|
||||
#define FRAME_duck05 49
|
||||
#define FRAME_pain101 50
|
||||
#define FRAME_pain102 51
|
||||
#define FRAME_pain103 52
|
||||
#define FRAME_pain104 53
|
||||
#define FRAME_pain105 54
|
||||
#define FRAME_pain201 55
|
||||
#define FRAME_pain202 56
|
||||
#define FRAME_pain203 57
|
||||
#define FRAME_pain204 58
|
||||
#define FRAME_pain205 59
|
||||
#define FRAME_pain206 60
|
||||
#define FRAME_pain207 61
|
||||
#define FRAME_pain301 62
|
||||
#define FRAME_pain302 63
|
||||
#define FRAME_pain303 64
|
||||
#define FRAME_pain304 65
|
||||
#define FRAME_pain305 66
|
||||
#define FRAME_pain306 67
|
||||
#define FRAME_pain307 68
|
||||
#define FRAME_pain308 69
|
||||
#define FRAME_pain309 70
|
||||
#define FRAME_pain310 71
|
||||
#define FRAME_pain311 72
|
||||
#define FRAME_pain312 73
|
||||
#define FRAME_pain313 74
|
||||
#define FRAME_pain314 75
|
||||
#define FRAME_pain315 76
|
||||
#define FRAME_pain316 77
|
||||
#define FRAME_pain317 78
|
||||
#define FRAME_pain318 79
|
||||
#define FRAME_pain401 80
|
||||
#define FRAME_pain402 81
|
||||
#define FRAME_pain403 82
|
||||
#define FRAME_pain404 83
|
||||
#define FRAME_pain405 84
|
||||
#define FRAME_pain406 85
|
||||
#define FRAME_pain407 86
|
||||
#define FRAME_pain408 87
|
||||
#define FRAME_pain409 88
|
||||
#define FRAME_pain410 89
|
||||
#define FRAME_pain411 90
|
||||
#define FRAME_pain412 91
|
||||
#define FRAME_pain413 92
|
||||
#define FRAME_pain414 93
|
||||
#define FRAME_pain415 94
|
||||
#define FRAME_pain416 95
|
||||
#define FRAME_pain417 96
|
||||
#define FRAME_run01 97
|
||||
#define FRAME_run02 98
|
||||
#define FRAME_run03 99
|
||||
#define FRAME_run04 100
|
||||
#define FRAME_run05 101
|
||||
#define FRAME_run06 102
|
||||
#define FRAME_run07 103
|
||||
#define FRAME_run08 104
|
||||
#define FRAME_run09 105
|
||||
#define FRAME_run10 106
|
||||
#define FRAME_run11 107
|
||||
#define FRAME_run12 108
|
||||
#define FRAME_runs01 109
|
||||
#define FRAME_runs02 110
|
||||
#define FRAME_runs03 111
|
||||
#define FRAME_runs04 112
|
||||
#define FRAME_runs05 113
|
||||
#define FRAME_runs06 114
|
||||
#define FRAME_runs07 115
|
||||
#define FRAME_runs08 116
|
||||
#define FRAME_runs09 117
|
||||
#define FRAME_runs10 118
|
||||
#define FRAME_runs11 119
|
||||
#define FRAME_runs12 120
|
||||
#define FRAME_runs13 121
|
||||
#define FRAME_runs14 122
|
||||
#define FRAME_runs15 123
|
||||
#define FRAME_runs16 124
|
||||
#define FRAME_runs17 125
|
||||
#define FRAME_runs18 126
|
||||
#define FRAME_runt01 127
|
||||
#define FRAME_runt02 128
|
||||
#define FRAME_runt03 129
|
||||
#define FRAME_runt04 130
|
||||
#define FRAME_runt05 131
|
||||
#define FRAME_runt06 132
|
||||
#define FRAME_runt07 133
|
||||
#define FRAME_runt08 134
|
||||
#define FRAME_runt09 135
|
||||
#define FRAME_runt10 136
|
||||
#define FRAME_runt11 137
|
||||
#define FRAME_runt12 138
|
||||
#define FRAME_runt13 139
|
||||
#define FRAME_runt14 140
|
||||
#define FRAME_runt15 141
|
||||
#define FRAME_runt16 142
|
||||
#define FRAME_runt17 143
|
||||
#define FRAME_runt18 144
|
||||
#define FRAME_runt19 145
|
||||
#define FRAME_stand101 146
|
||||
#define FRAME_stand102 147
|
||||
#define FRAME_stand103 148
|
||||
#define FRAME_stand104 149
|
||||
#define FRAME_stand105 150
|
||||
#define FRAME_stand106 151
|
||||
#define FRAME_stand107 152
|
||||
#define FRAME_stand108 153
|
||||
#define FRAME_stand109 154
|
||||
#define FRAME_stand110 155
|
||||
#define FRAME_stand111 156
|
||||
#define FRAME_stand112 157
|
||||
#define FRAME_stand113 158
|
||||
#define FRAME_stand114 159
|
||||
#define FRAME_stand115 160
|
||||
#define FRAME_stand116 161
|
||||
#define FRAME_stand117 162
|
||||
#define FRAME_stand118 163
|
||||
#define FRAME_stand119 164
|
||||
#define FRAME_stand120 165
|
||||
#define FRAME_stand121 166
|
||||
#define FRAME_stand122 167
|
||||
#define FRAME_stand123 168
|
||||
#define FRAME_stand124 169
|
||||
#define FRAME_stand125 170
|
||||
#define FRAME_stand126 171
|
||||
#define FRAME_stand127 172
|
||||
#define FRAME_stand128 173
|
||||
#define FRAME_stand129 174
|
||||
#define FRAME_stand130 175
|
||||
#define FRAME_stand301 176
|
||||
#define FRAME_stand302 177
|
||||
#define FRAME_stand303 178
|
||||
#define FRAME_stand304 179
|
||||
#define FRAME_stand305 180
|
||||
#define FRAME_stand306 181
|
||||
#define FRAME_stand307 182
|
||||
#define FRAME_stand308 183
|
||||
#define FRAME_stand309 184
|
||||
#define FRAME_stand310 185
|
||||
#define FRAME_stand311 186
|
||||
#define FRAME_stand312 187
|
||||
#define FRAME_stand313 188
|
||||
#define FRAME_stand314 189
|
||||
#define FRAME_stand315 190
|
||||
#define FRAME_stand316 191
|
||||
#define FRAME_stand317 192
|
||||
#define FRAME_stand318 193
|
||||
#define FRAME_stand319 194
|
||||
#define FRAME_stand320 195
|
||||
#define FRAME_stand321 196
|
||||
#define FRAME_stand322 197
|
||||
#define FRAME_stand323 198
|
||||
#define FRAME_stand324 199
|
||||
#define FRAME_stand325 200
|
||||
#define FRAME_stand326 201
|
||||
#define FRAME_stand327 202
|
||||
#define FRAME_stand328 203
|
||||
#define FRAME_stand329 204
|
||||
#define FRAME_stand330 205
|
||||
#define FRAME_stand331 206
|
||||
#define FRAME_stand332 207
|
||||
#define FRAME_stand333 208
|
||||
#define FRAME_stand334 209
|
||||
#define FRAME_stand335 210
|
||||
#define FRAME_stand336 211
|
||||
#define FRAME_stand337 212
|
||||
#define FRAME_stand338 213
|
||||
#define FRAME_stand339 214
|
||||
#define FRAME_walk101 215
|
||||
#define FRAME_walk102 216
|
||||
#define FRAME_walk103 217
|
||||
#define FRAME_walk104 218
|
||||
#define FRAME_walk105 219
|
||||
#define FRAME_walk106 220
|
||||
#define FRAME_walk107 221
|
||||
#define FRAME_walk108 222
|
||||
#define FRAME_walk109 223
|
||||
#define FRAME_walk110 224
|
||||
#define FRAME_walk111 225
|
||||
#define FRAME_walk112 226
|
||||
#define FRAME_walk113 227
|
||||
#define FRAME_walk114 228
|
||||
#define FRAME_walk115 229
|
||||
#define FRAME_walk116 230
|
||||
#define FRAME_walk117 231
|
||||
#define FRAME_walk118 232
|
||||
#define FRAME_walk119 233
|
||||
#define FRAME_walk120 234
|
||||
#define FRAME_walk121 235
|
||||
#define FRAME_walk122 236
|
||||
#define FRAME_walk123 237
|
||||
#define FRAME_walk124 238
|
||||
#define FRAME_walk125 239
|
||||
#define FRAME_walk126 240
|
||||
#define FRAME_walk127 241
|
||||
#define FRAME_walk128 242
|
||||
#define FRAME_walk129 243
|
||||
#define FRAME_walk130 244
|
||||
#define FRAME_walk131 245
|
||||
#define FRAME_walk132 246
|
||||
#define FRAME_walk133 247
|
||||
#define FRAME_walk201 248
|
||||
#define FRAME_walk202 249
|
||||
#define FRAME_walk203 250
|
||||
#define FRAME_walk204 251
|
||||
#define FRAME_walk205 252
|
||||
#define FRAME_walk206 253
|
||||
#define FRAME_walk207 254
|
||||
#define FRAME_walk208 255
|
||||
#define FRAME_walk209 256
|
||||
#define FRAME_walk210 257
|
||||
#define FRAME_walk211 258
|
||||
#define FRAME_walk212 259
|
||||
#define FRAME_walk213 260
|
||||
#define FRAME_walk214 261
|
||||
#define FRAME_walk215 262
|
||||
#define FRAME_walk216 263
|
||||
#define FRAME_walk217 264
|
||||
#define FRAME_walk218 265
|
||||
#define FRAME_walk219 266
|
||||
#define FRAME_walk220 267
|
||||
#define FRAME_walk221 268
|
||||
#define FRAME_walk222 269
|
||||
#define FRAME_walk223 270
|
||||
#define FRAME_walk224 271
|
||||
#define FRAME_death101 272
|
||||
#define FRAME_death102 273
|
||||
#define FRAME_death103 274
|
||||
#define FRAME_death104 275
|
||||
#define FRAME_death105 276
|
||||
#define FRAME_death106 277
|
||||
#define FRAME_death107 278
|
||||
#define FRAME_death108 279
|
||||
#define FRAME_death109 280
|
||||
#define FRAME_death110 281
|
||||
#define FRAME_death111 282
|
||||
#define FRAME_death112 283
|
||||
#define FRAME_death113 284
|
||||
#define FRAME_death114 285
|
||||
#define FRAME_death115 286
|
||||
#define FRAME_death116 287
|
||||
#define FRAME_death117 288
|
||||
#define FRAME_death118 289
|
||||
#define FRAME_death119 290
|
||||
#define FRAME_death120 291
|
||||
#define FRAME_death121 292
|
||||
#define FRAME_death122 293
|
||||
#define FRAME_death123 294
|
||||
#define FRAME_death124 295
|
||||
#define FRAME_death125 296
|
||||
#define FRAME_death126 297
|
||||
#define FRAME_death127 298
|
||||
#define FRAME_death128 299
|
||||
#define FRAME_death129 300
|
||||
#define FRAME_death130 301
|
||||
#define FRAME_death131 302
|
||||
#define FRAME_death132 303
|
||||
#define FRAME_death133 304
|
||||
#define FRAME_death134 305
|
||||
#define FRAME_death135 306
|
||||
#define FRAME_death136 307
|
||||
#define FRAME_death201 308
|
||||
#define FRAME_death202 309
|
||||
#define FRAME_death203 310
|
||||
#define FRAME_death204 311
|
||||
#define FRAME_death205 312
|
||||
#define FRAME_death206 313
|
||||
#define FRAME_death207 314
|
||||
#define FRAME_death208 315
|
||||
#define FRAME_death209 316
|
||||
#define FRAME_death210 317
|
||||
#define FRAME_death211 318
|
||||
#define FRAME_death212 319
|
||||
#define FRAME_death213 320
|
||||
#define FRAME_death214 321
|
||||
#define FRAME_death215 322
|
||||
#define FRAME_death216 323
|
||||
#define FRAME_death217 324
|
||||
#define FRAME_death218 325
|
||||
#define FRAME_death219 326
|
||||
#define FRAME_death220 327
|
||||
#define FRAME_death221 328
|
||||
#define FRAME_death222 329
|
||||
#define FRAME_death223 330
|
||||
#define FRAME_death224 331
|
||||
#define FRAME_death225 332
|
||||
#define FRAME_death226 333
|
||||
#define FRAME_death227 334
|
||||
#define FRAME_death228 335
|
||||
#define FRAME_death229 336
|
||||
#define FRAME_death230 337
|
||||
#define FRAME_death231 338
|
||||
#define FRAME_death232 339
|
||||
#define FRAME_death233 340
|
||||
#define FRAME_death234 341
|
||||
#define FRAME_death235 342
|
||||
#define FRAME_death301 343
|
||||
#define FRAME_death302 344
|
||||
#define FRAME_death303 345
|
||||
#define FRAME_death304 346
|
||||
#define FRAME_death305 347
|
||||
#define FRAME_death306 348
|
||||
#define FRAME_death307 349
|
||||
#define FRAME_death308 350
|
||||
#define FRAME_death309 351
|
||||
#define FRAME_death310 352
|
||||
#define FRAME_death311 353
|
||||
#define FRAME_death312 354
|
||||
#define FRAME_death313 355
|
||||
#define FRAME_death314 356
|
||||
#define FRAME_death315 357
|
||||
#define FRAME_death316 358
|
||||
#define FRAME_death317 359
|
||||
#define FRAME_death318 360
|
||||
#define FRAME_death319 361
|
||||
#define FRAME_death320 362
|
||||
#define FRAME_death321 363
|
||||
#define FRAME_death322 364
|
||||
#define FRAME_death323 365
|
||||
#define FRAME_death324 366
|
||||
#define FRAME_death325 367
|
||||
#define FRAME_death326 368
|
||||
#define FRAME_death327 369
|
||||
#define FRAME_death328 370
|
||||
#define FRAME_death329 371
|
||||
#define FRAME_death330 372
|
||||
#define FRAME_death331 373
|
||||
#define FRAME_death332 374
|
||||
#define FRAME_death333 375
|
||||
#define FRAME_death334 376
|
||||
#define FRAME_death335 377
|
||||
#define FRAME_death336 378
|
||||
#define FRAME_death337 379
|
||||
#define FRAME_death338 380
|
||||
#define FRAME_death339 381
|
||||
#define FRAME_death340 382
|
||||
#define FRAME_death341 383
|
||||
#define FRAME_death342 384
|
||||
#define FRAME_death343 385
|
||||
#define FRAME_death344 386
|
||||
#define FRAME_death345 387
|
||||
#define FRAME_death401 388
|
||||
#define FRAME_death402 389
|
||||
#define FRAME_death403 390
|
||||
#define FRAME_death404 391
|
||||
#define FRAME_death405 392
|
||||
#define FRAME_death406 393
|
||||
#define FRAME_death407 394
|
||||
#define FRAME_death408 395
|
||||
#define FRAME_death409 396
|
||||
#define FRAME_death410 397
|
||||
#define FRAME_death411 398
|
||||
#define FRAME_death412 399
|
||||
#define FRAME_death413 400
|
||||
#define FRAME_death414 401
|
||||
#define FRAME_death415 402
|
||||
#define FRAME_death416 403
|
||||
#define FRAME_death417 404
|
||||
#define FRAME_death418 405
|
||||
#define FRAME_death419 406
|
||||
#define FRAME_death420 407
|
||||
#define FRAME_death421 408
|
||||
#define FRAME_death422 409
|
||||
#define FRAME_death423 410
|
||||
#define FRAME_death424 411
|
||||
#define FRAME_death425 412
|
||||
#define FRAME_death426 413
|
||||
#define FRAME_death427 414
|
||||
#define FRAME_death428 415
|
||||
#define FRAME_death429 416
|
||||
#define FRAME_death430 417
|
||||
#define FRAME_death431 418
|
||||
#define FRAME_death432 419
|
||||
#define FRAME_death433 420
|
||||
#define FRAME_death434 421
|
||||
#define FRAME_death435 422
|
||||
#define FRAME_death436 423
|
||||
#define FRAME_death437 424
|
||||
#define FRAME_death438 425
|
||||
#define FRAME_death439 426
|
||||
#define FRAME_death440 427
|
||||
#define FRAME_death441 428
|
||||
#define FRAME_death442 429
|
||||
#define FRAME_death443 430
|
||||
#define FRAME_death444 431
|
||||
#define FRAME_death445 432
|
||||
#define FRAME_death446 433
|
||||
#define FRAME_death447 434
|
||||
#define FRAME_death448 435
|
||||
#define FRAME_death449 436
|
||||
#define FRAME_death450 437
|
||||
#define FRAME_death451 438
|
||||
#define FRAME_death452 439
|
||||
#define FRAME_death453 440
|
||||
#define FRAME_death501 441
|
||||
#define FRAME_death502 442
|
||||
#define FRAME_death503 443
|
||||
#define FRAME_death504 444
|
||||
#define FRAME_death505 445
|
||||
#define FRAME_death506 446
|
||||
#define FRAME_death507 447
|
||||
#define FRAME_death508 448
|
||||
#define FRAME_death509 449
|
||||
#define FRAME_death510 450
|
||||
#define FRAME_death511 451
|
||||
#define FRAME_death512 452
|
||||
#define FRAME_death513 453
|
||||
#define FRAME_death514 454
|
||||
#define FRAME_death515 455
|
||||
#define FRAME_death516 456
|
||||
#define FRAME_death517 457
|
||||
#define FRAME_death518 458
|
||||
#define FRAME_death519 459
|
||||
#define FRAME_death520 460
|
||||
#define FRAME_death521 461
|
||||
#define FRAME_death522 462
|
||||
#define FRAME_death523 463
|
||||
#define FRAME_death524 464
|
||||
#define FRAME_death601 465
|
||||
#define FRAME_death602 466
|
||||
#define FRAME_death603 467
|
||||
#define FRAME_death604 468
|
||||
#define FRAME_death605 469
|
||||
#define FRAME_death606 470
|
||||
#define FRAME_death607 471
|
||||
#define FRAME_death608 472
|
||||
#define FRAME_death609 473
|
||||
#define FRAME_death610 474
|
||||
|
||||
#define MODEL_SCALE 1.200000
|
Loading…
Reference in New Issue