mirror of
https://github.com/yquake2/yquake2remaster.git
synced 2024-11-26 06:20:48 +00:00
ctf: Add game monsters
This commit is contained in:
parent
2fe5162add
commit
d68c31e0e4
11 changed files with 2213 additions and 82 deletions
41
Makefile
41
Makefile
|
@ -1480,8 +1480,6 @@ CTF_OBJS_ = \
|
|||
src/common/shared/flash.o \
|
||||
src/common/shared/rand.o \
|
||||
src/common/shared/shared.o \
|
||||
src/game/g_newai.o \
|
||||
src/game/g_newtrig.o \
|
||||
src/ctf/g_ai.o \
|
||||
src/ctf/g_chase.o \
|
||||
src/ctf/g_cmds.o \
|
||||
|
@ -1489,11 +1487,18 @@ CTF_OBJS_ = \
|
|||
src/game/g_ctf.o \
|
||||
src/ctf/g_func.o \
|
||||
src/ctf/g_items.o \
|
||||
src/game/g_newai.o \
|
||||
src/game/g_newdm.o \
|
||||
src/game/g_newfnc.o \
|
||||
src/game/g_newtarg.o \
|
||||
src/game/g_newtrig.o \
|
||||
src/game/g_newweap.o \
|
||||
src/ctf/g_main.o \
|
||||
src/ctf/g_misc.o \
|
||||
src/ctf/g_monster.o \
|
||||
src/ctf/g_phys.o \
|
||||
src/ctf/g_save.o \
|
||||
src/game/g_sphere.o \
|
||||
src/ctf/g_spawn.o \
|
||||
src/ctf/g_svcmds.o \
|
||||
src/ctf/g_target.o \
|
||||
|
@ -1501,7 +1506,39 @@ CTF_OBJS_ = \
|
|||
src/game/g_utils.o \
|
||||
src/game/g_weapon.o \
|
||||
src/game/menu/menu.o \
|
||||
src/game/dm/ball.o \
|
||||
src/game/dm/tag.o \
|
||||
src/game/monster/berserker/berserker.o \
|
||||
src/game/monster/boss2/boss2.o \
|
||||
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/game/monster/brain/brain.o \
|
||||
src/game/monster/carrier/carrier.o \
|
||||
src/game/monster/chick/chick.o \
|
||||
src/game/monster/fixbot/fixbot.o \
|
||||
src/game/monster/flipper/flipper.o \
|
||||
src/game/monster/float/float.o \
|
||||
src/game/monster/flyer/flyer.o \
|
||||
src/game/monster/gekk/gekk.o \
|
||||
src/game/monster/gladiator/gladb.o \
|
||||
src/game/monster/gladiator/gladiator.o \
|
||||
src/game/monster/gunner/gunner.o \
|
||||
src/game/monster/hover/hover.o \
|
||||
src/game/monster/infantry/infantry.o \
|
||||
src/game/monster/insane/insane.o \
|
||||
src/game/monster/medic/medic.o \
|
||||
src/game/monster/misc/move.o \
|
||||
src/game/monster/mutant/mutant.o \
|
||||
src/game/monster/parasite/parasite.o \
|
||||
src/game/monster/soldier/soldier.o \
|
||||
src/game/monster/stalker/stalker.o \
|
||||
src/game/monster/supertank/supertank.o \
|
||||
src/game/monster/tank/tank.o \
|
||||
src/game/monster/turret/turret.o \
|
||||
src/game/monster/widow/widow2.o \
|
||||
src/game/monster/widow/widow.o \
|
||||
src/ctf/player/client.o \
|
||||
src/ctf/player/hud.o \
|
||||
src/ctf/player/trail.o \
|
||||
|
|
|
@ -811,3 +811,178 @@ T_RadiusDamage(edict_t *inflictor, edict_t *attacker, float damage,
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
T_RadiusNukeDamage(edict_t *inflictor, edict_t *attacker, float damage,
|
||||
edict_t *ignore, float radius, int mod)
|
||||
{
|
||||
float points;
|
||||
edict_t *ent = NULL;
|
||||
vec3_t v;
|
||||
vec3_t dir;
|
||||
float len;
|
||||
float killzone, killzone2;
|
||||
trace_t tr;
|
||||
float dist;
|
||||
|
||||
killzone = radius;
|
||||
killzone2 = radius * 2.0;
|
||||
|
||||
if (!inflictor || !attacker || !ignore)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
while ((ent = findradius(ent, inflictor->s.origin, killzone2)) != NULL)
|
||||
{
|
||||
/* ignore nobody */
|
||||
if (ent == ignore)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ent->takedamage)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ent->inuse)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!(ent->client || (ent->svflags & SVF_MONSTER) ||
|
||||
(ent->svflags & SVF_DAMAGEABLE)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
VectorAdd(ent->mins, ent->maxs, v);
|
||||
VectorMA(ent->s.origin, 0.5, v, v);
|
||||
VectorSubtract(inflictor->s.origin, v, v);
|
||||
len = VectorLength(v);
|
||||
|
||||
if (len <= killzone)
|
||||
{
|
||||
if (ent->client)
|
||||
{
|
||||
ent->flags |= FL_NOGIB;
|
||||
}
|
||||
|
||||
points = 10000;
|
||||
}
|
||||
else if (len <= killzone2)
|
||||
{
|
||||
points = (damage / killzone) * (killzone2 - len);
|
||||
}
|
||||
else
|
||||
{
|
||||
points = 0;
|
||||
}
|
||||
|
||||
if (points > 0)
|
||||
{
|
||||
if (ent->client)
|
||||
{
|
||||
ent->client->nuke_framenum = level.framenum + 20;
|
||||
}
|
||||
|
||||
VectorSubtract(ent->s.origin, inflictor->s.origin, dir);
|
||||
T_Damage(ent, inflictor, attacker, dir, inflictor->s.origin,
|
||||
vec3_origin, (int)points, (int)points, DAMAGE_RADIUS,
|
||||
mod);
|
||||
}
|
||||
}
|
||||
|
||||
/* skip the worldspawn */
|
||||
ent = g_edicts + 1;
|
||||
|
||||
/* cycle through players */
|
||||
while (ent)
|
||||
{
|
||||
if ((ent->client) &&
|
||||
(ent->client->nuke_framenum != level.framenum + 20) && (ent->inuse))
|
||||
{
|
||||
tr = gi.trace(inflictor->s.origin, NULL, NULL, ent->s.origin,
|
||||
inflictor, MASK_SOLID);
|
||||
|
||||
if (tr.fraction == 1.0)
|
||||
{
|
||||
ent->client->nuke_framenum = level.framenum + 20;
|
||||
}
|
||||
else
|
||||
{
|
||||
dist = realrange(ent, inflictor);
|
||||
|
||||
if (dist < 2048)
|
||||
{
|
||||
ent->client->nuke_framenum = max(ent->client->nuke_framenum,
|
||||
level.framenum + 15);
|
||||
}
|
||||
else
|
||||
{
|
||||
ent->client->nuke_framenum = max(ent->client->nuke_framenum,
|
||||
level.framenum + 10);
|
||||
}
|
||||
}
|
||||
|
||||
ent++;
|
||||
}
|
||||
else
|
||||
{
|
||||
ent = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Like T_RadiusDamage, but ignores
|
||||
* anything with classname=ignoreClass
|
||||
*/
|
||||
void
|
||||
T_RadiusClassDamage(edict_t *inflictor, edict_t *attacker, float damage,
|
||||
char *ignoreClass, float radius, int mod)
|
||||
{
|
||||
float points;
|
||||
edict_t *ent = NULL;
|
||||
vec3_t v;
|
||||
vec3_t dir;
|
||||
|
||||
if (!inflictor || !attacker || !ignoreClass)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
while ((ent = findradius(ent, inflictor->s.origin, radius)) != NULL)
|
||||
{
|
||||
if (ent->classname && !strcmp(ent->classname, ignoreClass))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ent->takedamage)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
VectorAdd(ent->mins, ent->maxs, v);
|
||||
VectorMA(ent->s.origin, 0.5, v, v);
|
||||
VectorSubtract(inflictor->s.origin, v, v);
|
||||
points = damage - 0.5 * VectorLength(v);
|
||||
|
||||
if (ent == attacker)
|
||||
{
|
||||
points = points * 0.5;
|
||||
}
|
||||
|
||||
if (points > 0)
|
||||
{
|
||||
if (CanDamage(ent, inflictor))
|
||||
{
|
||||
VectorSubtract(ent->s.origin, inflictor->s.origin, dir);
|
||||
T_Damage(ent, inflictor, attacker, dir, inflictor->s.origin,
|
||||
vec3_origin, (int)points, (int)points, DAMAGE_RADIUS,
|
||||
mod);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,8 +27,12 @@
|
|||
|
||||
#include "header/local.h"
|
||||
|
||||
#define HEALTH_IGNORE_MAX 1
|
||||
#define HEALTH_TIMED 2
|
||||
|
||||
qboolean Pickup_Weapon(edict_t *ent, edict_t *other);
|
||||
void Use_Weapon(edict_t *ent, gitem_t *inv);
|
||||
void Use_Weapon2(edict_t *ent, gitem_t *inv);
|
||||
void Drop_Weapon(edict_t *ent, gitem_t *inv);
|
||||
|
||||
void Weapon_Blaster(edict_t *ent);
|
||||
|
@ -42,10 +46,21 @@ 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);
|
||||
|
||||
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);
|
||||
|
||||
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;
|
||||
|
@ -57,6 +72,7 @@ static int power_shield_index;
|
|||
#define HEALTH_TIMED 2
|
||||
|
||||
void Use_Quad(edict_t *ent, gitem_t *item);
|
||||
void Use_QuadFire(edict_t *ent, gitem_t *item);
|
||||
|
||||
static int quad_drop_timeout_hack;
|
||||
|
||||
|
@ -1503,13 +1519,14 @@ 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)
|
||||
* QUAKED item_armor_body (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN
|
||||
*/
|
||||
{
|
||||
"item_armor_body",
|
||||
|
@ -2675,12 +2692,19 @@ gitem_t itemlist[] = {
|
|||
{NULL}
|
||||
};
|
||||
|
||||
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)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (deathmatch->value && ((int)dmflags->value & DF_NO_HEALTH))
|
||||
{
|
||||
G_FreeEdict(self);
|
||||
|
@ -2694,11 +2718,16 @@ SP_item_health(edict_t *self)
|
|||
}
|
||||
|
||||
/*
|
||||
* QUAKED item_health_small (.3 .3 1) (-16 -16 -16) (16 16 16)
|
||||
* QUAKED item_health_small (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN
|
||||
*/
|
||||
void
|
||||
SP_item_health_small(edict_t *self)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (deathmatch->value && ((int)dmflags->value & DF_NO_HEALTH))
|
||||
{
|
||||
G_FreeEdict(self);
|
||||
|
@ -2713,11 +2742,16 @@ SP_item_health_small(edict_t *self)
|
|||
}
|
||||
|
||||
/*
|
||||
* QUAKED item_health_large (.3 .3 1) (-16 -16 -16) (16 16 16)
|
||||
* QUAKED item_health_large (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN
|
||||
*/
|
||||
void
|
||||
SP_item_health_large(edict_t *self)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (deathmatch->value && ((int)dmflags->value & DF_NO_HEALTH))
|
||||
{
|
||||
G_FreeEdict(self);
|
||||
|
@ -2731,11 +2765,16 @@ SP_item_health_large(edict_t *self)
|
|||
}
|
||||
|
||||
/*
|
||||
* QUAKED item_health_mega (.3 .3 1) (-16 -16 -16) (16 16 16)
|
||||
* QUAKED item_health_mega (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN
|
||||
*/
|
||||
void
|
||||
SP_item_health_mega(edict_t *self)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (deathmatch->value && ((int)dmflags->value & DF_NO_HEALTH))
|
||||
{
|
||||
G_FreeEdict(self);
|
||||
|
@ -2774,7 +2813,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;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -2799,3 +2840,72 @@ SetItemNames(void)
|
|||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
315
src/ctf/g_misc.c
315
src/ctf/g_misc.c
|
@ -1,12 +1,37 @@
|
|||
/*
|
||||
* 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.
|
||||
*
|
||||
* =======================================================================
|
||||
*
|
||||
* Miscellaneos entities, functs and functions.
|
||||
*
|
||||
* =======================================================================
|
||||
*/
|
||||
|
||||
#include "header/local.h"
|
||||
|
||||
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.
|
||||
|
@ -15,8 +40,13 @@ int gibsthisframe;
|
|||
/* ===================================================== */
|
||||
|
||||
void
|
||||
Use_Areaportal(edict_t *ent, edict_t *other, edict_t *activator)
|
||||
Use_Areaportal(edict_t *ent, edict_t *other /* unused */, edict_t *activator /* unused */)
|
||||
{
|
||||
if (!ent)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ent->count ^= 1; /* toggle state */
|
||||
gi.SetAreaPortalState(ent->style, ent->count);
|
||||
}
|
||||
|
@ -31,8 +61,13 @@ Use_Areaportal(edict_t *ent, edict_t *other, edict_t *activator)
|
|||
void
|
||||
SP_func_areaportal(edict_t *ent)
|
||||
{
|
||||
if (!ent)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ent->use = Use_Areaportal;
|
||||
ent->count = 0; /* allways start closed; */
|
||||
ent->count = 0; /* always start closed; */
|
||||
}
|
||||
|
||||
/* ===================================================== */
|
||||
|
@ -57,6 +92,11 @@ VelocityForDamage(int damage, vec3_t v)
|
|||
void
|
||||
ClipGibVelocity(edict_t *ent)
|
||||
{
|
||||
if (!ent)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (ent->velocity[0] < -300)
|
||||
{
|
||||
ent->velocity[0] = -300;
|
||||
|
@ -85,9 +125,16 @@ ClipGibVelocity(edict_t *ent)
|
|||
}
|
||||
}
|
||||
|
||||
/* ===================================================== */
|
||||
|
||||
void
|
||||
gib_think(edict_t *self)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
self->s.frame++;
|
||||
self->nextthink = level.time + FRAMETIME;
|
||||
|
||||
|
@ -99,10 +146,15 @@ gib_think(edict_t *self)
|
|||
}
|
||||
|
||||
void
|
||||
gib_touch(edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
|
||||
gib_touch(edict_t *self, edict_t *other /* unused */, cplane_t *plane, csurface_t *surf /* unused */)
|
||||
{
|
||||
vec3_t normal_angles, right;
|
||||
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!self->groundentity)
|
||||
{
|
||||
return;
|
||||
|
@ -112,7 +164,8 @@ gib_touch(edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
|
|||
|
||||
if (plane)
|
||||
{
|
||||
gi.sound(self, CHAN_VOICE, gi.soundindex("misc/fhit3.wav"), 1, ATTN_NORM, 0);
|
||||
gi.sound(self, CHAN_VOICE, gi.soundindex(
|
||||
"misc/fhit3.wav"), 1, ATTN_NORM, 0);
|
||||
|
||||
vectoangles(plane->normal, normal_angles);
|
||||
AngleVectors(normal_angles, NULL, right, NULL);
|
||||
|
@ -128,9 +181,14 @@ gib_touch(edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
|
|||
}
|
||||
|
||||
void
|
||||
gib_die(edict_t *self, edict_t *inflictor, edict_t *attacker,
|
||||
int damage, vec3_t point)
|
||||
gib_die(edict_t *self, edict_t *inflictor /* unused */, edict_t *attacker /* unused */,
|
||||
int damage /* unused */, vec3_t point /* unused */)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
G_FreeEdict(self);
|
||||
}
|
||||
|
||||
|
@ -237,13 +295,141 @@ ThrowHead(edict_t *self, char *gibname, int damage, int type)
|
|||
gi.linkentity(self);
|
||||
}
|
||||
|
||||
void
|
||||
ThrowGibACID(edict_t *self, char *gibname, int damage, int type)
|
||||
{
|
||||
edict_t *gib;
|
||||
vec3_t vd;
|
||||
vec3_t origin;
|
||||
vec3_t size;
|
||||
float vscale;
|
||||
|
||||
if (!self || !gibname)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
gibsthisframe++;
|
||||
|
||||
if (gibsthisframe > MAX_GIBS)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
gib = G_Spawn();
|
||||
|
||||
VectorScale(self->size, 0.5, size);
|
||||
VectorAdd(self->absmin, size, origin);
|
||||
gib->s.origin[0] = origin[0] + crandom() * size[0];
|
||||
gib->s.origin[1] = origin[1] + crandom() * size[1];
|
||||
gib->s.origin[2] = origin[2] + crandom() * size[2];
|
||||
|
||||
/* gi.setmodel (gib, gibname); */
|
||||
gib->s.modelindex = gi.modelindex(gibname);
|
||||
|
||||
gib->clipmask = MASK_SHOT;
|
||||
gib->solid = SOLID_BBOX;
|
||||
|
||||
gib->s.effects |= EF_GREENGIB;
|
||||
/* note to self check this */
|
||||
gib->s.renderfx |= RF_FULLBRIGHT;
|
||||
gib->flags |= FL_NO_KNOCKBACK;
|
||||
gib->takedamage = DAMAGE_YES;
|
||||
gib->die = gib_die;
|
||||
gib->dmg = 2;
|
||||
gib->health = 250;
|
||||
|
||||
if (type == GIB_ORGANIC)
|
||||
{
|
||||
gib->movetype = MOVETYPE_TOSS;
|
||||
vscale = 3.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
gib->movetype = MOVETYPE_BOUNCE;
|
||||
vscale = 1.0;
|
||||
}
|
||||
|
||||
VelocityForDamage(damage, vd);
|
||||
VectorMA(self->velocity, vscale, vd, gib->velocity);
|
||||
ClipGibVelocity(gib);
|
||||
gib->avelocity[0] = random() * 600;
|
||||
gib->avelocity[1] = random() * 600;
|
||||
gib->avelocity[2] = random() * 600;
|
||||
|
||||
gib->think = G_FreeEdict;
|
||||
gib->nextthink = level.time + 10 + random() * 10;
|
||||
|
||||
gi.linkentity(gib);
|
||||
}
|
||||
|
||||
void
|
||||
ThrowHeadACID(edict_t *self, char *gibname, int damage, int type)
|
||||
{
|
||||
vec3_t vd;
|
||||
float vscale;
|
||||
|
||||
if (!self || !gibname)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
self->s.skinnum = 0;
|
||||
self->s.frame = 0;
|
||||
VectorClear(self->mins);
|
||||
VectorClear(self->maxs);
|
||||
|
||||
self->s.modelindex2 = 0;
|
||||
gi.setmodel(self, gibname);
|
||||
|
||||
self->clipmask = MASK_SHOT;
|
||||
self->solid = SOLID_BBOX;
|
||||
|
||||
self->s.effects |= EF_GREENGIB;
|
||||
self->s.effects &= ~EF_FLIES;
|
||||
self->s.effects |= RF_FULLBRIGHT;
|
||||
self->s.sound = 0;
|
||||
self->flags |= FL_NO_KNOCKBACK;
|
||||
self->svflags &= ~SVF_MONSTER;
|
||||
self->takedamage = DAMAGE_YES;
|
||||
self->die = gib_die;
|
||||
self->dmg = 2;
|
||||
|
||||
if (type == GIB_ORGANIC)
|
||||
{
|
||||
self->movetype = MOVETYPE_TOSS;
|
||||
vscale = 0.5;
|
||||
}
|
||||
else
|
||||
{
|
||||
self->movetype = MOVETYPE_BOUNCE;
|
||||
vscale = 1.0;
|
||||
}
|
||||
|
||||
VelocityForDamage(damage, vd);
|
||||
VectorMA(self->velocity, vscale, vd, self->velocity);
|
||||
ClipGibVelocity(self);
|
||||
|
||||
self->avelocity[YAW] = crandom() * 600;
|
||||
|
||||
self->think = G_FreeEdict;
|
||||
self->nextthink = level.time + 10 + random() * 10;
|
||||
|
||||
gi.linkentity(self);
|
||||
}
|
||||
|
||||
void
|
||||
ThrowClientHead(edict_t *self, int damage)
|
||||
{
|
||||
vec3_t vd;
|
||||
char *gibname;
|
||||
|
||||
if (rand() & 1)
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (randk() & 1)
|
||||
{
|
||||
gibname = "models/objects/gibs/head2/tris.md2";
|
||||
self->s.skinnum = 1; /* second skin is player */
|
||||
|
@ -275,14 +461,26 @@ ThrowClientHead(edict_t *self, int damage)
|
|||
self->client->anim_priority = ANIM_DEATH;
|
||||
self->client->anim_end = self->s.frame;
|
||||
}
|
||||
else
|
||||
{
|
||||
self->think = NULL;
|
||||
self->nextthink = 0;
|
||||
}
|
||||
|
||||
gi.linkentity(self);
|
||||
}
|
||||
|
||||
/* ===================================================== */
|
||||
|
||||
void
|
||||
debris_die(edict_t *self, edict_t *inflictor, edict_t *attacker,
|
||||
int damage, vec3_t point)
|
||||
debris_die(edict_t *self, edict_t *inflictor /* unused */, edict_t *attacker /* unused */,
|
||||
int damage /* unused */, vec3_t point /* unused */)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
G_FreeEdict(self);
|
||||
}
|
||||
|
||||
|
@ -325,6 +523,11 @@ ThrowDebris(edict_t *self, char *modelname, float speed, vec3_t origin)
|
|||
void
|
||||
BecomeExplosion1(edict_t *self)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* flags are important */
|
||||
if (strcmp(self->classname, "item_flag_team1") == 0)
|
||||
{
|
||||
|
@ -360,6 +563,11 @@ BecomeExplosion1(edict_t *self)
|
|||
void
|
||||
BecomeExplosion2(edict_t *self)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
gi.WriteByte(svc_temp_entity);
|
||||
gi.WriteByte(TE_EXPLOSION2);
|
||||
gi.WritePosition(self->s.origin);
|
||||
|
@ -367,21 +575,27 @@ BecomeExplosion2(edict_t *self)
|
|||
|
||||
G_FreeEdict(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
|
||||
*/
|
||||
|
||||
void
|
||||
path_corner_touch(edict_t *self, edict_t *other, cplane_t *plane,
|
||||
csurface_t *surf)
|
||||
path_corner_touch(edict_t *self, edict_t *other, cplane_t *plane /* unused */,
|
||||
csurface_t *surf /* unused */)
|
||||
{
|
||||
vec3_t v;
|
||||
edict_t *next;
|
||||
|
||||
if (!self || !other)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (other->movetarget != self)
|
||||
{
|
||||
return;
|
||||
|
@ -418,6 +632,7 @@ path_corner_touch(edict_t *self, edict_t *other, cplane_t *plane,
|
|||
v[2] -= other->mins[2];
|
||||
VectorCopy(v, other->s.origin);
|
||||
next = G_PickTarget(next->target);
|
||||
other->s.event = EV_OTHER_TELEPORT;
|
||||
}
|
||||
|
||||
other->goalentity = other->movetarget = next;
|
||||
|
@ -444,6 +659,11 @@ path_corner_touch(edict_t *self, edict_t *other, cplane_t *plane,
|
|||
void
|
||||
SP_path_corner(edict_t *self)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!self->targetname)
|
||||
{
|
||||
gi.dprintf("path_corner with no targetname at %s\n",
|
||||
|
@ -460,18 +680,26 @@ SP_path_corner(edict_t *self)
|
|||
gi.linkentity(self);
|
||||
}
|
||||
|
||||
/* ===================================================== */
|
||||
|
||||
/*
|
||||
* QUAKED point_combat (0.5 0.3 0) (-8 -8 -8) (8 8 8) Hold
|
||||
*
|
||||
* Makes this the target of a monster and it will head here
|
||||
* when first activated before going after the activator. If
|
||||
* hold is selected, it will stay here.
|
||||
*/
|
||||
void
|
||||
point_combat_touch(edict_t *self, edict_t *other, cplane_t *plane,
|
||||
csurface_t *surf)
|
||||
point_combat_touch(edict_t *self, edict_t *other, cplane_t *plane /* unused */,
|
||||
csurface_t *surf /* unused */)
|
||||
{
|
||||
edict_t *activator;
|
||||
|
||||
if (!self || !other)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (other->movetarget != self)
|
||||
{
|
||||
return;
|
||||
|
@ -484,8 +712,10 @@ point_combat_touch(edict_t *self, edict_t *other, cplane_t *plane,
|
|||
|
||||
if (!other->goalentity)
|
||||
{
|
||||
gi.dprintf("%s at %s target %s does not exist\n", self->classname,
|
||||
vtos(self->s.origin), self->target);
|
||||
gi.dprintf("%s at %s target %s does not exist\n",
|
||||
self->classname,
|
||||
vtos(self->s.origin),
|
||||
self->target);
|
||||
other->movetarget = self;
|
||||
}
|
||||
|
||||
|
@ -538,6 +768,11 @@ point_combat_touch(edict_t *self, edict_t *other, cplane_t *plane,
|
|||
void
|
||||
SP_point_combat(edict_t *self)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (deathmatch->value)
|
||||
{
|
||||
G_FreeEdict(self);
|
||||
|
@ -552,8 +787,11 @@ SP_point_combat(edict_t *self)
|
|||
gi.linkentity(self);
|
||||
}
|
||||
|
||||
/* ===================================================== */
|
||||
|
||||
/*
|
||||
* QUAKED viewthing (0 .5 .8) (-8 -8 -8) (8 8 8)
|
||||
*
|
||||
* Just for the debugging level. Don't use
|
||||
*/
|
||||
static int robotron[4];
|
||||
|
@ -577,6 +815,11 @@ TH_viewthing(edict_t *ent)
|
|||
void
|
||||
SP_viewthing(edict_t *ent)
|
||||
{
|
||||
if (!ent)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
gi.dprintf("viewthing spawned\n");
|
||||
|
||||
ent->movetype = MOVETYPE_NONE;
|
||||
|
@ -591,41 +834,60 @@ SP_viewthing(edict_t *ent)
|
|||
return;
|
||||
}
|
||||
|
||||
/* ===================================================== */
|
||||
|
||||
/*
|
||||
* QUAKED info_null (0 0.5 0) (-4 -4 -4) (4 4 4)
|
||||
*
|
||||
* Used as a positional target for spotlights, etc.
|
||||
*/
|
||||
void
|
||||
SP_info_null(edict_t *self)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
G_FreeEdict(self);
|
||||
}
|
||||
|
||||
/*
|
||||
* QUAKED info_notnull (0 0.5 0) (-4 -4 -4) (4 4 4)
|
||||
* Used as a positional target for lightning.
|
||||
*
|
||||
* Used as a positional target for lighting.
|
||||
*/
|
||||
void
|
||||
SP_info_notnull(edict_t *self)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
VectorCopy(self->s.origin, self->absmin);
|
||||
VectorCopy(self->s.origin, self->absmax);
|
||||
}
|
||||
|
||||
#define START_OFF 1
|
||||
|
||||
/*
|
||||
* 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.
|
||||
* If targeted, will toggle between on and off.
|
||||
* Default _cone value is 10 (used to set size of light for spotlights)
|
||||
*/
|
||||
|
||||
#define START_OFF 1
|
||||
|
||||
void
|
||||
light_use(edict_t *self, edict_t *other, edict_t *activator)
|
||||
light_use(edict_t *self, edict_t *other /* unused */, edict_t *activator /* unused */)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->spawnflags & START_OFF)
|
||||
{
|
||||
gi.configstring(CS_LIGHTS + self->style, "m");
|
||||
|
@ -641,6 +903,11 @@ light_use(edict_t *self, edict_t *other, edict_t *activator)
|
|||
void
|
||||
SP_light(edict_t *self)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* no targeted lights in deathmatch, because they cause global messages */
|
||||
if (!self->targetname || deathmatch->value)
|
||||
{
|
||||
|
@ -663,6 +930,8 @@ SP_light(edict_t *self)
|
|||
}
|
||||
}
|
||||
|
||||
/* ===================================================== */
|
||||
|
||||
/*
|
||||
* QUAKED func_wall (0 .5 .8) ? TRIGGER_SPAWN TOGGLE START_ON ANIMATED ANIMATED_FAST
|
||||
* This is just a solid wall if not inhibited
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
* 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
|
||||
|
@ -27,12 +28,19 @@
|
|||
|
||||
#include "header/local.h"
|
||||
|
||||
/* monster weapons */
|
||||
void monster_start_go(edict_t *self);
|
||||
|
||||
/* Monster weapons */
|
||||
|
||||
void
|
||||
monster_fire_bullet(edict_t *self, vec3_t start, vec3_t dir, int damage,
|
||||
int kick, int hspread, int vspread, int flashtype)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
fire_bullet(self, start, dir, damage, kick, hspread, vspread, MOD_UNKNOWN);
|
||||
|
||||
gi.WriteByte(svc_muzzleflash2);
|
||||
|
@ -45,6 +53,11 @@ void
|
|||
monster_fire_shotgun(edict_t *self, vec3_t start, vec3_t aimdir, int damage,
|
||||
int kick, int hspread, int vspread, int count, int flashtype)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
fire_shotgun(self, start, aimdir, damage, kick, hspread,
|
||||
vspread, count, MOD_UNKNOWN);
|
||||
|
||||
|
@ -55,9 +68,14 @@ monster_fire_shotgun(edict_t *self, vec3_t start, vec3_t aimdir, int damage,
|
|||
}
|
||||
|
||||
void
|
||||
monster_fire_blaster(edict_t *self, vec3_t start, vec3_t dir,
|
||||
int damage, int speed, int flashtype, int effect)
|
||||
monster_fire_blaster(edict_t *self, vec3_t start, vec3_t dir, int damage,
|
||||
int speed, int flashtype, int effect)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
fire_blaster(self, start, dir, damage, speed, effect, false);
|
||||
|
||||
gi.WriteByte(svc_muzzleflash2);
|
||||
|
@ -66,10 +84,251 @@ monster_fire_blaster(edict_t *self, vec3_t start, vec3_t dir,
|
|||
gi.multicast(start, MULTICAST_PVS);
|
||||
}
|
||||
|
||||
void
|
||||
monster_fire_blueblaster(edict_t *self, vec3_t start, vec3_t dir, int damage,
|
||||
int speed, int flashtype, int effect)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
fire_blueblaster(self, start, dir, damage, speed, effect);
|
||||
|
||||
gi.WriteByte(svc_muzzleflash2);
|
||||
gi.WriteShort(self - g_edicts);
|
||||
gi.WriteByte(MZ_BLUEHYPERBLASTER);
|
||||
gi.multicast(start, MULTICAST_PVS);
|
||||
}
|
||||
|
||||
void
|
||||
monster_fire_blaster2(edict_t *self, vec3_t start, vec3_t dir, int damage,
|
||||
int speed, int flashtype, int effect)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
fire_blaster2(self, start, dir, damage, speed, effect, false);
|
||||
|
||||
gi.WriteByte(svc_muzzleflash2);
|
||||
gi.WriteShort(self - g_edicts);
|
||||
gi.WriteByte(flashtype);
|
||||
gi.multicast(start, MULTICAST_PVS);
|
||||
}
|
||||
|
||||
void
|
||||
monster_fire_ionripper(edict_t *self, vec3_t start, vec3_t dir, int damage,
|
||||
int speed, int flashtype, int effect)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
fire_ionripper(self, start, dir, damage, speed, effect);
|
||||
|
||||
gi.WriteByte(svc_muzzleflash2);
|
||||
gi.WriteShort(self - g_edicts);
|
||||
gi.WriteByte(flashtype);
|
||||
gi.multicast(start, MULTICAST_PVS);
|
||||
}
|
||||
|
||||
void
|
||||
monster_fire_heat(edict_t *self, vec3_t start, vec3_t dir, int damage,
|
||||
int speed, int flashtype)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
fire_heat(self, start, dir, damage, speed, damage, damage);
|
||||
|
||||
gi.WriteByte(svc_muzzleflash2);
|
||||
gi.WriteShort(self - g_edicts);
|
||||
gi.WriteByte(flashtype);
|
||||
gi.multicast(start, MULTICAST_PVS);
|
||||
}
|
||||
|
||||
void
|
||||
monster_fire_tracker(edict_t *self, vec3_t start, vec3_t dir, int damage,
|
||||
int speed, edict_t *enemy, int flashtype)
|
||||
{
|
||||
if (!self || !enemy)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
fire_tracker(self, start, dir, damage, speed, enemy);
|
||||
|
||||
gi.WriteByte(svc_muzzleflash2);
|
||||
gi.WriteShort(self - g_edicts);
|
||||
gi.WriteByte(flashtype);
|
||||
gi.multicast(start, MULTICAST_PVS);
|
||||
}
|
||||
|
||||
void
|
||||
monster_fire_heatbeam(edict_t *self, vec3_t start, vec3_t dir, vec3_t offset,
|
||||
int damage, int kick, int flashtype)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
fire_heatbeam(self, start, dir, offset, damage, kick, true);
|
||||
|
||||
gi.WriteByte(svc_muzzleflash2);
|
||||
gi.WriteShort(self - g_edicts);
|
||||
gi.WriteByte(flashtype);
|
||||
gi.multicast(start, MULTICAST_PVS);
|
||||
}
|
||||
|
||||
void
|
||||
dabeam_hit(edict_t *self)
|
||||
{
|
||||
edict_t *ignore;
|
||||
vec3_t start;
|
||||
vec3_t end;
|
||||
trace_t tr;
|
||||
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ignore = self;
|
||||
VectorCopy(self->s.origin, start);
|
||||
VectorMA(start, 2048, self->movedir, end);
|
||||
|
||||
while (1)
|
||||
{
|
||||
tr = gi.trace(start, NULL, NULL, end, ignore,
|
||||
CONTENTS_SOLID | CONTENTS_MONSTER | CONTENTS_DEADMONSTER);
|
||||
|
||||
if (!tr.ent)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
/* hurt it if we can */
|
||||
if ((tr.ent->takedamage) && !(tr.ent->flags & FL_IMMUNE_LASER) &&
|
||||
(tr.ent != self->owner))
|
||||
{
|
||||
T_Damage(tr.ent, self, self->owner, self->movedir, tr.endpos,
|
||||
vec3_origin, self->dmg, skill->value, DAMAGE_ENERGY,
|
||||
MOD_TARGET_LASER);
|
||||
}
|
||||
|
||||
if (self->dmg < 0) /* healer ray */
|
||||
{
|
||||
/* when player is at 100 health
|
||||
just undo health fix */
|
||||
if (tr.ent->client && (tr.ent->health > 100))
|
||||
{
|
||||
tr.ent->health += self->dmg;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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 (self->spawnflags & 0x80000000)
|
||||
{
|
||||
self->spawnflags &= ~0x80000000;
|
||||
gi.WriteByte(svc_temp_entity);
|
||||
gi.WriteByte(TE_LASER_SPARKS);
|
||||
gi.WriteByte(10);
|
||||
gi.WritePosition(tr.endpos);
|
||||
gi.WriteDir(tr.plane.normal);
|
||||
gi.WriteByte(self->s.skinnum);
|
||||
gi.multicast(tr.endpos, MULTICAST_PVS);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
ignore = tr.ent;
|
||||
VectorCopy(tr.endpos, start);
|
||||
}
|
||||
|
||||
VectorCopy(tr.endpos, self->s.old_origin);
|
||||
self->nextthink = level.time + 0.1;
|
||||
self->think = G_FreeEdict;
|
||||
}
|
||||
|
||||
void
|
||||
monster_dabeam(edict_t *self)
|
||||
{
|
||||
vec3_t last_movedir;
|
||||
vec3_t point;
|
||||
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
self->movetype = MOVETYPE_NONE;
|
||||
self->solid = SOLID_NOT;
|
||||
self->s.renderfx |= RF_BEAM | RF_TRANSLUCENT;
|
||||
self->s.modelindex = 1;
|
||||
|
||||
self->s.frame = 2;
|
||||
|
||||
if (self->owner->monsterinfo.aiflags & AI_MEDIC)
|
||||
{
|
||||
self->s.skinnum = 0xf3f3f1f1;
|
||||
}
|
||||
else
|
||||
{
|
||||
self->s.skinnum = 0xf2f2f0f0;
|
||||
}
|
||||
|
||||
if (self->enemy)
|
||||
{
|
||||
VectorCopy(self->movedir, last_movedir);
|
||||
VectorMA(self->enemy->absmin, 0.5, self->enemy->size, point);
|
||||
|
||||
if (self->owner->monsterinfo.aiflags & AI_MEDIC)
|
||||
{
|
||||
point[0] += sin(level.time) * 8;
|
||||
}
|
||||
|
||||
VectorSubtract(point, self->s.origin, self->movedir);
|
||||
VectorNormalize(self->movedir);
|
||||
|
||||
if (!VectorCompare(self->movedir, last_movedir))
|
||||
{
|
||||
self->spawnflags |= 0x80000000;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
G_SetMovedir(self->s.angles, self->movedir);
|
||||
}
|
||||
|
||||
self->think = dabeam_hit;
|
||||
self->nextthink = level.time + 0.1;
|
||||
VectorSet(self->mins, -8, -8, -8);
|
||||
VectorSet(self->maxs, 8, 8, 8);
|
||||
gi.linkentity(self);
|
||||
|
||||
self->spawnflags |= 0x80000001;
|
||||
self->svflags &= ~SVF_NOCLIENT;
|
||||
}
|
||||
|
||||
void
|
||||
monster_fire_grenade(edict_t *self, vec3_t start, vec3_t aimdir,
|
||||
int damage, int speed, int flashtype)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
fire_grenade(self, start, aimdir, damage, speed, 2.5, damage + 40);
|
||||
|
||||
gi.WriteByte(svc_muzzleflash2);
|
||||
|
@ -82,6 +341,11 @@ void
|
|||
monster_fire_rocket(edict_t *self, vec3_t start, vec3_t dir,
|
||||
int damage, int speed, int flashtype)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
fire_rocket(self, start, dir, damage, speed, damage + 20, damage);
|
||||
|
||||
gi.WriteByte(svc_muzzleflash2);
|
||||
|
@ -94,7 +358,15 @@ void
|
|||
monster_fire_railgun(edict_t *self, vec3_t start, vec3_t aimdir,
|
||||
int damage, int kick, int flashtype)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(gi.pointcontents(start) & MASK_SOLID))
|
||||
{
|
||||
fire_rail(self, start, aimdir, damage, kick);
|
||||
}
|
||||
|
||||
gi.WriteByte(svc_muzzleflash2);
|
||||
gi.WriteShort(self - g_edicts);
|
||||
|
@ -104,9 +376,14 @@ monster_fire_railgun(edict_t *self, vec3_t start, vec3_t aimdir,
|
|||
|
||||
void
|
||||
monster_fire_bfg(edict_t *self, vec3_t start, vec3_t aimdir,
|
||||
int damage, int speed, int kick, float damage_radius,
|
||||
int damage, int speed, int kick /* unused */, float damage_radius,
|
||||
int flashtype)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
fire_bfg(self, start, aimdir, damage, speed, damage_radius);
|
||||
|
||||
gi.WriteByte(svc_muzzleflash2);
|
||||
|
@ -115,11 +392,18 @@ monster_fire_bfg(edict_t *self, vec3_t start, vec3_t aimdir,
|
|||
gi.multicast(start, MULTICAST_PVS);
|
||||
}
|
||||
|
||||
/* ================================================================== */
|
||||
|
||||
/* Monster utility functions */
|
||||
|
||||
void
|
||||
M_FliesOff(edict_t *self)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
self->s.effects &= ~EF_FLIES;
|
||||
self->s.sound = 0;
|
||||
}
|
||||
|
@ -127,6 +411,11 @@ M_FliesOff(edict_t *self)
|
|||
void
|
||||
M_FliesOn(edict_t *self)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->waterlevel)
|
||||
{
|
||||
return;
|
||||
|
@ -141,6 +430,11 @@ M_FliesOn(edict_t *self)
|
|||
void
|
||||
M_FlyCheck(edict_t *self)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->waterlevel)
|
||||
{
|
||||
return;
|
||||
|
@ -158,6 +452,11 @@ M_FlyCheck(edict_t *self)
|
|||
void
|
||||
AttackFinished(edict_t *self, float time)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
self->monsterinfo.attack_finished = level.time + time;
|
||||
}
|
||||
|
||||
|
@ -167,12 +466,17 @@ M_CheckGround(edict_t *ent)
|
|||
vec3_t point;
|
||||
trace_t trace;
|
||||
|
||||
if (!ent)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (ent->flags & (FL_SWIM | FL_FLY))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (ent->velocity[2] > 100)
|
||||
if ((ent->velocity[2] * ent->gravityVector[2]) < -100)
|
||||
{
|
||||
ent->groundentity = NULL;
|
||||
return;
|
||||
|
@ -182,7 +486,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);
|
||||
|
@ -209,10 +513,15 @@ M_CatagorizePosition(edict_t *ent)
|
|||
vec3_t point;
|
||||
int cont;
|
||||
|
||||
if (!ent)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* get waterlevel */
|
||||
point[0] = ent->s.origin[0];
|
||||
point[1] = ent->s.origin[1];
|
||||
point[2] = ent->s.origin[2] + ent->mins[2] + 1;
|
||||
point[0] = (ent->absmax[0] + ent->absmin[0])/2;
|
||||
point[1] = (ent->absmax[1] + ent->absmin[1])/2;
|
||||
point[2] = ent->absmin[2] + 2;
|
||||
cont = gi.pointcontents(point);
|
||||
|
||||
if (!(cont & MASK_WATER))
|
||||
|
@ -247,6 +556,11 @@ M_WorldEffects(edict_t *ent)
|
|||
{
|
||||
int dmg;
|
||||
|
||||
if (!ent)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (ent->health > 0)
|
||||
{
|
||||
if (!(ent->flags & FL_SWIM))
|
||||
|
@ -303,7 +617,8 @@ M_WorldEffects(edict_t *ent)
|
|||
{
|
||||
if (ent->flags & FL_INWATER)
|
||||
{
|
||||
gi.sound(ent, CHAN_BODY, gi.soundindex("player/watr_out.wav"), 1, ATTN_NORM, 0);
|
||||
gi.sound(ent, CHAN_BODY, gi.soundindex(
|
||||
"player/watr_out.wav"), 1, ATTN_NORM, 0);
|
||||
ent->flags &= ~FL_INWATER;
|
||||
}
|
||||
|
||||
|
@ -315,18 +630,18 @@ M_WorldEffects(edict_t *ent)
|
|||
if (ent->damage_debounce_time < level.time)
|
||||
{
|
||||
ent->damage_debounce_time = level.time + 0.2;
|
||||
T_Damage(ent, world, world, vec3_origin, ent->s.origin, vec3_origin,
|
||||
10 * ent->waterlevel, 0, 0, MOD_LAVA);
|
||||
T_Damage(ent, world, world, vec3_origin, ent->s.origin,
|
||||
vec3_origin, 10 * ent->waterlevel, 0, 0, MOD_LAVA);
|
||||
}
|
||||
}
|
||||
|
||||
if ((ent->watertype & CONTENTS_SLIME) && !(ent->flags & FL_IMMUNE_SLIME))
|
||||
if ((ent->watertype & CONTENTS_SLIME) && !(ent->flags & FL_IMMUNE_SLIME) && !(ent->svflags & SVF_DEADMONSTER))
|
||||
{
|
||||
if (ent->damage_debounce_time < level.time)
|
||||
{
|
||||
ent->damage_debounce_time = level.time + 1;
|
||||
T_Damage(ent, world, world, vec3_origin, ent->s.origin, vec3_origin,
|
||||
4 * ent->waterlevel, 0, 0, MOD_SLIME);
|
||||
T_Damage(ent, world, world, vec3_origin, ent->s.origin,
|
||||
vec3_origin, 4 * ent->waterlevel, 0, 0, MOD_SLIME);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -370,12 +685,17 @@ M_droptofloor(edict_t *ent)
|
|||
vec3_t end;
|
||||
trace_t trace;
|
||||
|
||||
if (!ent)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
trace = gi.trace(ent->s.origin, ent->mins, ent->maxs, end,
|
||||
ent, MASK_MONSTERSOLID);
|
||||
|
||||
if ((trace.fraction == 1) || trace.allsolid)
|
||||
{
|
||||
|
@ -392,8 +712,15 @@ M_droptofloor(edict_t *ent)
|
|||
void
|
||||
M_SetEffects(edict_t *ent)
|
||||
{
|
||||
ent->s.effects &= ~(EF_COLOR_SHELL | EF_POWERSCREEN);
|
||||
ent->s.renderfx &= ~(RF_SHELL_RED | RF_SHELL_GREEN | RF_SHELL_BLUE);
|
||||
int remaining;
|
||||
|
||||
if (!ent)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ent->s.effects &= ~(EF_COLOR_SHELL | EF_POWERSCREEN | EF_DOUBLE | EF_QUAD | EF_PENT);
|
||||
ent->s.renderfx &= ~(RF_SHELL_RED | RF_SHELL_GREEN | RF_SHELL_BLUE | RF_SHELL_DOUBLE);
|
||||
|
||||
if (ent->monsterinfo.aiflags & AI_RESURRECTING)
|
||||
{
|
||||
|
@ -418,6 +745,48 @@ M_SetEffects(edict_t *ent)
|
|||
ent->s.renderfx |= RF_SHELL_GREEN;
|
||||
}
|
||||
}
|
||||
|
||||
if (ent->monsterinfo.quad_framenum > level.framenum)
|
||||
{
|
||||
remaining = ent->monsterinfo.quad_framenum - level.framenum;
|
||||
|
||||
if ((remaining > 30) || (remaining & 4))
|
||||
{
|
||||
ent->s.effects |= EF_QUAD;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ent->s.effects &= ~EF_QUAD;
|
||||
}
|
||||
|
||||
if (ent->monsterinfo.double_framenum > level.framenum)
|
||||
{
|
||||
remaining = ent->monsterinfo.double_framenum - level.framenum;
|
||||
|
||||
if ((remaining > 30) || (remaining & 4))
|
||||
{
|
||||
ent->s.effects |= EF_DOUBLE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ent->s.effects &= ~EF_DOUBLE;
|
||||
}
|
||||
|
||||
if (ent->monsterinfo.invincible_framenum > level.framenum)
|
||||
{
|
||||
remaining = ent->monsterinfo.invincible_framenum - level.framenum;
|
||||
|
||||
if ((remaining > 30) || (remaining & 4))
|
||||
{
|
||||
ent->s.effects |= EF_PENT;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ent->s.effects &= ~EF_PENT;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -426,6 +795,11 @@ M_MoveFrame(edict_t *self)
|
|||
mmove_t *move;
|
||||
int index;
|
||||
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
move = self->monsterinfo.currentmove;
|
||||
self->nextthink = level.time + FRAMETIME;
|
||||
|
||||
|
@ -438,6 +812,9 @@ M_MoveFrame(edict_t *self)
|
|||
}
|
||||
else
|
||||
{
|
||||
/* prevent nextframe from leaking into a future move */
|
||||
self->monsterinfo.nextframe = 0;
|
||||
|
||||
if (self->s.frame == move->lastframe)
|
||||
{
|
||||
if (move->endfunc)
|
||||
|
@ -499,6 +876,11 @@ M_MoveFrame(edict_t *self)
|
|||
void
|
||||
monster_think(edict_t *self)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
M_MoveFrame(self);
|
||||
|
||||
if (self->linkcount != self->monsterinfo.linkcount)
|
||||
|
@ -517,8 +899,13 @@ monster_think(edict_t *self)
|
|||
* at the current activator
|
||||
*/
|
||||
void
|
||||
monster_use(edict_t *self, edict_t *other, edict_t *activator)
|
||||
monster_use(edict_t *self, edict_t *other /* unused */, edict_t *activator)
|
||||
{
|
||||
if (!self || !activator)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->enemy)
|
||||
{
|
||||
return;
|
||||
|
@ -544,11 +931,14 @@ monster_use(edict_t *self, edict_t *other, edict_t *activator)
|
|||
FoundTarget(self);
|
||||
}
|
||||
|
||||
void monster_start_go(edict_t *self);
|
||||
|
||||
void
|
||||
monster_triggered_spawn(edict_t *self)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
self->s.origin[2] += 1;
|
||||
KillBox(self);
|
||||
|
||||
|
@ -572,8 +962,13 @@ monster_triggered_spawn(edict_t *self)
|
|||
}
|
||||
|
||||
void
|
||||
monster_triggered_spawn_use(edict_t *self, edict_t *other, edict_t *activator)
|
||||
monster_triggered_spawn_use(edict_t *self, edict_t *other /* unused */, edict_t *activator)
|
||||
{
|
||||
if (!self || !activator)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* we have a one frame delay here so we
|
||||
don't telefrag the guy who activated us */
|
||||
self->think = monster_triggered_spawn;
|
||||
|
@ -590,6 +985,11 @@ monster_triggered_spawn_use(edict_t *self, edict_t *other, edict_t *activator)
|
|||
void
|
||||
monster_triggered_start(edict_t *self)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
self->solid = SOLID_NOT;
|
||||
self->movetype = MOVETYPE_NONE;
|
||||
self->svflags |= SVF_NOCLIENT;
|
||||
|
@ -598,12 +998,17 @@ monster_triggered_start(edict_t *self)
|
|||
}
|
||||
|
||||
/*
|
||||
* When a monster dies, it fires all of its
|
||||
* targets with the current enemy as activator.
|
||||
* When a monster dies, it fires all of its targets
|
||||
* with the current enemy as activator.
|
||||
*/
|
||||
void
|
||||
monster_death_use(edict_t *self)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
self->flags &= ~(FL_FLY | FL_SWIM);
|
||||
self->monsterinfo.aiflags &= AI_GOOD_GUY;
|
||||
|
||||
|
@ -626,11 +1031,16 @@ monster_death_use(edict_t *self)
|
|||
G_UseTargets(self, self->enemy);
|
||||
}
|
||||
|
||||
/* ============================================================================ */
|
||||
/* ================================================================== */
|
||||
|
||||
qboolean
|
||||
monster_start(edict_t *self)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (deathmatch->value)
|
||||
{
|
||||
G_FreeEdict(self);
|
||||
|
@ -654,7 +1064,12 @@ monster_start(edict_t *self)
|
|||
self->takedamage = DAMAGE_AIM;
|
||||
self->air_finished = level.time + 12;
|
||||
self->use = monster_use;
|
||||
|
||||
if(!self->max_health)
|
||||
{
|
||||
self->max_health = self->health;
|
||||
}
|
||||
|
||||
self->clipmask = MASK_MONSTERSOLID;
|
||||
|
||||
self->s.skinnum = 0;
|
||||
|
@ -683,10 +1098,15 @@ monster_start(edict_t *self)
|
|||
if (self->monsterinfo.currentmove)
|
||||
{
|
||||
self->s.frame = self->monsterinfo.currentmove->firstframe +
|
||||
(rand() % (self->monsterinfo.currentmove->lastframe -
|
||||
(randk() % (self->monsterinfo.currentmove->lastframe -
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -695,6 +1115,11 @@ monster_start_go(edict_t *self)
|
|||
{
|
||||
vec3_t v;
|
||||
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->health <= 0)
|
||||
{
|
||||
return;
|
||||
|
@ -796,6 +1221,11 @@ monster_start_go(edict_t *self)
|
|||
void
|
||||
walkmonster_start_go(edict_t *self)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(self->spawnflags & 2) && (level.time < 1))
|
||||
{
|
||||
M_droptofloor(self);
|
||||
|
@ -815,19 +1245,29 @@ walkmonster_start_go(edict_t *self)
|
|||
self->yaw_speed = 20;
|
||||
}
|
||||
|
||||
if (!self->viewheight)
|
||||
{
|
||||
self->viewheight = 25;
|
||||
|
||||
monster_start_go(self);
|
||||
}
|
||||
|
||||
if (self->spawnflags & 2)
|
||||
{
|
||||
monster_triggered_start(self);
|
||||
}
|
||||
else
|
||||
{
|
||||
monster_start_go(self);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
walkmonster_start(edict_t *self)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
self->think = walkmonster_start_go;
|
||||
monster_start(self);
|
||||
}
|
||||
|
@ -835,6 +1275,11 @@ walkmonster_start(edict_t *self)
|
|||
void
|
||||
flymonster_start_go(edict_t *self)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!M_walkmove(self, 0, 0))
|
||||
{
|
||||
gi.dprintf("%s in solid at %s\n", self->classname, vtos(self->s.origin));
|
||||
|
@ -845,19 +1290,29 @@ flymonster_start_go(edict_t *self)
|
|||
self->yaw_speed = 10;
|
||||
}
|
||||
|
||||
if (!self->viewheight)
|
||||
{
|
||||
self->viewheight = 25;
|
||||
|
||||
monster_start_go(self);
|
||||
}
|
||||
|
||||
if (self->spawnflags & 2)
|
||||
{
|
||||
monster_triggered_start(self);
|
||||
}
|
||||
else
|
||||
{
|
||||
monster_start_go(self);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
flymonster_start(edict_t *self)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
self->flags |= FL_FLY;
|
||||
self->think = flymonster_start_go;
|
||||
monster_start(self);
|
||||
|
@ -866,29 +1321,152 @@ flymonster_start(edict_t *self)
|
|||
void
|
||||
swimmonster_start_go(edict_t *self)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!self->yaw_speed)
|
||||
{
|
||||
self->yaw_speed = 10;
|
||||
}
|
||||
|
||||
if (!self->viewheight)
|
||||
{
|
||||
self->viewheight = 10;
|
||||
|
||||
monster_start_go(self);
|
||||
}
|
||||
|
||||
if (self->spawnflags & 2)
|
||||
{
|
||||
monster_triggered_start(self);
|
||||
}
|
||||
else
|
||||
{
|
||||
monster_start_go(self);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
swimmonster_start(edict_t *self)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
self->flags |= FL_SWIM;
|
||||
self->think = swimmonster_start_go;
|
||||
monster_start(self);
|
||||
}
|
||||
|
||||
void stationarymonster_start_go(edict_t *self);
|
||||
|
||||
void
|
||||
stationarymonster_triggered_spawn(edict_t *self)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
KillBox(self);
|
||||
|
||||
self->solid = SOLID_BBOX;
|
||||
self->movetype = MOVETYPE_NONE;
|
||||
self->svflags &= ~SVF_NOCLIENT;
|
||||
self->air_finished = level.time + 12;
|
||||
gi.linkentity(self);
|
||||
|
||||
monster_start_go(self);
|
||||
|
||||
if (self->enemy && !(self->spawnflags & 1) &&
|
||||
!(self->enemy->flags & FL_NOTARGET))
|
||||
{
|
||||
if (!(self->enemy->flags & FL_DISGUISED))
|
||||
{
|
||||
FoundTarget(self);
|
||||
}
|
||||
else
|
||||
{
|
||||
self->enemy = NULL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
self->enemy = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
stationarymonster_triggered_spawn_use(edict_t *self, edict_t *other /* unused */, edict_t *activator)
|
||||
{
|
||||
if (!self || !activator)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* we have a one frame delay here so we don't telefrag the guy who activated us */
|
||||
self->think = stationarymonster_triggered_spawn;
|
||||
self->nextthink = level.time + FRAMETIME;
|
||||
|
||||
if (activator->client)
|
||||
{
|
||||
self->enemy = activator;
|
||||
}
|
||||
|
||||
self->use = monster_use;
|
||||
}
|
||||
|
||||
void
|
||||
stationarymonster_triggered_start(edict_t *self)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
self->solid = SOLID_NOT;
|
||||
self->movetype = MOVETYPE_NONE;
|
||||
self->svflags |= SVF_NOCLIENT;
|
||||
self->nextthink = 0;
|
||||
self->use = stationarymonster_triggered_spawn_use;
|
||||
}
|
||||
|
||||
void
|
||||
stationarymonster_start_go(edict_t *self)
|
||||
{
|
||||
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!self->yaw_speed)
|
||||
{
|
||||
self->yaw_speed = 20;
|
||||
}
|
||||
|
||||
if (self->spawnflags & 2)
|
||||
{
|
||||
stationarymonster_triggered_start(self);
|
||||
}
|
||||
else
|
||||
{
|
||||
monster_start_go(self);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
stationarymonster_start(edict_t *self)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
self->think = stationarymonster_start_go;
|
||||
monster_start(self);
|
||||
}
|
||||
|
||||
void
|
||||
monster_done_dodge(edict_t *self)
|
||||
|
|
|
@ -27,6 +27,12 @@
|
|||
|
||||
#include "header/local.h"
|
||||
|
||||
#define LEG_WAIT_TIME 1
|
||||
#define MAX_LEGSFRAME 23
|
||||
|
||||
#define SPAWNGROW_LIFESPAN 0.3
|
||||
#define STEPSIZE 18
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char *name;
|
||||
|
@ -125,6 +131,7 @@ void SP_misc_easterchick2(edict_t *self);
|
|||
void SP_monster_berserk(edict_t *self);
|
||||
void SP_monster_gladiator(edict_t *self);
|
||||
void SP_monster_gunner(edict_t *self);
|
||||
void SP_monster_guncmdr(edict_t *self);
|
||||
void SP_monster_infantry(edict_t *self);
|
||||
void SP_monster_soldier_light(edict_t *self);
|
||||
void SP_monster_soldier(edict_t *self);
|
||||
|
@ -150,7 +157,7 @@ void SP_turret_breach(edict_t *self);
|
|||
void SP_turret_base(edict_t *self);
|
||||
void SP_turret_driver(edict_t *self);
|
||||
|
||||
spawn_t spawns[] = {
|
||||
static spawn_t spawns[] = {
|
||||
{"item_health", SP_item_health},
|
||||
{"item_health_small", SP_item_health_small},
|
||||
{"item_health_large", SP_item_health_large},
|
||||
|
@ -246,8 +253,39 @@ spawn_t spawns[] = {
|
|||
{NULL, NULL}
|
||||
};
|
||||
|
||||
static qboolean
|
||||
Spawn_CheckCoop_MapHacks(edict_t *ent)
|
||||
{
|
||||
if(!coop->value || !ent)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!Q_stricmp(level.mapname, "xsewer1"))
|
||||
{
|
||||
if(ent->classname && !Q_stricmp(ent->classname, "trigger_relay") && ent->target && !Q_stricmp(ent->target, "t3") && ent->targetname && !Q_stricmp(ent->targetname, "t2"))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if(ent->classname && !Q_stricmp(ent->classname, "func_button") && ent->target && !Q_stricmp(ent->target, "t16") && ent->model && !Q_stricmp(ent->model, "*71"))
|
||||
{
|
||||
ent->message = "Overflow valve maintenance\nhatch A opened.";
|
||||
return false;
|
||||
}
|
||||
|
||||
if(ent->classname && !Q_stricmp(ent->classname, "trigger_once") && ent->model && !Q_stricmp(ent->model, "*3"))
|
||||
{
|
||||
ent->message = "Overflow valve maintenance\nhatch B opened.";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Finds the spawn function for the entity and calls it
|
||||
* Finds the spawn function for
|
||||
* the entity and calls it
|
||||
*/
|
||||
void
|
||||
ED_CallSpawn(edict_t *ent)
|
||||
|
@ -256,12 +294,37 @@ ED_CallSpawn(edict_t *ent)
|
|||
gitem_t *item;
|
||||
int i;
|
||||
|
||||
if (!ent)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ent->classname)
|
||||
{
|
||||
gi.dprintf("ED_CallSpawn: NULL classname\n");
|
||||
G_FreeEdict(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++)
|
||||
{
|
||||
|
@ -760,7 +823,13 @@ SpawnEntities(const char *mapname, char *entities, const char *spawnpoint)
|
|||
SPAWNFLAG_NOT_HARD | 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);
|
||||
|
@ -908,6 +977,11 @@ char *dm_statusbar =
|
|||
void
|
||||
SP_worldspawn(edict_t *ent)
|
||||
{
|
||||
if (!ent)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ent->movetype = MOVETYPE_PUSH;
|
||||
ent->solid = SOLID_BSP;
|
||||
ent->inuse = true; /* since the world doesn't use G_Spawn() */
|
||||
|
@ -915,7 +989,8 @@ SP_worldspawn(edict_t *ent)
|
|||
|
||||
/* --------------- */
|
||||
|
||||
/* reserve some spots for dead player bodies for coop / deathmatch */
|
||||
/* reserve some spots for dead
|
||||
player bodies for coop / deathmatch */
|
||||
InitBodyQue();
|
||||
|
||||
/* set configstrings for items */
|
||||
|
@ -1055,6 +1130,7 @@ SP_worldspawn(edict_t *ent)
|
|||
gi.soundindex("misc/h2ohit1.wav"); /* landing splash */
|
||||
|
||||
gi.soundindex("items/damage.wav");
|
||||
gi.soundindex("misc/ddamage1.wav");
|
||||
gi.soundindex("items/protect.wav");
|
||||
gi.soundindex("items/protect4.wav");
|
||||
gi.soundindex("weapons/noammo.wav");
|
||||
|
@ -1069,7 +1145,8 @@ SP_worldspawn(edict_t *ent)
|
|||
gi.modelindex("models/objects/gibs/skull/tris.md2");
|
||||
gi.modelindex("models/objects/gibs/head2/tris.md2");
|
||||
|
||||
/* Setup light animation tables. 'a' is total darkness, 'z' is doublebright. */
|
||||
/* Setup light animation tables. 'a'
|
||||
is total darkness, 'z' is doublebright. */
|
||||
|
||||
/* 0 normal */
|
||||
gi.configstring(CS_LIGHTS + 0, "m");
|
||||
|
@ -1113,3 +1190,527 @@ SP_worldspawn(edict_t *ent)
|
|||
gi.configstring(CS_LIGHTS + 63, "a");
|
||||
}
|
||||
|
||||
/*
|
||||
* Monster spawning code:
|
||||
* Used by the carrier, the medic_commander, and the black widow
|
||||
*
|
||||
* The sequence to create a flying monster is:
|
||||
* FindSpawnPoint - tries to find suitable spot to spawn the monster in
|
||||
* CreateFlyMonster - this verifies the point as good and creates the monster
|
||||
*
|
||||
* To create a ground walking monster:
|
||||
* FindSpawnPoint - same thing
|
||||
* CreateGroundMonster - this checks the volume and makes sure the floor under the volume is suitable
|
||||
*/
|
||||
|
||||
edict_t *
|
||||
CreateMonster(vec3_t origin, vec3_t angles, char *classname)
|
||||
{
|
||||
edict_t *newEnt;
|
||||
|
||||
if (!classname)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
newEnt = G_Spawn();
|
||||
|
||||
VectorCopy(origin, newEnt->s.origin);
|
||||
VectorCopy(angles, newEnt->s.angles);
|
||||
newEnt->classname = ED_NewString(classname);
|
||||
newEnt->monsterinfo.aiflags |= AI_DO_NOT_COUNT;
|
||||
|
||||
VectorSet(newEnt->gravityVector, 0, 0, -1);
|
||||
ED_CallSpawn(newEnt);
|
||||
newEnt->s.renderfx |= RF_IR_VISIBLE;
|
||||
|
||||
return newEnt;
|
||||
}
|
||||
|
||||
edict_t *
|
||||
CreateFlyMonster(vec3_t origin, vec3_t angles, vec3_t mins,
|
||||
vec3_t maxs, char *classname)
|
||||
{
|
||||
if (!classname)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!mins || !maxs ||
|
||||
VectorCompare(mins, vec3_origin) || VectorCompare(maxs, vec3_origin))
|
||||
{
|
||||
DetermineBBox(classname, mins, maxs);
|
||||
}
|
||||
|
||||
if (!CheckSpawnPoint(origin, mins, maxs))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return CreateMonster(origin, angles, classname);
|
||||
}
|
||||
|
||||
edict_t *
|
||||
CreateGroundMonster(vec3_t origin, vec3_t angles, vec3_t entMins,
|
||||
vec3_t entMaxs, char *classname, int height)
|
||||
{
|
||||
edict_t *newEnt;
|
||||
vec3_t mins, maxs;
|
||||
|
||||
if (!classname)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* if they don't provide us a bounding box, figure it out */
|
||||
if (!entMins || !entMaxs || VectorCompare(entMins,
|
||||
vec3_origin) || VectorCompare(entMaxs, vec3_origin))
|
||||
{
|
||||
DetermineBBox(classname, mins, maxs);
|
||||
}
|
||||
else
|
||||
{
|
||||
VectorCopy(entMins, mins);
|
||||
VectorCopy(entMaxs, maxs);
|
||||
}
|
||||
|
||||
/* check the ground to make sure it's there, it's relatively flat, and it's not toxic */
|
||||
if (!CheckGroundSpawnPoint(origin, mins, maxs, height, -1))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
newEnt = CreateMonster(origin, angles, classname);
|
||||
|
||||
if (!newEnt)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return newEnt;
|
||||
}
|
||||
|
||||
qboolean
|
||||
FindSpawnPoint(vec3_t startpoint, vec3_t mins, vec3_t maxs,
|
||||
vec3_t spawnpoint, float maxMoveUp)
|
||||
{
|
||||
trace_t tr;
|
||||
vec3_t top;
|
||||
|
||||
tr = gi.trace(startpoint, mins, maxs, startpoint,
|
||||
NULL, MASK_MONSTERSOLID | CONTENTS_PLAYERCLIP);
|
||||
|
||||
if ((tr.startsolid || tr.allsolid) || (tr.ent != world))
|
||||
{
|
||||
VectorCopy(startpoint, top);
|
||||
top[2] += maxMoveUp;
|
||||
|
||||
tr = gi.trace(top, mins, maxs, startpoint, NULL, MASK_MONSTERSOLID);
|
||||
|
||||
if (tr.startsolid || tr.allsolid)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
VectorCopy(tr.endpos, spawnpoint);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
VectorCopy(startpoint, spawnpoint);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
qboolean
|
||||
CheckSpawnPoint(vec3_t origin, vec3_t mins, vec3_t maxs)
|
||||
{
|
||||
trace_t tr;
|
||||
|
||||
if (!mins || !maxs ||
|
||||
VectorCompare(mins, vec3_origin) || VectorCompare(maxs, vec3_origin))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
tr = gi.trace(origin, mins, maxs, origin, NULL, MASK_MONSTERSOLID);
|
||||
|
||||
if (tr.startsolid || tr.allsolid)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (tr.ent != world)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
qboolean
|
||||
CheckGroundSpawnPoint(vec3_t origin, vec3_t entMins, vec3_t entMaxs,
|
||||
float height, float gravity)
|
||||
{
|
||||
trace_t tr;
|
||||
vec3_t start, stop;
|
||||
vec3_t mins, maxs;
|
||||
int x, y;
|
||||
float mid, bottom;
|
||||
|
||||
if (!CheckSpawnPoint(origin, entMins, entMaxs))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
VectorCopy(origin, stop);
|
||||
stop[2] = origin[2] + entMins[2] - height;
|
||||
|
||||
tr = gi.trace(origin, entMins, entMaxs, stop,
|
||||
NULL, MASK_MONSTERSOLID | MASK_WATER);
|
||||
|
||||
if ((tr.fraction < 1) && (tr.contents & MASK_MONSTERSOLID))
|
||||
{
|
||||
/* first, do the midpoint trace */
|
||||
VectorAdd(tr.endpos, entMins, mins);
|
||||
VectorAdd(tr.endpos, entMaxs, maxs);
|
||||
|
||||
/* first, do the easy flat check */
|
||||
if (gravity > 0)
|
||||
{
|
||||
start[2] = maxs[2] + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
start[2] = mins[2] - 1;
|
||||
}
|
||||
|
||||
for (x = 0; x <= 1; x++)
|
||||
{
|
||||
for (y = 0; y <= 1; y++)
|
||||
{
|
||||
start[0] = x ? maxs[0] : mins[0];
|
||||
start[1] = y ? maxs[1] : mins[1];
|
||||
|
||||
if (gi.pointcontents(start) != CONTENTS_SOLID)
|
||||
{
|
||||
goto realcheck;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* if it passed all four above checks, we're done */
|
||||
return true;
|
||||
|
||||
realcheck:
|
||||
|
||||
/* check it for real */
|
||||
start[0] = stop[0] = (mins[0] + maxs[0]) * 0.5;
|
||||
start[1] = stop[1] = (mins[1] + maxs[1]) * 0.5;
|
||||
start[2] = mins[2];
|
||||
|
||||
tr = gi.trace(start, vec3_origin, vec3_origin,
|
||||
stop, NULL, MASK_MONSTERSOLID);
|
||||
|
||||
if (tr.fraction == 1.0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (gravity < 0)
|
||||
{
|
||||
start[2] = mins[2];
|
||||
stop[2] = start[2] - STEPSIZE - STEPSIZE;
|
||||
mid = bottom = tr.endpos[2] + entMins[2];
|
||||
}
|
||||
else
|
||||
{
|
||||
start[2] = maxs[2];
|
||||
stop[2] = start[2] + STEPSIZE + STEPSIZE;
|
||||
mid = bottom = tr.endpos[2] - entMaxs[2];
|
||||
}
|
||||
|
||||
for (x = 0; x <= 1; x++)
|
||||
{
|
||||
for (y = 0; y <= 1; y++)
|
||||
{
|
||||
start[0] = stop[0] = x ? maxs[0] : mins[0];
|
||||
start[1] = stop[1] = y ? maxs[1] : mins[1];
|
||||
|
||||
tr = gi.trace(start, vec3_origin, vec3_origin,
|
||||
stop, NULL, MASK_MONSTERSOLID);
|
||||
|
||||
if (gravity > 0)
|
||||
{
|
||||
if ((tr.fraction != 1.0) && (tr.endpos[2] < bottom))
|
||||
{
|
||||
bottom = tr.endpos[2];
|
||||
}
|
||||
|
||||
if ((tr.fraction == 1.0) || (tr.endpos[2] - mid > STEPSIZE))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((tr.fraction != 1.0) && (tr.endpos[2] > bottom))
|
||||
{
|
||||
bottom = tr.endpos[2];
|
||||
}
|
||||
|
||||
if ((tr.fraction == 1.0) || (mid - tr.endpos[2] > STEPSIZE))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true; /* we can land on it, it's ok */
|
||||
}
|
||||
|
||||
/* otherwise, it's either water (bad) or not
|
||||
* there (too far) if we're here, it's bad below */
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
DetermineBBox(char *classname, vec3_t mins, vec3_t maxs)
|
||||
{
|
||||
edict_t *newEnt;
|
||||
|
||||
if (!classname)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
newEnt = G_Spawn();
|
||||
|
||||
VectorCopy(vec3_origin, newEnt->s.origin);
|
||||
VectorCopy(vec3_origin, newEnt->s.angles);
|
||||
newEnt->classname = ED_NewString(classname);
|
||||
newEnt->monsterinfo.aiflags |= AI_DO_NOT_COUNT;
|
||||
|
||||
ED_CallSpawn(newEnt);
|
||||
|
||||
VectorCopy(newEnt->mins, mins);
|
||||
VectorCopy(newEnt->maxs, maxs);
|
||||
|
||||
G_FreeEdict(newEnt);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
spawngrow_think(edict_t *self)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < 2; i++)
|
||||
{
|
||||
self->s.angles[0] = rand() % 360;
|
||||
self->s.angles[1] = rand() % 360;
|
||||
self->s.angles[2] = rand() % 360;
|
||||
}
|
||||
|
||||
if ((level.time < self->wait) && (self->s.frame < 2))
|
||||
{
|
||||
self->s.frame++;
|
||||
}
|
||||
|
||||
if (level.time >= self->wait)
|
||||
{
|
||||
if (self->s.effects & EF_SPHERETRANS)
|
||||
{
|
||||
G_FreeEdict(self);
|
||||
return;
|
||||
}
|
||||
else if (self->s.frame > 0)
|
||||
{
|
||||
self->s.frame--;
|
||||
}
|
||||
else
|
||||
{
|
||||
G_FreeEdict(self);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
self->nextthink += FRAMETIME;
|
||||
}
|
||||
|
||||
void
|
||||
SpawnGrow_Spawn(vec3_t startpos, int size)
|
||||
{
|
||||
edict_t *ent;
|
||||
int i;
|
||||
float lifespan;
|
||||
|
||||
ent = G_Spawn();
|
||||
VectorCopy(startpos, ent->s.origin);
|
||||
|
||||
for (i = 0; i < 2; i++)
|
||||
{
|
||||
ent->s.angles[0] = rand() % 360;
|
||||
ent->s.angles[1] = rand() % 360;
|
||||
ent->s.angles[2] = rand() % 360;
|
||||
}
|
||||
|
||||
ent->solid = SOLID_NOT;
|
||||
ent->s.renderfx = RF_IR_VISIBLE;
|
||||
ent->movetype = MOVETYPE_NONE;
|
||||
ent->classname = "spawngro";
|
||||
|
||||
if (size <= 1)
|
||||
{
|
||||
lifespan = SPAWNGROW_LIFESPAN;
|
||||
ent->s.modelindex = gi.modelindex("models/items/spawngro2/tris.md2");
|
||||
}
|
||||
else if (size == 2)
|
||||
{
|
||||
ent->s.modelindex = gi.modelindex("models/items/spawngro3/tris.md2");
|
||||
lifespan = 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
ent->s.modelindex = gi.modelindex("models/items/spawngro/tris.md2");
|
||||
lifespan = SPAWNGROW_LIFESPAN;
|
||||
}
|
||||
|
||||
ent->think = spawngrow_think;
|
||||
|
||||
ent->wait = level.time + lifespan;
|
||||
ent->nextthink = level.time + FRAMETIME;
|
||||
|
||||
if (size != 2)
|
||||
{
|
||||
ent->s.effects |= EF_SPHERETRANS;
|
||||
}
|
||||
|
||||
gi.linkentity(ent);
|
||||
}
|
||||
|
||||
void
|
||||
widowlegs_think(edict_t *self)
|
||||
{
|
||||
vec3_t offset;
|
||||
vec3_t point;
|
||||
vec3_t f, r, u;
|
||||
|
||||
if (!self)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->s.frame == 17)
|
||||
{
|
||||
VectorSet(offset, 11.77, -7.24, 23.31);
|
||||
AngleVectors(self->s.angles, f, r, u);
|
||||
G_ProjectSource2(self->s.origin, offset, f, r, u, point);
|
||||
gi.WriteByte(svc_temp_entity);
|
||||
gi.WriteByte(TE_EXPLOSION1);
|
||||
gi.WritePosition(point);
|
||||
gi.multicast(point, MULTICAST_ALL);
|
||||
ThrowSmallStuff(self, point);
|
||||
}
|
||||
|
||||
if (self->s.frame < MAX_LEGSFRAME)
|
||||
{
|
||||
self->s.frame++;
|
||||
self->nextthink = level.time + FRAMETIME;
|
||||
return;
|
||||
}
|
||||
else if (self->wait == 0)
|
||||
{
|
||||
self->wait = level.time + LEG_WAIT_TIME;
|
||||
}
|
||||
|
||||
if (level.time > self->wait)
|
||||
{
|
||||
AngleVectors(self->s.angles, f, r, u);
|
||||
|
||||
VectorSet(offset, -65.6, -8.44, 28.59);
|
||||
G_ProjectSource2(self->s.origin, offset, f, r, u, point);
|
||||
gi.WriteByte(svc_temp_entity);
|
||||
gi.WriteByte(TE_EXPLOSION1);
|
||||
gi.WritePosition(point);
|
||||
gi.multicast(point, MULTICAST_ALL);
|
||||
ThrowSmallStuff(self, point);
|
||||
|
||||
ThrowWidowGibSized(self, "models/monsters/blackwidow/gib1/tris.md2",
|
||||
80 + (int)(random() * 20.0), GIB_METALLIC, point, 0, true);
|
||||
ThrowWidowGibSized(self, "models/monsters/blackwidow/gib2/tris.md2",
|
||||
80 + (int)(random() * 20.0), GIB_METALLIC, point, 0, true);
|
||||
|
||||
VectorSet(offset, -1.04, -51.18, 7.04);
|
||||
G_ProjectSource2(self->s.origin, offset, f, r, u, point);
|
||||
gi.WriteByte(svc_temp_entity);
|
||||
gi.WriteByte(TE_EXPLOSION1);
|
||||
gi.WritePosition(point);
|
||||
gi.multicast(point, MULTICAST_ALL);
|
||||
ThrowSmallStuff(self, point);
|
||||
|
||||
ThrowWidowGibSized(self, "models/monsters/blackwidow/gib1/tris.md2",
|
||||
80 + (int)(random() * 20.0), GIB_METALLIC, point, 0, true);
|
||||
ThrowWidowGibSized(self, "models/monsters/blackwidow/gib2/tris.md2",
|
||||
80 + (int)(random() * 20.0), GIB_METALLIC, point, 0, true);
|
||||
ThrowWidowGibSized(self, "models/monsters/blackwidow/gib3/tris.md2",
|
||||
80 + (int)(random() * 20.0), GIB_METALLIC, point, 0, true);
|
||||
|
||||
G_FreeEdict(self);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((level.time > (self->wait - 0.5)) && (self->count == 0))
|
||||
{
|
||||
self->count = 1;
|
||||
AngleVectors(self->s.angles, f, r, u);
|
||||
|
||||
VectorSet(offset, 31, -88.7, 10.96);
|
||||
G_ProjectSource2(self->s.origin, offset, f, r, u, point);
|
||||
gi.WriteByte(svc_temp_entity);
|
||||
gi.WriteByte(TE_EXPLOSION1);
|
||||
gi.WritePosition(point);
|
||||
gi.multicast(point, MULTICAST_ALL);
|
||||
|
||||
VectorSet(offset, -12.67, -4.39, 15.68);
|
||||
G_ProjectSource2(self->s.origin, offset, f, r, u, point);
|
||||
gi.WriteByte(svc_temp_entity);
|
||||
gi.WriteByte(TE_EXPLOSION1);
|
||||
gi.WritePosition(point);
|
||||
gi.multicast(point, MULTICAST_ALL);
|
||||
|
||||
self->nextthink = level.time + FRAMETIME;
|
||||
return;
|
||||
}
|
||||
|
||||
self->nextthink = level.time + FRAMETIME;
|
||||
}
|
||||
|
||||
void
|
||||
Widowlegs_Spawn(vec3_t startpos, vec3_t angles)
|
||||
{
|
||||
edict_t *ent;
|
||||
|
||||
ent = G_Spawn();
|
||||
VectorCopy(startpos, ent->s.origin);
|
||||
VectorCopy(angles, ent->s.angles);
|
||||
ent->solid = SOLID_NOT;
|
||||
ent->s.renderfx = RF_IR_VISIBLE;
|
||||
ent->movetype = MOVETYPE_NONE;
|
||||
ent->classname = "widowlegs";
|
||||
|
||||
ent->s.modelindex = gi.modelindex("models/monsters/legs/tris.md2");
|
||||
ent->think = widowlegs_think;
|
||||
|
||||
ent->nextthink = level.time + FRAMETIME;
|
||||
gi.linkentity(ent);
|
||||
}
|
||||
|
|
|
@ -29,11 +29,77 @@
|
|||
#include "../monster/misc/player.h"
|
||||
#include <limits.h>
|
||||
|
||||
#define PLAYER_NOISE_SELF 0
|
||||
#define PLAYER_NOISE_IMPACT 1
|
||||
|
||||
#define FRAME_FIRE_FIRST (FRAME_ACTIVATE_LAST + 1)
|
||||
#define FRAME_IDLE_FIRST (FRAME_FIRE_LAST + 1)
|
||||
#define FRAME_DEACTIVATE_FIRST (FRAME_IDLE_LAST + 1)
|
||||
|
||||
#define GRENADE_TIMER 3.0
|
||||
#define GRENADE_MINSPEED 400
|
||||
#define GRENADE_MAXSPEED 800
|
||||
|
||||
#define CHAINFIST_REACH 64
|
||||
|
||||
#define HEATBEAM_DM_DMG 15
|
||||
#define HEATBEAM_SP_DMG 15
|
||||
|
||||
#define TRAP_TIMER 5.0
|
||||
#define TRAP_MINSPEED 300
|
||||
#define TRAP_MAXSPEED 700
|
||||
|
||||
static qboolean is_quad;
|
||||
static qboolean is_quadfire;
|
||||
static byte damage_multiplier;
|
||||
static byte is_silenced;
|
||||
|
||||
void weapon_grenade_fire(edict_t *ent, qboolean held);
|
||||
void weapon_trap_fire(edict_t *ent, qboolean held);
|
||||
|
||||
byte
|
||||
P_DamageModifier(edict_t *ent)
|
||||
{
|
||||
is_quad = 0;
|
||||
damage_multiplier = 1;
|
||||
|
||||
if (!ent)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ent->client->quad_framenum > level.framenum)
|
||||
{
|
||||
damage_multiplier *= 4;
|
||||
is_quad = 1;
|
||||
|
||||
/* if we're quad and DF_NO_STACK_DOUBLE is on, return now. */
|
||||
if (((int)(dmflags->value) & DF_NO_STACK_DOUBLE))
|
||||
{
|
||||
return damage_multiplier;
|
||||
}
|
||||
}
|
||||
|
||||
if (ent->client->double_framenum > level.framenum)
|
||||
{
|
||||
if ((deathmatch->value) || (damage_multiplier == 1))
|
||||
{
|
||||
damage_multiplier *= 2;
|
||||
is_quad = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (ent->client->quadfire_framenum > level.framenum)
|
||||
{
|
||||
if ((deathmatch->value) || (damage_multiplier == 1))
|
||||
{
|
||||
damage_multiplier *= 2;
|
||||
is_quadfire = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return damage_multiplier;
|
||||
}
|
||||
|
||||
void
|
||||
P_ProjectSource(edict_t *ent, vec3_t distance,
|
||||
|
@ -77,6 +143,47 @@ P_ProjectSource(edict_t *ent, vec3_t distance,
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
P_ProjectSource2(edict_t *ent, vec3_t point, vec3_t distance, vec3_t forward,
|
||||
vec3_t right, vec3_t up, vec3_t result)
|
||||
{
|
||||
gclient_t *client = ent->client;
|
||||
vec3_t _distance;
|
||||
|
||||
if (!client)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
VectorCopy(distance, _distance);
|
||||
|
||||
if (client->pers.hand == LEFT_HANDED)
|
||||
{
|
||||
_distance[1] *= -1;
|
||||
}
|
||||
else if (client->pers.hand == CENTER_HANDED)
|
||||
{
|
||||
_distance[1] = 0;
|
||||
}
|
||||
|
||||
G_ProjectSource2(point, _distance, forward, right, up, result);
|
||||
|
||||
// Berserker: fix - now the projectile hits exactly where the scope is pointing.
|
||||
if (aimfix->value)
|
||||
{
|
||||
vec3_t start, end;
|
||||
VectorSet(start, ent->s.origin[0], ent->s.origin[1], ent->s.origin[2] + ent->viewheight);
|
||||
VectorMA(start, 8192, forward, end);
|
||||
|
||||
trace_t tr = gi.trace(start, NULL, NULL, end, ent, MASK_SHOT);
|
||||
if (tr.fraction < 1)
|
||||
{
|
||||
VectorSubtract(tr.endpos, result, forward);
|
||||
VectorNormalize(forward);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Each player can have two noise objects associated with it:
|
||||
* a personal noise (jumping, pain, weapon firing), and a weapon
|
||||
|
@ -85,11 +192,115 @@ P_ProjectSource(edict_t *ent, vec3_t distance,
|
|||
* Monsters that don't directly see the player can move
|
||||
* to a noise in hopes of seeing the player from there.
|
||||
*/
|
||||
static edict_t *
|
||||
PlayerNoise_Spawn(edict_t *who, int type)
|
||||
{
|
||||
edict_t *noise;
|
||||
|
||||
if (!who)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
noise = G_SpawnOptional();
|
||||
if (!noise)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
noise->classname = "player_noise";
|
||||
noise->spawnflags = type;
|
||||
VectorSet (noise->mins, -8, -8, -8);
|
||||
VectorSet (noise->maxs, 8, 8, 8);
|
||||
noise->owner = who;
|
||||
noise->svflags = SVF_NOCLIENT;
|
||||
|
||||
return noise;
|
||||
}
|
||||
|
||||
static void
|
||||
PlayerNoise_Verify(edict_t *who)
|
||||
{
|
||||
edict_t *e;
|
||||
edict_t *n1;
|
||||
edict_t *n2;
|
||||
|
||||
if (!who)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
n1 = who->mynoise;
|
||||
n2 = who->mynoise2;
|
||||
|
||||
if (n1 && !n1->inuse)
|
||||
{
|
||||
n1 = NULL;
|
||||
}
|
||||
|
||||
if (n2 && !n2->inuse)
|
||||
{
|
||||
n2 = NULL;
|
||||
}
|
||||
|
||||
if (n1 && n2)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (e = g_edicts + 1 + game.maxclients; e < &g_edicts[globals.num_edicts]; e++)
|
||||
{
|
||||
if (!e->inuse || strcmp(e->classname, "player_noise") != 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (e->owner && e->owner != who)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
e->owner = who;
|
||||
|
||||
if (!n2 && (e->spawnflags == PLAYER_NOISE_IMPACT || n1))
|
||||
{
|
||||
n2 = e;
|
||||
}
|
||||
else
|
||||
{
|
||||
n1 = e;
|
||||
}
|
||||
|
||||
if (n1 && n2)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!n1)
|
||||
{
|
||||
n1 = PlayerNoise_Spawn(who, PLAYER_NOISE_SELF);
|
||||
}
|
||||
|
||||
if (!n2)
|
||||
{
|
||||
n2 = PlayerNoise_Spawn(who, PLAYER_NOISE_IMPACT);
|
||||
}
|
||||
|
||||
who->mynoise = n1;
|
||||
who->mynoise2 = n2;
|
||||
}
|
||||
|
||||
void
|
||||
PlayerNoise(edict_t *who, vec3_t where, int type)
|
||||
{
|
||||
edict_t *noise;
|
||||
|
||||
if (!who || !who->client)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == PNOISE_WEAPON)
|
||||
{
|
||||
if (who->client->silencer_shots)
|
||||
|
@ -154,6 +365,11 @@ Pickup_Weapon(edict_t *ent, edict_t *other)
|
|||
int index;
|
||||
gitem_t *ammo;
|
||||
|
||||
if (!ent || !other)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
index = ITEM_INDEX(ent->item);
|
||||
|
||||
if ((((int)(dmflags->value) & DF_WEAPONS_STAY) || coop->value) &&
|
||||
|
@ -198,6 +414,7 @@ Pickup_Weapon(edict_t *ent, edict_t *other)
|
|||
if (coop->value)
|
||||
{
|
||||
ent->flags |= FL_RESPAWN;
|
||||
ent->flags |= FL_COOP_TAKEN;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -222,6 +439,11 @@ ChangeWeapon(edict_t *ent)
|
|||
{
|
||||
int i;
|
||||
|
||||
if (!ent)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (ent->client->grenade_time)
|
||||
{
|
||||
ent->client->grenade_time = level.time;
|
||||
|
@ -262,6 +484,7 @@ ChangeWeapon(edict_t *ent)
|
|||
|
||||
if (!ent->client->pers.weapon)
|
||||
{
|
||||
/* dead */
|
||||
ent->client->ps.gunindex = 0;
|
||||
return;
|
||||
}
|
||||
|
@ -288,6 +511,11 @@ ChangeWeapon(edict_t *ent)
|
|||
void
|
||||
NoAmmoWeaponChange(edict_t *ent)
|
||||
{
|
||||
if (!ent)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (ent->client->pers.inventory[ITEM_INDEX(FindItem("slugs"))] &&
|
||||
ent->client->pers.inventory[ITEM_INDEX(FindItem("railgun"))])
|
||||
{
|
||||
|
@ -295,6 +523,27 @@ NoAmmoWeaponChange(edict_t *ent)
|
|||
return;
|
||||
}
|
||||
|
||||
if ((ent->client->pers.inventory[ITEM_INDEX(FindItem("cells"))] >= 2) &&
|
||||
ent->client->pers.inventory[ITEM_INDEX(FindItem("Plasma Beam"))])
|
||||
{
|
||||
ent->client->newweapon = FindItem("Plasma Beam");
|
||||
return;
|
||||
}
|
||||
|
||||
if (ent->client->pers.inventory[ITEM_INDEX(FindItem("flechettes"))] &&
|
||||
ent->client->pers.inventory[ITEM_INDEX(FindItem("etf rifle"))])
|
||||
{
|
||||
ent->client->newweapon = FindItem("etf rifle");
|
||||
return;
|
||||
}
|
||||
|
||||
if (ent->client->pers.inventory[ITEM_INDEX(FindItem("cells"))] > 1 &&
|
||||
ent->client->pers.inventory[ITEM_INDEX(FindItem("ionripper"))])
|
||||
{
|
||||
ent->client->newweapon = FindItem("ionripper");
|
||||
return;
|
||||
}
|
||||
|
||||
if (ent->client->pers.inventory[ITEM_INDEX(FindItem("cells"))] &&
|
||||
ent->client->pers.inventory[ITEM_INDEX(FindItem("hyperblaster"))])
|
||||
{
|
||||
|
@ -339,6 +588,11 @@ NoAmmoWeaponChange(edict_t *ent)
|
|||
void
|
||||
Think_Weapon(edict_t *ent)
|
||||
{
|
||||
if (!ent)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* if just died, put the weapon away */
|
||||
if (ent->health < 1)
|
||||
{
|
||||
|
@ -373,6 +627,11 @@ Use_Weapon(edict_t *ent, gitem_t *item)
|
|||
int ammo_index;
|
||||
gitem_t *ammo_item;
|
||||
|
||||
if (!ent || !item)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* see if we're already using it */
|
||||
if (item == ent->client->pers.weapon)
|
||||
{
|
||||
|
@ -403,11 +662,100 @@ Use_Weapon(edict_t *ent, gitem_t *item)
|
|||
ent->client->newweapon = item;
|
||||
}
|
||||
|
||||
void
|
||||
Use_Weapon2(edict_t *ent, gitem_t *item)
|
||||
{
|
||||
int ammo_index;
|
||||
gitem_t *ammo_item;
|
||||
gitem_t *nextitem;
|
||||
int index;
|
||||
|
||||
if (!ent || !item)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcmp(item->pickup_name, "HyperBlaster") == 0)
|
||||
{
|
||||
if (item == ent->client->pers.weapon)
|
||||
{
|
||||
item = FindItem("Ionripper");
|
||||
index = ITEM_INDEX(item);
|
||||
|
||||
if (!ent->client->pers.inventory[index])
|
||||
{
|
||||
item = FindItem("HyperBlaster");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if (strcmp(item->pickup_name, "Railgun") == 0)
|
||||
{
|
||||
ammo_item = FindItem(item->ammo);
|
||||
ammo_index = ITEM_INDEX(ammo_item);
|
||||
|
||||
if (!ent->client->pers.inventory[ammo_index])
|
||||
{
|
||||
nextitem = FindItem("Phalanx");
|
||||
ammo_item = FindItem(nextitem->ammo);
|
||||
ammo_index = ITEM_INDEX(ammo_item);
|
||||
|
||||
if (ent->client->pers.inventory[ammo_index])
|
||||
{
|
||||
item = FindItem("Phalanx");
|
||||
index = ITEM_INDEX(item);
|
||||
|
||||
if (!ent->client->pers.inventory[index])
|
||||
{
|
||||
item = FindItem("Railgun");
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (item == ent->client->pers.weapon)
|
||||
{
|
||||
item = FindItem("Phalanx");
|
||||
index = ITEM_INDEX(item);
|
||||
|
||||
if (!ent->client->pers.inventory[index])
|
||||
{
|
||||
item = FindItem("Railgun");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* see if we're already using it */
|
||||
if (item == ent->client->pers.weapon)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (item->ammo)
|
||||
{
|
||||
ammo_item = FindItem(item->ammo);
|
||||
ammo_index = ITEM_INDEX(ammo_item);
|
||||
|
||||
if (!ent->client->pers.inventory[ammo_index] && !g_select_empty->value)
|
||||
{
|
||||
gi.cprintf(ent, PRINT_HIGH, "No %s for %s.\n",
|
||||
ammo_item->pickup_name, item->pickup_name);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* change to this weapon when down */
|
||||
ent->client->newweapon = item;
|
||||
}
|
||||
|
||||
void
|
||||
Drop_Weapon(edict_t *ent, gitem_t *item)
|
||||
{
|
||||
int index;
|
||||
|
||||
if (!ent || !item)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ((int)(dmflags->value) & DF_WEAPONS_STAY)
|
||||
{
|
||||
return;
|
||||
|
|
|
@ -20,7 +20,8 @@
|
|||
*
|
||||
* =======================================================================
|
||||
*
|
||||
* Monster utility functions.
|
||||
* Monster utility functions. While unused by the CTF code they must
|
||||
* persist here since most of the other game codes has ties to it.
|
||||
*
|
||||
* =======================================================================
|
||||
*/
|
||||
|
|
|
@ -91,6 +91,7 @@ void SP_target_crosslevel_trigger(edict_t *ent);
|
|||
void SP_target_crosslevel_target(edict_t *ent);
|
||||
void SP_target_laser(edict_t *self);
|
||||
void SP_target_help(edict_t *ent);
|
||||
void SP_target_actor(edict_t *ent);
|
||||
void SP_target_lightramp(edict_t *self);
|
||||
void SP_target_earthquake(edict_t *ent);
|
||||
void SP_target_music(edict_t *ent);
|
||||
|
@ -112,6 +113,7 @@ void SP_point_combat(edict_t *self);
|
|||
void SP_misc_explobox(edict_t *self);
|
||||
void SP_misc_banner(edict_t *self);
|
||||
void SP_misc_satellite_dish(edict_t *self);
|
||||
void SP_misc_actor(edict_t *self);
|
||||
void SP_misc_gib_arm(edict_t *self);
|
||||
void SP_misc_gib_leg(edict_t *self);
|
||||
void SP_misc_gib_head(edict_t *self);
|
||||
|
@ -1309,6 +1311,7 @@ SP_worldspawn(edict_t *ent)
|
|||
gi.modelindex("#w_hyperblaster.md2");
|
||||
gi.modelindex("#w_railgun.md2");
|
||||
gi.modelindex("#w_bfg.md2");
|
||||
gi.modelindex("#w_grapple.md2");
|
||||
gi.modelindex("#w_disrupt.md2");
|
||||
gi.modelindex("#w_etfrifle.md2");
|
||||
gi.modelindex("#w_plasma.md2");
|
||||
|
|
|
@ -760,7 +760,7 @@ G_SpawnOptional(void)
|
|||
}
|
||||
|
||||
e = &g_edicts[globals.num_edicts++];
|
||||
G_InitEdict (e);
|
||||
G_InitEdict(e);
|
||||
|
||||
return e;
|
||||
}
|
||||
|
@ -893,6 +893,14 @@ G_TouchSolids(edict_t *ent)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* ==============================================================================
|
||||
*
|
||||
* Kill box
|
||||
*
|
||||
* ==============================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
* Kills all entities that would touch the
|
||||
* proposed new positioning of ent. Ent s
|
||||
|
|
|
@ -440,7 +440,8 @@ Pickup_Weapon(edict_t *ent, edict_t *other)
|
|||
|
||||
if ((other->client->pers.weapon != ent->item) &&
|
||||
(other->client->pers.inventory[index] == 1) &&
|
||||
(!deathmatch->value || (other->client->pers.weapon == FindItem("blaster"))))
|
||||
(!deathmatch->value ||
|
||||
(other->client->pers.weapon == FindItem("blaster"))))
|
||||
{
|
||||
other->client->newweapon = ent->item;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue