1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879 |
- /*
- Copyright (C) 1997-2001 Id Software, Inc.
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License
- as published by the Free Software Foundation; either version 2
- of the License, or (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
- // g_misc.c
- #include "g_local.h"
- /*QUAKED func_group (0 0 0) ?
- Used to group brushes together just for editor convenience.
- */
- //=====================================================
- void Use_Areaportal (edict_t *ent, edict_t *other, edict_t *activator)
- {
- ent->count ^= 1; // toggle state
- // gi.dprintf ("portalstate: %i = %i\n", ent->style, ent->count);
- gi.SetAreaPortalState (ent->style, ent->count);
- }
- /*QUAKED func_areaportal (0 0 0) ?
- This is a non-visible object that divides the world into
- areas that are seperated when this portal is not activated.
- Usually enclosed in the middle of a door.
- */
- void SP_func_areaportal (edict_t *ent)
- {
- ent->use = Use_Areaportal;
- ent->count = 0; // always start closed;
- }
- //=====================================================
- /*
- =================
- Misc functions
- =================
- */
- 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; // always some upwards
- else if (ent->velocity[2] > 500)
- ent->velocity[2] = 500;
- }
- /*
- =================
- gibs
- =================
- */
- 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();
- 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; // second skin is player
- }
- 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) // bodies in the queue don't have a client anymore
- {
- self->client->anim_priority = ANIM_DEATH;
- self->client->anim_end = self->s.frame;
- }
- else
- {
- self->think = NULL;
- self->nextthink = 0;
- }
- gi.linkentity (self);
- }
- /*
- =================
- debris
- =================
- */
- 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();
- 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.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.origin, MULTICAST_PVS);
- 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)
- {
- 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->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(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);
- }
- /*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)
- {
- edict_t *activator;
- if (other->movetarget != self)
- return;
- if (self->target)
- {
- other->target = self->target;
- other->goalentity = other->movetarget = G_PickTarget(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);
- };
- /*QUAKED viewthing (0 .5 .8) (-8 -8 -8) (8 8 8)
- Just for the debugging level. Don't use
- */
- 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;
- }
- /*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)
- {
- G_FreeEdict (self);
- };
- /*QUAKED info_notnull (0 0.5 0) (-4 -4 -4) (4 4 4)
- Used as a positional target for lightning.
- */
- void SP_info_notnull (edict_t *self)
- {
- VectorCopy (self->s.origin, self->absmin);
- VectorCopy (self->s.origin, self->absmax);
- };
- /*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
- 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)
- {
- // no targeted lights in deathmatch, because they cause global messages
- 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");
- }
- }
- /*QUAKED func_wall (0 .5 .8) ? TRIGGER_SPAWN TOGGLE START_ON ANIMATED ANIMATED_FAST
- This is just a solid wall if not inhibited
- TRIGGER_SPAWN the wall will not be present until triggered
- it will then blink in to existance; it will
- kill anything that was in it's way
- TOGGLE only valid for TRIGGER_SPAWN walls
- this allows the wall to be turned on and off
- START_ON only valid for TRIGGER_SPAWN walls
- the wall will initially be present
- */
- 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;
- // just a wall
- if ((self->spawnflags & 7) == 0)
- {
- self->solid = SOLID_BSP;
- gi.linkentity (self);
- return;
- }
- // it must be TRIGGER_SPAWN
- if (!(self->spawnflags & 1))
- {
- // gi.dprintf("func_wall missing TRIGGER_SPAWN\n");
- self->spawnflags |= 1;
- }
- // yell if the spawnflags are odd
- 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);
- }
- /*QUAKED func_object (0 .5 .8) ? TRIGGER_SPAWN ANIMATED ANIMATED_FAST
- This is solid bmodel that will fall if it's support it removed.
- */
- void func_object_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
- {
- // only squash thing we fall on top of
- 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);
- }
- /*QUAKED func_explosive (0 .5 .8) ? Trigger_Spawn ANIMATED ANIMATED_FAST
- Any brush that you want to explode or break apart. If you want an
- ex0plosion, set dmg and it will do a radius explosion of that amount
- at the center of the bursh.
- If targeted it will not be shootable.
- health defaults to 100.
- mass defaults to 75. This determines how much debris is emitted when
- it explodes. You get one large chunk per 100 of mass (up to 8) and
- one small chunk per 25 of mass (up to 16). So 800 gives the most.
- */
- 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;
- // bmodel origins are (0 0 0), we need to adjust that here
- 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);
- // start chunks towards the center
- VectorScale (size, 0.5, size);
- mass = self->mass;
- if (!mass)
- mass = 75;
- // big chunks
- 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);
- }
- }
- // small chunks
- 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, self->health, 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)
- { // auto-remove for deathmatch
- 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 (!self->health)
- self->health = 100;
- self->die = func_explosive_explode;
- self->takedamage = DAMAGE_YES;
- }
- gi.linkentity (self);
- }
- /*QUAKED misc_explobox (0 .5 .8) (-16 -16 0) (16 16 40)
- Large exploding box. You can override its mass (100),
- health (80), and dmg (150).
- */
- 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);
- // a few big chunks
- 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);
- // bottom corners
- 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);
- // a bunch of little chunks
- 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)
- { // auto-remove for deathmatch
- 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 (!self->health)
- self->health = 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);
- }
- //
- // miscellaneous specialty items
- //
- /*QUAKED misc_blackhole (1 .5 0) (-8 -8 -8) (8 8 8)
- */
- void misc_blackhole_use (edict_t *ent, edict_t *other, edict_t *activator)
- {
- /*
- gi.WriteByte (svc_temp_entity);
- gi.WriteByte (TE_BOSSTPORT);
- gi.WritePosition (ent->s.origin);
- gi.multicast (ent->s.origin, MULTICAST_PVS);
- */
- 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);
- }
- /*QUAKED misc_eastertank (1 .5 0) (-32 -32 -16) (32 32 32)
- */
- 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);
- }
- /*QUAKED misc_easterchick (1 .5 0) (-32 -32 0) (32 32 32)
- */
- 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);
- }
- /*QUAKED misc_easterchick2 (1 .5 0) (-32 -32 0) (32 32 32)
- */
- 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);
- }
- /*QUAKED monster_commander_body (1 .5 0) (-32 -32 0) (32 32 48)
- Not really a monster, this is the Tank Commander's decapitated body.
- There should be a item_commander_head that has this as it's target.
- */
- 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;
- }
- /*QUAKED misc_banner (1 .5 0) (-4 -4 -4) (4 4 4)
- The origin is the bottom of the banner.
- The banner is 128 tall.
- */
- 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;
- }
- /*QUAKED misc_deadsoldier (1 .5 0) (-16 -16 0) (16 16 16) ON_BACK ON_STOMACH BACK_DECAP FETAL_POS SIT_DECAP IMPALED
- This is the dead player model. Comes in 6 exciting different poses!
- */
- void misc_deadsoldier_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
- {
- int n;
- if (self->health > -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)
- { // auto-remove for deathmatch
- G_FreeEdict (ent);
- return;
- }
- ent->movetype = MOVETYPE_NONE;
- ent->solid = SOLID_BBOX;
- ent->s.modelindex=gi.modelindex ("models/deadbods/dude/tris.md2");
- // Defaults to frame 0
- 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);
- }
- /*QUAKED misc_viper (1 .5 0) (-16 -16 0) (16 16 32)
- This is the Viper for the flyby bombing.
- It is trigger_spawned, so you must have something use it for it to show up.
- There must be a path for it to follow once it is activated.
- "speed" How fast the Viper should fly
- */
- 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);
- }
- /*QUAKED misc_bigviper (1 .5 0) (-176 -120 -24) (176 120 72)
- This is a large stationary viper as seen in Paul's intro
- */
- void SP_misc_bigviper (edict_t *ent)
- {
- 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);
- }
- /*QUAKED misc_viper_bomb (1 0 0) (-8 -8 -8) (8 8 8)
- "dmg" how much boom should the bomb make?
- */
- 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 (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);
- }
- /*QUAKED misc_strogg_ship (1 .5 0) (-16 -16 0) (16 16 32)
- This is a Storgg ship for the flybys.
- It is trigger_spawned, so you must have something use it for it to show up.
- There must be a path for it to follow once it is activated.
- "speed" How fast it should fly
- */
- 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);
- }
- /*QUAKED misc_satellite_dish (1 .5 0) (-64 -64 0) (64 64 128)
- */
- 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);
- }
- /*QUAKED light_mine1 (0 1 0) (-2 -2 -12) (2 2 12)
- */
- 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);
- }
- /*QUAKED light_mine2 (0 1 0) (-2 -2 -12) (2 2 12)
- */
- 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);
- }
- /*QUAKED misc_gib_arm (1 0 0) (-8 -8 -8) (8 8 8)
- Intended for use with the target_spawner
- */
- 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);
- }
- /*QUAKED misc_gib_leg (1 0 0) (-8 -8 -8) (8 8 8)
- Intended for use with the target_spawner
- */
- 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);
- }
- /*QUAKED misc_gib_head (1 0 0) (-8 -8 -8) (8 8 8)
- Intended for use with the target_spawner
- */
- 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);
- }
- //=====================================================
- /*QUAKED target_character (0 0 1) ?
- used with target_string (must be on same "team")
- "count" is position in the string (starts at 1)
- */
- void SP_target_character (edict_t *self)
- {
- self->movetype = MOVETYPE_PUSH;
- gi.setmodel (self, self->model);
- self->solid = SOLID_BSP;
- self->s.frame = 12;
- gi.linkentity (self);
- return;
- }
- /*QUAKED target_string (0 0 1) (-8 -8 -8) (8 8 8)
- */
- 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;
- }
- /*QUAKED func_clock (0 0 1) (-8 -8 -8) (8 8 8) TIMER_UP TIMER_DOWN START_OFF MULTI_USE
- target a target_string with this
- The default is to be a time of day clock
- TIMER_UP and TIMER_DOWN run for "count" seconds and the fire "pathtarget"
- If START_OFF, this entity must be used before it starts
- "style" 0 "xx"
- 1 "xx:xx"
- 2 "xx:xx:xx"
- */
- #define CLOCK_MESSAGE_SIZE 16
- // don't let field width of any clock messages change, or it
- // could cause an overwrite after a game load
- static void func_clock_reset (edict_t *self)
- {
- self->activator = NULL;
- if (self->spawnflags & 1)
- {
- self->health = 0;
- self->wait = self->count;
- }
- else if (self->spawnflags & 2)
- {
- self->health = 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", self->health);
- return;
- }
- if (self->style == 1)
- {
- Com_sprintf(self->message, CLOCK_MESSAGE_SIZE, "%2i:%2i", self->health / 60, self->health % 60);
- if (self->message[3] == ' ')
- self->message[3] = '0';
- return;
- }
- if (self->style == 2)
- {
- Com_sprintf(self->message, CLOCK_MESSAGE_SIZE, "%2i:%2i:%2i", self->health / 3600, (self->health - (self->health / 3600) * 3600) / 60, self->health % 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 (NULL, FOFS(targetname), self->target);
- if (!self->enemy)
- return;
- }
- if (self->spawnflags & 1)
- {
- func_clock_format_countdown (self);
- self->health++;
- }
- else if (self->spawnflags & 2)
- {
- func_clock_format_countdown (self);
- self->health--;
- }
- 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) && (self->health > self->wait)) ||
- ((self->spawnflags & 2) && (self->health < 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 (NULL, FOFS(targetname), self->target);
- if (!dest)
- {
- gi.dprintf ("Couldn't find destination\n");
- return;
- }
- // unlink to make sure it can't possibly interfere with KillBox
- gi.unlinkentity (other);
- VectorCopy (dest->s.origin, other->s.origin);
- VectorCopy (dest->s.origin, other->s.old_origin);
- other->s.origin[2] += 10;
- // clear the velocity and hold them in place briefly
- VectorClear (other->velocity);
- other->client->ps.pmove.pm_time = 160>>3; // hold time
- other->client->ps.pmove.pm_flags |= PMF_TIME_TELEPORT;
- // draw the teleport splash at source and on the player
- self->owner->s.event = EV_PLAYER_TELEPORT;
- other->s.event = EV_PLAYER_TELEPORT;
- // set angles
- for (i=0 ; i<3 ; i++)
- {
- other->client->ps.pmove.delta_angles[i] = ANGLE2SHORT(dest->s.angles[i] - other->client->resp.cmd_angles[i]);
- }
- VectorClear (other->s.angles);
- VectorClear (other->client->ps.viewangles);
- VectorClear (other->client->v_angle);
- // kill anything at the destination
- KillBox (other);
- gi.linkentity (other);
- }
- /*QUAKED misc_teleporter (1 0 0) (-32 -32 -24) (32 32 -16)
- Stepping onto this disc will teleport players to the targeted misc_teleporter_dest object.
- */
- void 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 ();
- 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);
-
- }
- /*QUAKED misc_teleporter_dest (1 0 0) (-32 -32 -24) (32 32 -16)
- Point teleporters at these.
- */
- void SP_misc_teleporter_dest (edict_t *ent)
- {
- gi.setmodel (ent, "models/objects/dmspot/tris.md2");
- ent->s.skinnum = 0;
- ent->solid = SOLID_BBOX;
- // ent->s.effects |= EF_FLIES;
- VectorSet (ent->mins, -32, -32, -24);
- VectorSet (ent->maxs, 32, 32, -16);
- gi.linkentity (ent);
- }
|