#include "g_local.h"
void Use_Areaportal(edict_t *ent, edict_t *other, edict_t *activator) {
ent->count ^= 1; gi.SetAreaPortalState(ent->s.cmodel_index, ent->style, ent->count);
}
void SP_func_areaportal(edict_t *ent) {
ent->use = Use_Areaportal;
ent->count = 0; }
void VelocityForDamage(int damage, vec3_t v) {
v[0] = 100.0 * crandom();
v[1] = 100.0 * crandom();
v[2] = 200.0 + 100.0 * random();
if(damage < 50)
VectorScale(v, 0.7, v);
else
VectorScale(v, 1.2, v);
}
void ClipGibVelocity(edict_t *ent) {
if(ent->velocity[0] < -300)
ent->velocity[0] = -300;
else if(ent->velocity[0] > 300)
ent->velocity[0] = 300;
if(ent->velocity[1] < -300)
ent->velocity[1] = -300;
else if(ent->velocity[1] > 300)
ent->velocity[1] = 300;
if(ent->velocity[2] < 200)
ent->velocity[2] = 200; else if(ent->velocity[2] > 500)
ent->velocity[2] = 500;
}
void gib_think(edict_t *self) {
self->s.frame++;
self->nextthink = level.time + FRAMETIME;
if(self->s.frame == 10) {
self->think = G_FreeEdict;
self->nextthink = level.time + 8 + random() * 10;
}
}
void gib_touch(edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf) {
vec3_t normal_angles, right;
if(!self->groundentity)
return;
self->touch = NULL;
if(plane) {
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);
vectoangles(right, self->s.angles);
if(self->s.modelindex == sm_meat_index) {
self->s.frame++;
self->think = gib_think;
self->nextthink = level.time + FRAMETIME;
}
}
}
void gib_die(edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point) { G_FreeEdict(self); }
void ThrowGib(edict_t *self, char *gibname, int damage, int type) {
edict_t *gib;
vec3_t vd;
vec3_t origin;
vec3_t size;
float vscale;
gib = G_Spawn(self->s.cmodel_index);
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->solid = SOLID_NOT;
gib->s.effects |= EF_GIB;
gib->flags |= FL_NO_KNOCKBACK;
gib->takedamage = DAMAGE_YES;
gib->die = gib_die;
if(type == GIB_ORGANIC) {
gib->movetype = MOVETYPE_TOSS;
gib->touch = gib_touch;
vscale = 0.5;
} 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 ThrowHead(edict_t *self, char *gibname, int damage, int type) {
vec3_t vd;
float vscale;
self->s.skinnum = 0;
self->s.frame = 0;
VectorClear(self->mins);
VectorClear(self->maxs);
self->s.modelindex2 = 0;
gi.setmodel(self, gibname);
self->solid = SOLID_NOT;
self->s.effects |= EF_GIB;
self->s.effects &= ~EF_FLIES;
self->s.sound = 0;
self->flags |= FL_NO_KNOCKBACK;
self->svflags &= ~SVF_MONSTER;
self->takedamage = DAMAGE_YES;
self->die = gib_die;
if(type == GIB_ORGANIC) {
self->movetype = MOVETYPE_TOSS;
self->touch = gib_touch;
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) {
gibname = "models/objects/gibs/head2/tris.md2";
self->s.skinnum = 1; } else {
gibname = "models/objects/gibs/skull/tris.md2";
self->s.skinnum = 0;
}
self->s.origin[2] += 32;
self->s.frame = 0;
gi.setmodel(self, gibname);
VectorSet(self->mins, -16, -16, 0);
VectorSet(self->maxs, 16, 16, 16);
self->takedamage = DAMAGE_NO;
self->solid = SOLID_NOT;
self->s.effects = EF_GIB;
self->s.sound = 0;
self->flags |= FL_NO_KNOCKBACK;
self->movetype = MOVETYPE_BOUNCE;
VelocityForDamage(damage, vd);
VectorAdd(self->velocity, vd, self->velocity);
if(self->client) {
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) { G_FreeEdict(self); }
void ThrowDebris(edict_t *self, char *modelname, float speed, vec3_t origin) {
edict_t *chunk;
vec3_t v;
chunk = G_Spawn(self->s.cmodel_index);
VectorCopy(origin, chunk->s.origin);
gi.setmodel(chunk, modelname);
v[0] = 100 * crandom();
v[1] = 100 * crandom();
v[2] = 100 + 100 * crandom();
VectorMA(self->velocity, speed, v, chunk->velocity);
chunk->movetype = MOVETYPE_BOUNCE;
chunk->solid = SOLID_NOT;
chunk->avelocity[0] = random() * 600;
chunk->avelocity[1] = random() * 600;
chunk->avelocity[2] = random() * 600;
chunk->think = G_FreeEdict;
chunk->nextthink = level.time + 5 + random() * 5;
chunk->s.frame = 0;
chunk->flags = 0;
chunk->classname = "debris";
chunk->takedamage = DAMAGE_YES;
chunk->die = debris_die;
gi.linkentity(chunk);
}
void BecomeExplosion1(edict_t *self) {
gi.WriteByte(svc_temp_entity);
gi.WriteByte(TE_EXPLOSION1);
gi.WritePosition(self->s.origin);
gi.multicast(self->s.cmodel_index, self->s.origin, MULTICAST_PVS);
G_FreeEdict(self);
}
void BecomeExplosion2(edict_t *self) {
gi.WriteByte(svc_temp_entity);
gi.WriteByte(TE_EXPLOSION2);
gi.WritePosition(self->s.origin);
gi.multicast(self->s.cmodel_index, self->s.origin, MULTICAST_PVS);
G_FreeEdict(self);
}
void path_corner_touch(edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf) {
vec3_t v;
edict_t *next;
if(other->movetarget != self)
return;
if(other->enemy)
return;
if(self->pathtarget) {
char *savetarget;
savetarget = self->target;
self->target = self->pathtarget;
G_UseTargets(self, other);
self->target = savetarget;
}
if(self->target)
next = G_PickTarget(self->s.cmodel_index, self->target);
else
next = NULL;
if((next) && (next->spawnflags & 1)) {
VectorCopy(next->s.origin, v);
v[2] += next->mins[2];
v[2] -= other->mins[2];
VectorCopy(v, other->s.origin);
next = G_PickTarget(self->s.cmodel_index, next->target);
other->s.event = EV_OTHER_TELEPORT;
}
other->goalentity = other->movetarget = next;
if(self->wait) {
other->monsterinfo.pausetime = level.time + self->wait;
other->monsterinfo.stand(other);
return;
}
if(!other->movetarget) {
other->monsterinfo.pausetime = level.time + 100000000;
other->monsterinfo.stand(other);
} else {
VectorSubtract(other->goalentity->s.origin, other->s.origin, v);
other->ideal_yaw = vectoyaw(v);
}
}
void SP_path_corner(edict_t *self) {
if(!self->targetname) {
gi.dprintf("path_corner with no targetname at %s\n", vtos(self->s.origin));
G_FreeEdict(self);
return;
}
self->solid = SOLID_TRIGGER;
self->touch = path_corner_touch;
VectorSet(self->mins, -8, -8, -8);
VectorSet(self->maxs, 8, 8, 8);
self->svflags |= SVF_NOCLIENT;
gi.linkentity(self);
}
void point_combat_touch(edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf) {
edict_t *activator;
if(other->movetarget != self)
return;
if(self->target) {
other->target = self->target;
other->goalentity = other->movetarget = G_PickTarget(self->s.cmodel_index, other->target);
if(!other->goalentity) {
gi.dprintf("%s at %s target %s does not exist\n", self->classname, vtos(self->s.origin), self->target);
other->movetarget = self;
}
self->target = NULL;
} else if((self->spawnflags & 1) && !(other->flags & (FL_SWIM | FL_FLY))) {
other->monsterinfo.pausetime = level.time + 100000000;
other->monsterinfo.aiflags |= AI_STAND_GROUND;
other->monsterinfo.stand(other);
}
if(other->movetarget == self) {
other->target = NULL;
other->movetarget = NULL;
other->goalentity = other->enemy;
other->monsterinfo.aiflags &= ~AI_COMBAT_POINT;
}
if(self->pathtarget) {
char *savetarget;
savetarget = self->target;
self->target = self->pathtarget;
if(other->enemy && other->enemy->client)
activator = other->enemy;
else if(other->oldenemy && other->oldenemy->client)
activator = other->oldenemy;
else if(other->activator && other->activator->client)
activator = other->activator;
else
activator = other;
G_UseTargets(self, activator);
self->target = savetarget;
}
}
void SP_point_combat(edict_t *self) {
if(deathmatch->value) {
G_FreeEdict(self);
return;
}
self->solid = SOLID_TRIGGER;
self->touch = point_combat_touch;
VectorSet(self->mins, -8, -8, -16);
VectorSet(self->maxs, 8, 8, 16);
self->svflags = SVF_NOCLIENT;
gi.linkentity(self);
};
void TH_viewthing(edict_t *ent) {
ent->s.frame = (ent->s.frame + 1) % 7;
ent->nextthink = level.time + FRAMETIME;
}
void SP_viewthing(edict_t *ent) {
gi.dprintf("viewthing spawned\n");
ent->movetype = MOVETYPE_NONE;
ent->solid = SOLID_BBOX;
ent->s.renderfx = RF_FRAMELERP;
VectorSet(ent->mins, -16, -16, -24);
VectorSet(ent->maxs, 16, 16, 32);
ent->s.modelindex = gi.modelindex("models/objects/banner/tris.md2");
gi.linkentity(ent);
ent->nextthink = level.time + 0.5;
ent->think = TH_viewthing;
return;
}
void SP_info_null(edict_t *self) { G_FreeEdict(self); };
void SP_info_notnull(edict_t *self) {
VectorCopy(self->s.origin, self->absmin);
VectorCopy(self->s.origin, self->absmax);
};
#define START_OFF 1
static void light_use(edict_t *self, edict_t *other, edict_t *activator) {
if(self->spawnflags & START_OFF) {
gi.configstring(CS_LIGHTS + self->style, "m");
self->spawnflags &= ~START_OFF;
} else {
gi.configstring(CS_LIGHTS + self->style, "a");
self->spawnflags |= START_OFF;
}
}
void SP_light(edict_t *self) {
if(!self->targetname || deathmatch->value) {
G_FreeEdict(self);
return;
}
if(self->style >= 32) {
self->use = light_use;
if(self->spawnflags & START_OFF)
gi.configstring(CS_LIGHTS + self->style, "a");
else
gi.configstring(CS_LIGHTS + self->style, "m");
}
}
void func_wall_use(edict_t *self, edict_t *other, edict_t *activator) {
if(self->solid == SOLID_NOT) {
self->solid = SOLID_BSP;
self->svflags &= ~SVF_NOCLIENT;
KillBox(self);
} else {
self->solid = SOLID_NOT;
self->svflags |= SVF_NOCLIENT;
}
gi.linkentity(self);
if(!(self->spawnflags & 2))
self->use = NULL;
}
void SP_func_wall(edict_t *self) {
self->movetype = MOVETYPE_PUSH;
gi.setmodel(self, self->model);
if(self->spawnflags & 8)
self->s.effects |= EF_ANIM_ALL;
if(self->spawnflags & 16)
self->s.effects |= EF_ANIM_ALLFAST;
if((self->spawnflags & 7) == 0) {
self->solid = SOLID_BSP;
gi.linkentity(self);
return;
}
if(!(self->spawnflags & 1)) {
self->spawnflags |= 1;
}
if(self->spawnflags & 4) {
if(!(self->spawnflags & 2)) {
gi.dprintf("func_wall START_ON without TOGGLE\n");
self->spawnflags |= 2;
}
}
self->use = func_wall_use;
if(self->spawnflags & 4) {
self->solid = SOLID_BSP;
} else {
self->solid = SOLID_NOT;
self->svflags |= SVF_NOCLIENT;
}
gi.linkentity(self);
}
void func_object_touch(edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf) {
if(!plane)
return;
if(plane->normal[2] < 1.0)
return;
if(other->takedamage == DAMAGE_NO)
return;
T_Damage(other, self, self, vec3_origin, self->s.origin, vec3_origin, self->dmg, 1, 0, MOD_CRUSH);
}
void func_object_release(edict_t *self) {
self->movetype = MOVETYPE_TOSS;
self->touch = func_object_touch;
}
void func_object_use(edict_t *self, edict_t *other, edict_t *activator) {
self->solid = SOLID_BSP;
self->svflags &= ~SVF_NOCLIENT;
self->use = NULL;
KillBox(self);
func_object_release(self);
}
void SP_func_object(edict_t *self) {
gi.setmodel(self, self->model);
self->mins[0] += 1;
self->mins[1] += 1;
self->mins[2] += 1;
self->maxs[0] -= 1;
self->maxs[1] -= 1;
self->maxs[2] -= 1;
if(!self->dmg)
self->dmg = 100;
if(self->spawnflags == 0) {
self->solid = SOLID_BSP;
self->movetype = MOVETYPE_PUSH;
self->think = func_object_release;
self->nextthink = level.time + 2 * FRAMETIME;
} else {
self->solid = SOLID_NOT;
self->movetype = MOVETYPE_PUSH;
self->use = func_object_use;
self->svflags |= SVF_NOCLIENT;
}
if(self->spawnflags & 2)
self->s.effects |= EF_ANIM_ALL;
if(self->spawnflags & 4)
self->s.effects |= EF_ANIM_ALLFAST;
self->clipmask = MASK_MONSTERSOLID;
gi.linkentity(self);
}
void func_explosive_explode(edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point) {
vec3_t origin;
vec3_t chunkorigin;
vec3_t size;
int count;
int mass;
VectorScale(self->size, 0.5, size);
VectorAdd(self->absmin, size, origin);
VectorCopy(origin, self->s.origin);
self->takedamage = DAMAGE_NO;
if(self->dmg)
T_RadiusDamage(self, attacker, self->dmg, NULL, self->dmg + 40, MOD_EXPLOSIVE);
VectorSubtract(self->s.origin, inflictor->s.origin, self->velocity);
VectorNormalize(self->velocity);
VectorScale(self->velocity, 150, self->velocity);
VectorScale(size, 0.5, size);
mass = self->mass;
if(!mass)
mass = 75;
if(mass >= 100) {
count = mass / 100;
if(count > 8)
count = 8;
while(count--) {
chunkorigin[0] = origin[0] + crandom() * size[0];
chunkorigin[1] = origin[1] + crandom() * size[1];
chunkorigin[2] = origin[2] + crandom() * size[2];
ThrowDebris(self, "models/objects/debris1/tris.md2", 1, chunkorigin);
}
}
count = mass / 25;
if(count > 16)
count = 16;
while(count--) {
chunkorigin[0] = origin[0] + crandom() * size[0];
chunkorigin[1] = origin[1] + crandom() * size[1];
chunkorigin[2] = origin[2] + crandom() * size[2];
ThrowDebris(self, "models/objects/debris2/tris.md2", 2, chunkorigin);
}
G_UseTargets(self, attacker);
if(self->dmg)
BecomeExplosion1(self);
else
G_FreeEdict(self);
}
void func_explosive_use(edict_t *self, edict_t *other, edict_t *activator) {
func_explosive_explode(self, self, other, ent_read_health(self)->value, vec3_origin);
}
void func_explosive_spawn(edict_t *self, edict_t *other, edict_t *activator) {
self->solid = SOLID_BSP;
self->svflags &= ~SVF_NOCLIENT;
self->use = NULL;
KillBox(self);
gi.linkentity(self);
}
void SP_func_explosive(edict_t *self) {
if(deathmatch->value) { G_FreeEdict(self);
return;
}
self->movetype = MOVETYPE_PUSH;
gi.modelindex("models/objects/debris1/tris.md2");
gi.modelindex("models/objects/debris2/tris.md2");
gi.setmodel(self, self->model);
if(self->spawnflags & 1) {
self->svflags |= SVF_NOCLIENT;
self->solid = SOLID_NOT;
self->use = func_explosive_spawn;
} else {
self->solid = SOLID_BSP;
if(self->targetname)
self->use = func_explosive_use;
}
if(self->spawnflags & 2)
self->s.effects |= EF_ANIM_ALL;
if(self->spawnflags & 4)
self->s.effects |= EF_ANIM_ALLFAST;
if(self->use != func_explosive_use) {
if(!ent_read_health(self)->value)
dv_set(ent_write_health(self), 100);
self->die = func_explosive_explode;
self->takedamage = DAMAGE_YES;
}
gi.linkentity(self);
}
void barrel_touch(edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
{
float ratio;
vec3_t v;
if((!other->groundentity) || (other->groundentity == self))
return;
ratio = (float)other->mass / (float)self->mass;
VectorSubtract(self->s.origin, other->s.origin, v);
M_walkmove(self, vectoyaw(v), 20 * ratio * FRAMETIME);
}
void barrel_explode(edict_t *self) {
vec3_t org;
float spd;
vec3_t save;
T_RadiusDamage(self, self->activator, self->dmg, NULL, self->dmg + 40, MOD_BARREL);
VectorCopy(self->s.origin, save);
VectorMA(self->absmin, 0.5, self->size, self->s.origin);
spd = 1.5 * (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];
ThrowDebris(self, "models/objects/debris1/tris.md2", spd, org);
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];
ThrowDebris(self, "models/objects/debris1/tris.md2", spd, org);
spd = 1.75 * (float)self->dmg / 200.0;
VectorCopy(self->absmin, org);
ThrowDebris(self, "models/objects/debris3/tris.md2", spd, org);
VectorCopy(self->absmin, org);
org[0] += self->size[0];
ThrowDebris(self, "models/objects/debris3/tris.md2", spd, org);
VectorCopy(self->absmin, org);
org[1] += self->size[1];
ThrowDebris(self, "models/objects/debris3/tris.md2", spd, org);
VectorCopy(self->absmin, org);
org[0] += self->size[0];
org[1] += self->size[1];
ThrowDebris(self, "models/objects/debris3/tris.md2", spd, org);
spd = 2 * self->dmg / 200;
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];
ThrowDebris(self, "models/objects/debris2/tris.md2", spd, org);
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];
ThrowDebris(self, "models/objects/debris2/tris.md2", spd, org);
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];
ThrowDebris(self, "models/objects/debris2/tris.md2", spd, org);
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];
ThrowDebris(self, "models/objects/debris2/tris.md2", spd, org);
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];
ThrowDebris(self, "models/objects/debris2/tris.md2", spd, org);
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];
ThrowDebris(self, "models/objects/debris2/tris.md2", spd, org);
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];
ThrowDebris(self, "models/objects/debris2/tris.md2", spd, org);
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];
ThrowDebris(self, "models/objects/debris2/tris.md2", spd, org);
VectorCopy(save, self->s.origin);
if(self->groundentity)
BecomeExplosion2(self);
else
BecomeExplosion1(self);
}
void barrel_delay(edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point) {
self->takedamage = DAMAGE_NO;
self->nextthink = level.time + 2 * FRAMETIME;
self->think = barrel_explode;
self->activator = attacker;
}
void SP_misc_explobox(edict_t *self) {
if(deathmatch->value) { G_FreeEdict(self);
return;
}
gi.modelindex("models/objects/debris1/tris.md2");
gi.modelindex("models/objects/debris2/tris.md2");
gi.modelindex("models/objects/debris3/tris.md2");
self->solid = SOLID_BBOX;
self->movetype = MOVETYPE_STEP;
self->model = "models/objects/barrels/tris.md2";
self->s.modelindex = gi.modelindex(self->model);
VectorSet(self->mins, -16, -16, 0);
VectorSet(self->maxs, 16, 16, 40);
if(!self->mass)
self->mass = 400;
if(!ent_read_health(self)->value)
dv_set(ent_write_health(self), 10);
if(!self->dmg)
self->dmg = 150;
self->die = barrel_delay;
self->takedamage = DAMAGE_YES;
self->monsterinfo.aiflags = AI_NOSTEP;
self->touch = barrel_touch;
self->think = M_droptofloor;
self->nextthink = level.time + 2 * FRAMETIME;
gi.linkentity(self);
}
void misc_blackhole_use(edict_t *ent, edict_t *other, edict_t *activator) {
G_FreeEdict(ent);
}
void misc_blackhole_think(edict_t *self) {
if(++self->s.frame < 19)
self->nextthink = level.time + FRAMETIME;
else {
self->s.frame = 0;
self->nextthink = level.time + FRAMETIME;
}
}
void SP_misc_blackhole(edict_t *ent) {
ent->movetype = MOVETYPE_NONE;
ent->solid = SOLID_NOT;
VectorSet(ent->mins, -64, -64, 0);
VectorSet(ent->maxs, 64, 64, 8);
ent->s.modelindex = gi.modelindex("models/objects/black/tris.md2");
ent->s.renderfx = RF_TRANSLUCENT;
ent->use = misc_blackhole_use;
ent->think = misc_blackhole_think;
ent->nextthink = level.time + 2 * FRAMETIME;
gi.linkentity(ent);
}
void misc_eastertank_think(edict_t *self) {
if(++self->s.frame < 293)
self->nextthink = level.time + FRAMETIME;
else {
self->s.frame = 254;
self->nextthink = level.time + FRAMETIME;
}
}
void SP_misc_eastertank(edict_t *ent) {
ent->movetype = MOVETYPE_NONE;
ent->solid = SOLID_BBOX;
VectorSet(ent->mins, -32, -32, -16);
VectorSet(ent->maxs, 32, 32, 32);
ent->s.modelindex = gi.modelindex("models/monsters/tank/tris.md2");
ent->s.frame = 254;
ent->think = misc_eastertank_think;
ent->nextthink = level.time + 2 * FRAMETIME;
gi.linkentity(ent);
}
void misc_easterchick_think(edict_t *self) {
if(++self->s.frame < 247)
self->nextthink = level.time + FRAMETIME;
else {
self->s.frame = 208;
self->nextthink = level.time + FRAMETIME;
}
}
void SP_misc_easterchick(edict_t *ent) {
ent->movetype = MOVETYPE_NONE;
ent->solid = SOLID_BBOX;
VectorSet(ent->mins, -32, -32, 0);
VectorSet(ent->maxs, 32, 32, 32);
ent->s.modelindex = gi.modelindex("models/monsters/bitch/tris.md2");
ent->s.frame = 208;
ent->think = misc_easterchick_think;
ent->nextthink = level.time + 2 * FRAMETIME;
gi.linkentity(ent);
}
void misc_easterchick2_think(edict_t *self) {
if(++self->s.frame < 287)
self->nextthink = level.time + FRAMETIME;
else {
self->s.frame = 248;
self->nextthink = level.time + FRAMETIME;
}
}
void SP_misc_easterchick2(edict_t *ent) {
ent->movetype = MOVETYPE_NONE;
ent->solid = SOLID_BBOX;
VectorSet(ent->mins, -32, -32, 0);
VectorSet(ent->maxs, 32, 32, 32);
ent->s.modelindex = gi.modelindex("models/monsters/bitch/tris.md2");
ent->s.frame = 248;
ent->think = misc_easterchick2_think;
ent->nextthink = level.time + 2 * FRAMETIME;
gi.linkentity(ent);
}
void commander_body_think(edict_t *self) {
if(++self->s.frame < 24)
self->nextthink = level.time + FRAMETIME;
else
self->nextthink = 0;
if(self->s.frame == 22)
gi.sound(self, CHAN_BODY, gi.soundindex("tank/thud.wav"), 1, ATTN_NORM, 0);
}
void commander_body_use(edict_t *self, edict_t *other, edict_t *activator) {
self->think = commander_body_think;
self->nextthink = level.time + FRAMETIME;
gi.sound(self, CHAN_BODY, gi.soundindex("tank/pain.wav"), 1, ATTN_NORM, 0);
}
void commander_body_drop(edict_t *self) {
self->movetype = MOVETYPE_TOSS;
self->s.origin[2] += 2;
}
void SP_monster_commander_body(edict_t *self) {
self->movetype = MOVETYPE_NONE;
self->solid = SOLID_BBOX;
self->model = "models/monsters/commandr/tris.md2";
self->s.modelindex = gi.modelindex(self->model);
VectorSet(self->mins, -32, -32, 0);
VectorSet(self->maxs, 32, 32, 48);
self->use = commander_body_use;
self->takedamage = DAMAGE_YES;
self->flags = FL_GODMODE;
self->s.renderfx |= RF_FRAMELERP;
gi.linkentity(self);
gi.soundindex("tank/thud.wav");
gi.soundindex("tank/pain.wav");
self->think = commander_body_drop;
self->nextthink = level.time + 5 * FRAMETIME;
}
void misc_banner_think(edict_t *ent) {
ent->s.frame = (ent->s.frame + 1) % 16;
ent->nextthink = level.time + FRAMETIME;
}
void SP_misc_banner(edict_t *ent) {
ent->movetype = MOVETYPE_NONE;
ent->solid = SOLID_NOT;
ent->s.modelindex = gi.modelindex("models/objects/banner/tris.md2");
ent->s.frame = rand() % 16;
gi.linkentity(ent);
ent->think = misc_banner_think;
ent->nextthink = level.time + FRAMETIME;
}
void misc_deadsoldier_die(edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point) {
int n;
if(ent_read_health(self)->value > -80)
return;
gi.sound(self, CHAN_BODY, gi.soundindex("misc/udeath.wav"), 1, ATTN_NORM, 0);
for(n = 0; n < 4; n++)
ThrowGib(self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC);
ThrowHead(self, "models/objects/gibs/head2/tris.md2", damage, GIB_ORGANIC);
}
void SP_misc_deadsoldier(edict_t *ent) {
if(deathmatch->value) { G_FreeEdict(ent);
return;
}
ent->movetype = MOVETYPE_NONE;
ent->solid = SOLID_BBOX;
ent->s.modelindex = gi.modelindex("models/deadbods/dude/tris.md2");
if(ent->spawnflags & 2)
ent->s.frame = 1;
else if(ent->spawnflags & 4)
ent->s.frame = 2;
else if(ent->spawnflags & 8)
ent->s.frame = 3;
else if(ent->spawnflags & 16)
ent->s.frame = 4;
else if(ent->spawnflags & 32)
ent->s.frame = 5;
else
ent->s.frame = 0;
VectorSet(ent->mins, -16, -16, 0);
VectorSet(ent->maxs, 16, 16, 16);
ent->deadflag = DEAD_DEAD;
ent->takedamage = DAMAGE_YES;
ent->svflags |= SVF_MONSTER | SVF_DEADMONSTER;
ent->die = misc_deadsoldier_die;
ent->monsterinfo.aiflags |= AI_GOOD_GUY;
gi.linkentity(ent);
}
extern void train_use(edict_t *self, edict_t *other, edict_t *activator);
extern void func_train_find(edict_t *self);
void misc_viper_use(edict_t *self, edict_t *other, edict_t *activator) {
self->svflags &= ~SVF_NOCLIENT;
self->use = train_use;
train_use(self, other, activator);
}
void SP_misc_viper(edict_t *ent) {
if(!ent->target) {
gi.dprintf("misc_viper without a target at %s\n", vtos(ent->absmin));
G_FreeEdict(ent);
return;
}
if(!ent->speed)
ent->speed = 300;
ent->movetype = MOVETYPE_PUSH;
ent->solid = SOLID_NOT;
ent->s.modelindex = gi.modelindex("models/ships/viper/tris.md2");
VectorSet(ent->mins, -16, -16, 0);
VectorSet(ent->maxs, 16, 16, 32);
ent->think = func_train_find;
ent->nextthink = level.time + FRAMETIME;
ent->use = misc_viper_use;
ent->svflags |= SVF_NOCLIENT;
ent->moveinfo.accel = ent->moveinfo.decel = ent->moveinfo.speed = ent->speed;
gi.linkentity(ent);
}
void SP_misc_bigviper(edict_t *ent) {
ent->movetype = MOVETYPE_NONE;
ent->solid = SOLID_BBOX;
VectorSet(ent->mins, -176, -120, -24);
VectorSet(ent->maxs, 176, 120, 72);
ent->s.modelindex = gi.modelindex("models/ships/bigviper/tris.md2");
gi.linkentity(ent);
}
void misc_viper_bomb_touch(edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf) {
G_UseTargets(self, self->activator);
self->s.origin[2] = self->absmin[2] + 1;
T_RadiusDamage(self, self, self->dmg, NULL, self->dmg + 40, MOD_BOMB);
BecomeExplosion2(self);
}
void misc_viper_bomb_prethink(edict_t *self) {
vec3_t v;
float diff;
self->groundentity = NULL;
diff = self->timestamp - level.time;
if(diff < -1.0)
diff = -1.0;
VectorScale(self->moveinfo.dir, 1.0 + diff, v);
v[2] = diff;
diff = self->s.angles[2];
vectoangles(v, self->s.angles);
self->s.angles[2] = diff + 10;
}
void misc_viper_bomb_use(edict_t *self, edict_t *other, edict_t *activator) {
edict_t *viper;
self->solid = SOLID_BBOX;
self->svflags &= ~SVF_NOCLIENT;
self->s.effects |= EF_ROCKET;
self->use = NULL;
self->movetype = MOVETYPE_TOSS;
self->prethink = misc_viper_bomb_prethink;
self->touch = misc_viper_bomb_touch;
self->activator = activator;
viper = G_Find(self->s.cmodel_index, NULL, FOFS(classname), "misc_viper");
VectorScale(viper->moveinfo.dir, viper->moveinfo.speed, self->velocity);
self->timestamp = level.time;
VectorCopy(viper->moveinfo.dir, self->moveinfo.dir);
}
void SP_misc_viper_bomb(edict_t *self) {
self->movetype = MOVETYPE_NONE;
self->solid = SOLID_NOT;
VectorSet(self->mins, -8, -8, -8);
VectorSet(self->maxs, 8, 8, 8);
self->s.modelindex = gi.modelindex("models/objects/bomb/tris.md2");
if(!self->dmg)
self->dmg = 1000;
self->use = misc_viper_bomb_use;
self->svflags |= SVF_NOCLIENT;
gi.linkentity(self);
}
extern void train_use(edict_t *self, edict_t *other, edict_t *activator);
extern void func_train_find(edict_t *self);
void misc_strogg_ship_use(edict_t *self, edict_t *other, edict_t *activator) {
self->svflags &= ~SVF_NOCLIENT;
self->use = train_use;
train_use(self, other, activator);
}
void SP_misc_strogg_ship(edict_t *ent) {
if(!ent->target) {
gi.dprintf("%s without a target at %s\n", ent->classname, vtos(ent->absmin));
G_FreeEdict(ent);
return;
}
if(!ent->speed)
ent->speed = 300;
ent->movetype = MOVETYPE_PUSH;
ent->solid = SOLID_NOT;
ent->s.modelindex = gi.modelindex("models/ships/strogg1/tris.md2");
VectorSet(ent->mins, -16, -16, 0);
VectorSet(ent->maxs, 16, 16, 32);
ent->think = func_train_find;
ent->nextthink = level.time + FRAMETIME;
ent->use = misc_strogg_ship_use;
ent->svflags |= SVF_NOCLIENT;
ent->moveinfo.accel = ent->moveinfo.decel = ent->moveinfo.speed = ent->speed;
gi.linkentity(ent);
}
void misc_satellite_dish_think(edict_t *self) {
self->s.frame++;
if(self->s.frame < 38)
self->nextthink = level.time + FRAMETIME;
}
void misc_satellite_dish_use(edict_t *self, edict_t *other, edict_t *activator) {
self->s.frame = 0;
self->think = misc_satellite_dish_think;
self->nextthink = level.time + FRAMETIME;
}
void SP_misc_satellite_dish(edict_t *ent) {
ent->movetype = MOVETYPE_NONE;
ent->solid = SOLID_BBOX;
VectorSet(ent->mins, -64, -64, 0);
VectorSet(ent->maxs, 64, 64, 128);
ent->s.modelindex = gi.modelindex("models/objects/satellite/tris.md2");
ent->use = misc_satellite_dish_use;
gi.linkentity(ent);
}
void 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");
gi.linkentity(ent);
}
void 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");
gi.linkentity(ent);
}
void SP_misc_gib_arm(edict_t *ent) {
gi.setmodel(ent, "models/objects/gibs/arm/tris.md2");
ent->solid = SOLID_NOT;
ent->s.effects |= EF_GIB;
ent->takedamage = DAMAGE_YES;
ent->die = gib_die;
ent->movetype = MOVETYPE_TOSS;
ent->svflags |= SVF_MONSTER;
ent->deadflag = DEAD_DEAD;
ent->avelocity[0] = random() * 200;
ent->avelocity[1] = random() * 200;
ent->avelocity[2] = random() * 200;
ent->think = G_FreeEdict;
ent->nextthink = level.time + 30;
gi.linkentity(ent);
}
void SP_misc_gib_leg(edict_t *ent) {
gi.setmodel(ent, "models/objects/gibs/leg/tris.md2");
ent->solid = SOLID_NOT;
ent->s.effects |= EF_GIB;
ent->takedamage = DAMAGE_YES;
ent->die = gib_die;
ent->movetype = MOVETYPE_TOSS;
ent->svflags |= SVF_MONSTER;
ent->deadflag = DEAD_DEAD;
ent->avelocity[0] = random() * 200;
ent->avelocity[1] = random() * 200;
ent->avelocity[2] = random() * 200;
ent->think = G_FreeEdict;
ent->nextthink = level.time + 30;
gi.linkentity(ent);
}
void SP_misc_gib_head(edict_t *ent) {
gi.setmodel(ent, "models/objects/gibs/head/tris.md2");
ent->solid = SOLID_NOT;
ent->s.effects |= EF_GIB;
ent->takedamage = DAMAGE_YES;
ent->die = gib_die;
ent->movetype = MOVETYPE_TOSS;
ent->svflags |= SVF_MONSTER;
ent->deadflag = DEAD_DEAD;
ent->avelocity[0] = random() * 200;
ent->avelocity[1] = random() * 200;
ent->avelocity[2] = random() * 200;
ent->think = G_FreeEdict;
ent->nextthink = level.time + 30;
gi.linkentity(ent);
}
void SP_target_character(edict_t *self) {
self->movetype = MOVETYPE_PUSH;
gi.setmodel(self, self->model);
self->solid = SOLID_BSP;
self->s.frame = 12;
gi.linkentity(self);
return;
}
void target_string_use(edict_t *self, edict_t *other, edict_t *activator) {
edict_t *e;
int n, l;
char c;
l = strlen(self->message);
for(e = self->teammaster; e; e = e->teamchain) {
if(!e->count)
continue;
n = e->count - 1;
if(n > l) {
e->s.frame = 12;
continue;
}
c = self->message[n];
if(c >= '0' && c <= '9')
e->s.frame = c - '0';
else if(c == '-')
e->s.frame = 10;
else if(c == ':')
e->s.frame = 11;
else
e->s.frame = 12;
}
}
void SP_target_string(edict_t *self) {
if(!self->message)
self->message = "";
self->use = target_string_use;
}
#define CLOCK_MESSAGE_SIZE 16
static void func_clock_reset(edict_t *self) {
self->activator = NULL;
if(self->spawnflags & 1) {
dv_set(ent_write_health(self), 0);
self->wait = self->count;
} else if(self->spawnflags & 2) {
dv_set(ent_write_health(self), self->count);
self->wait = 0;
}
}
static void func_clock_format_countdown(edict_t *self) {
if(self->style == 0) {
Com_sprintf(self->message, CLOCK_MESSAGE_SIZE, "%2i", ent_read_health(self)->value);
return;
}
if(self->style == 1) {
Com_sprintf(self->message, CLOCK_MESSAGE_SIZE, "%2i:%2i", ent_read_health(self)->value / 60,
(int)(ent_read_health(self)->value) % 60);
if(self->message[3] == ' ')
self->message[3] = '0';
return;
}
if(self->style == 2) {
Com_sprintf(self->message, CLOCK_MESSAGE_SIZE, "%2i:%2i:%2i", ent_read_health(self)->value / 3600,
(ent_read_health(self)->value - (ent_read_health(self)->value / 3600) * 3600) / 60,
(int)(ent_read_health(self)->value) % 60);
if(self->message[3] == ' ')
self->message[3] = '0';
if(self->message[6] == ' ')
self->message[6] = '0';
return;
}
}
void func_clock_think(edict_t *self) {
if(!self->enemy) {
self->enemy = G_Find(self->s.cmodel_index, NULL, FOFS(targetname), self->target);
if(!self->enemy)
return;
}
if(self->spawnflags & 1) {
func_clock_format_countdown(self);
dv_adjust(ent_write_health(self), level.time, 1, 0);
} else if(self->spawnflags & 2) {
func_clock_format_countdown(self);
dv_adjust(ent_write_health(self), level.time, -1, 0);
} else {
struct tm *ltime;
time_t gmtime;
time(&gmtime);
ltime = localtime(&gmtime);
Com_sprintf(self->message, CLOCK_MESSAGE_SIZE, "%2i:%2i:%2i", ltime->tm_hour, ltime->tm_min, ltime->tm_sec);
if(self->message[3] == ' ')
self->message[3] = '0';
if(self->message[6] == ' ')
self->message[6] = '0';
}
self->enemy->message = self->message;
self->enemy->use(self->enemy, self, self);
if(((self->spawnflags & 1) && (ent_read_health(self)->value > self->wait)) ||
((self->spawnflags & 2) && (ent_read_health(self)->value < self->wait))) {
if(self->pathtarget) {
char *savetarget;
char *savemessage;
savetarget = self->target;
savemessage = self->message;
self->target = self->pathtarget;
self->message = NULL;
G_UseTargets(self, self->activator);
self->target = savetarget;
self->message = savemessage;
}
if(!(self->spawnflags & 8))
return;
func_clock_reset(self);
if(self->spawnflags & 4)
return;
}
self->nextthink = level.time + 1;
}
void func_clock_use(edict_t *self, edict_t *other, edict_t *activator) {
if(!(self->spawnflags & 8))
self->use = NULL;
if(self->activator)
return;
self->activator = activator;
self->think(self);
}
void SP_func_clock(edict_t *self) {
if(!self->target) {
gi.dprintf("%s with no target at %s\n", self->classname, vtos(self->s.origin));
G_FreeEdict(self);
return;
}
if((self->spawnflags & 2) && (!self->count)) {
gi.dprintf("%s with no count at %s\n", self->classname, vtos(self->s.origin));
G_FreeEdict(self);
return;
}
if((self->spawnflags & 1) && (!self->count))
self->count = 60 * 60;
;
func_clock_reset(self);
self->message = gi.TagMalloc(CLOCK_MESSAGE_SIZE, TAG_LEVEL);
self->think = func_clock_think;
if(self->spawnflags & 4)
self->use = func_clock_use;
else
self->nextthink = level.time + 1;
}
void teleporter_touch(edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf) {
edict_t *dest;
int i;
if(!other->client)
return;
dest = G_Find(self->s.cmodel_index, NULL, FOFS(targetname), self->target);
if(!dest) {
gi.dprintf("Couldn't find destination\n");
return;
}
gi.unlinkentity(other);
VectorCopy(dest->s.origin, other->s.origin);
VectorCopy(dest->s.origin, other->s.old_origin);
other->s.origin[2] += 10;
VectorClear(other->velocity);
other->client->ps.pmove.pm_time = 160 >> 3; other->client->ps.pmove.pm_flags |= PMF_TIME_TELEPORT;
self->owner->s.event = EV_PLAYER_TELEPORT;
other->s.event = EV_PLAYER_TELEPORT;
for(i = 0; i < 3; i++)
other->client->ps.pmove.delta_angles[i] = ANGLE2SHORT(dest->s.angles[i] - other->client->resp.cmd_angles[i]);
VectorClear(other->s.angles);
VectorClear(other->client->ps.viewangles);
VectorClear(other->client->v_angle);
KillBox(other);
gi.linkentity(other);
}
void SP_misc_teleporter(edict_t *ent) {
edict_t *trig;
if(!ent->target) {
gi.dprintf("teleporter without a target.\n");
G_FreeEdict(ent);
return;
}
gi.setmodel(ent, "models/objects/dmspot/tris.md2");
ent->s.skinnum = 1;
ent->s.effects = EF_TELEPORTER;
ent->s.sound = gi.soundindex("world/amb10.wav");
ent->solid = SOLID_BBOX;
VectorSet(ent->mins, -32, -32, -24);
VectorSet(ent->maxs, 32, 32, -16);
gi.linkentity(ent);
trig = G_Spawn(ent->s.cmodel_index);
trig->touch = teleporter_touch;
trig->solid = SOLID_TRIGGER;
trig->target = ent->target;
trig->owner = ent;
VectorCopy(ent->s.origin, trig->s.origin);
VectorSet(trig->mins, -8, -8, 8);
VectorSet(trig->maxs, 8, 8, 24);
gi.linkentity(trig);
}
void SP_misc_teleporter_dest(edict_t *ent) {
gi.setmodel(ent, "models/objects/dmspot/tris.md2");
ent->s.skinnum = 0;
ent->solid = SOLID_BBOX;
VectorSet(ent->mins, -32, -32, -24);
VectorSet(ent->maxs, 32, 32, -16);
gi.linkentity(ent);
}
void SelectSpawnPoint(edict_t *ent, int *cmodel_index, vec3_t origin, vec3_t angles);
void redeploy_touch(edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf) {
if(!other->client)
return;
int cmodel_index;
vec3_t dest_origin;
vec3_t dest_angles;
SelectSpawnPoint(other, &cmodel_index, dest_origin, dest_angles);
gi.unlinkentity(other);
VectorCopy(dest_origin, other->s.origin);
VectorCopy(dest_origin, other->s.old_origin);
other->s.origin[2] += 10;
other->s.cmodel_index = cmodel_index;
other->client->ps.cmodel_index = cmodel_index;
VectorClear(other->velocity);
other->client->ps.pmove.pm_time = 160 >> 3; other->client->ps.pmove.pm_flags |= PMF_TIME_TELEPORT;
self->owner->s.event = EV_PLAYER_TELEPORT;
other->s.event = EV_PLAYER_TELEPORT;
for(int i = 0; i < 3; i++)
other->client->ps.pmove.delta_angles[i] = ANGLE2SHORT(dest_angles[i] - other->client->resp.cmd_angles[i]);
VectorClear(other->s.angles);
VectorClear(other->client->ps.viewangles);
VectorClear(other->client->v_angle);
KillBox(other);
gi.linkentity(other);
}
void SP_misc_redeploy(edict_t *ent) {
edict_t *trig;
gi.setmodel(ent, "models/objects/dmspot/tris.md2");
ent->s.skinnum = 1;
ent->s.effects = EF_TELEPORTER;
ent->s.sound = gi.soundindex("world/amb10.wav");
ent->solid = SOLID_BBOX;
VectorSet(ent->mins, -32, -32, -24);
VectorSet(ent->maxs, 32, 32, -16);
gi.linkentity(ent);
trig = G_Spawn(ent->s.cmodel_index);
trig->touch = redeploy_touch;
trig->solid = SOLID_TRIGGER;
trig->target = ent->target;
trig->owner = ent;
VectorCopy(ent->s.origin, trig->s.origin);
VectorSet(trig->mins, -8, -8, 8);
VectorSet(trig->maxs, 8, 8, 24);
gi.linkentity(trig);
}