1 /*
2 * Copyright(c) 1997-2001 Id Software, Inc.
3 * Copyright(c) 2002 The Quakeforge Project.
4 * Copyright(c) 2006 Quetoo.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or(at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14 *
15 * See the GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 */
21
22 #include "g_local.h"
23
24
25 /*QUAKED func_group(0 0 0) ?
26 Used to group brushes together just for editor convenience.
27 */
28
29
Use_Areaportal(edict_t * ent,edict_t * other,edict_t * activator)30 void Use_Areaportal(edict_t *ent, edict_t *other, edict_t *activator){
31 ent->count ^= 1; // toggle state
32 // gi.dprintf("portalstate: %i = %i\n", ent->style, ent->count);
33 gi.SetAreaPortalState(ent->style, ent->count);
34 }
35
36 /*QUAKED func_areaportal(0 0 0) ?
37
38 This is a non-visible object that divides the world into
39 areas that are seperated when this portal is not activated.
40 Usually enclosed in the middle of a door.
41 */
SP_func_areaportal(edict_t * ent)42 void SP_func_areaportal(edict_t *ent){
43 ent->use = Use_Areaportal;
44 ent->count = 0; // always start closed;
45 }
46
47
48
49 /*
50 Misc functions
51 */
VelocityForDamage(int damage,vec3_t v)52 void VelocityForDamage(int damage, vec3_t v){
53 v[0] = 100.0 * crandom();
54 v[1] = 100.0 * crandom();
55 v[2] = 200.0 + 100.0 * random();
56
57 if(damage < 50)
58 VectorScale(v, 0.7, v);
59 else
60 VectorScale(v, 1.2, v);
61 }
62
ClipGibVelocity(edict_t * ent)63 void ClipGibVelocity(edict_t *ent){
64 if(ent->velocity[0] < -300)
65 ent->velocity[0] = -300;
66 else if(ent->velocity[0] > 300)
67 ent->velocity[0] = 300;
68 if(ent->velocity[1] < -300)
69 ent->velocity[1] = -300;
70 else if(ent->velocity[1] > 300)
71 ent->velocity[1] = 300;
72 if(ent->velocity[2] < 200)
73 ent->velocity[2] = 200; // always some upwards
74 else if(ent->velocity[2] > 500)
75 ent->velocity[2] = 500;
76 }
77
78
79 /*
80 gibs
81 */
gib_think(edict_t * self)82 void gib_think(edict_t *self){
83 self->s.frame++;
84 self->nextthink = level.time + FRAMETIME;
85
86 if(self->s.frame == 10){
87 self->think = G_FreeEdict;
88 self->nextthink = level.time + 8 + random() * 10;
89 }
90 }
91
gib_touch(edict_t * self,edict_t * other,cplane_t * plane,csurface_t * surf)92 void gib_touch(edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf){
93 vec3_t normal_angles, right;
94
95 if(!self->groundentity)
96 return;
97
98 self->touch = NULL;
99
100 if(plane){
101 gi.sound(self, CHAN_VOICE, gi.soundindex("misc/fhit3.wav"), 1, ATTN_NORM, 0);
102
103 vectoangles(plane->normal, normal_angles);
104 AngleVectors(normal_angles, NULL, right, NULL);
105 vectoangles(right, self->s.angles);
106
107 if(self->s.modelindex == sm_meat_index){
108 self->s.frame++;
109 self->think = gib_think;
110 self->nextthink = level.time + FRAMETIME;
111 }
112 }
113 }
114
gib_die(edict_t * self,edict_t * inflictor,edict_t * attacker,int damage,vec3_t point)115 void gib_die(edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point){
116 G_FreeEdict(self);
117 }
118
ThrowGib(edict_t * self,char * gibname,int damage,int type)119 void ThrowGib(edict_t *self, char *gibname, int damage, int type){
120 edict_t *gib;
121 vec3_t vd;
122 vec3_t origin;
123 vec3_t size;
124 float vscale;
125
126 gib = G_Spawn();
127
128 VectorScale(self->size, 0.5, size);
129 VectorAdd(self->absmin, size, origin);
130 gib->s.origin[0] = origin[0] + crandom() * size[0];
131 gib->s.origin[1] = origin[1] + crandom() * size[1];
132 gib->s.origin[2] = origin[2] + crandom() * size[2];
133
134 gi.setmodel(gib, gibname);
135 gib->solid = SOLID_NOT;
136 gib->s.effects |= EF_GIB;
137 gib->flags |= FL_NO_KNOCKBACK;
138 gib->takedamage = DAMAGE_YES;
139 gib->die = gib_die;
140
141 if(type == GIB_ORGANIC){
142 gib->movetype = MOVETYPE_TOSS;
143 gib->touch = gib_touch;
144 vscale = 0.5;
145 } else {
146 gib->movetype = MOVETYPE_BOUNCE;
147 vscale = 1.0;
148 }
149
150 VelocityForDamage(damage, vd);
151 VectorMA(self->velocity, vscale, vd, gib->velocity);
152 ClipGibVelocity(gib);
153 gib->avelocity[0] = random() * 600;
154 gib->avelocity[1] = random() * 600;
155 gib->avelocity[2] = random() * 600;
156
157 gib->think = G_FreeEdict;
158 gib->nextthink = level.time + 10 + random() * 10;
159
160 gi.linkentity(gib);
161 }
162
ThrowHead(edict_t * self,char * gibname,int damage,int type)163 void ThrowHead(edict_t *self, char *gibname, int damage, int type){
164 vec3_t vd;
165 float vscale;
166
167 self->s.skinnum = 0;
168 self->s.frame = 0;
169 VectorClear(self->mins);
170 VectorClear(self->maxs);
171
172 self->s.modelindex2 = 0;
173 gi.setmodel(self, gibname);
174 self->solid = SOLID_NOT;
175 self->s.effects |= EF_GIB;
176 self->s.effects &= ~EF_FLIES;
177 self->s.sound = 0;
178 self->flags |= FL_NO_KNOCKBACK;
179 self->svflags &= ~SVF_MONSTER;
180 self->takedamage = DAMAGE_YES;
181 self->die = gib_die;
182
183 if(type == GIB_ORGANIC){
184 self->movetype = MOVETYPE_TOSS;
185 self->touch = gib_touch;
186 vscale = 0.5;
187 } else {
188 self->movetype = MOVETYPE_BOUNCE;
189 vscale = 1.0;
190 }
191
192 VelocityForDamage(damage, vd);
193 VectorMA(self->velocity, vscale, vd, self->velocity);
194 ClipGibVelocity(self);
195
196 self->avelocity[YAW] = crandom() * 600;
197
198 self->think = G_FreeEdict;
199 self->nextthink = level.time + 10 + random() * 10;
200
201 gi.linkentity(self);
202 }
203
204
ThrowClientHead(edict_t * self,int damage)205 void ThrowClientHead(edict_t *self, int damage){
206 vec3_t vd;
207 char *gibname;
208
209 if(rand()&1){
210 gibname = "models/objects/gibs/head2/tris.md2";
211 self->s.skinnum = 1; // second skin is player
212 } else {
213 gibname = "models/objects/gibs/skull/tris.md2";
214 self->s.skinnum = 0;
215 }
216
217 self->s.origin[2] += 32;
218 self->s.frame = 0;
219 gi.setmodel(self, gibname);
220 VectorSet(self->mins, -16, -16, 0);
221 VectorSet(self->maxs, 16, 16, 16);
222
223 self->takedamage = DAMAGE_NO;
224 self->solid = SOLID_NOT;
225 self->s.effects = EF_GIB;
226 self->s.sound = 0;
227 self->flags |= FL_NO_KNOCKBACK;
228
229 self->movetype = MOVETYPE_BOUNCE;
230 VelocityForDamage(damage, vd);
231 VectorAdd(self->velocity, vd, self->velocity);
232
233 if(self->client) // bodies in the queue don't have a client anymore
234 {
235 self->client->anim_priority = ANIM_DEATH;
236 self->client->anim_end = self->s.frame;
237 } else {
238 self->think = NULL;
239 self->nextthink = 0;
240 }
241
242 gi.linkentity(self);
243 }
244
245
246 /*
247 debris
248 */
debris_die(edict_t * self,edict_t * inflictor,edict_t * attacker,int damage,vec3_t point)249 void debris_die(edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point){
250 G_FreeEdict(self);
251 }
252
ThrowDebris(edict_t * self,char * modelname,float speed,vec3_t origin)253 void ThrowDebris(edict_t *self, char *modelname, float speed, vec3_t origin){
254 edict_t *chunk;
255 vec3_t v;
256
257 chunk = G_Spawn();
258 VectorCopy(origin, chunk->s.origin);
259 gi.setmodel(chunk, modelname);
260 v[0] = 100 * crandom();
261 v[1] = 100 * crandom();
262 v[2] = 100 + 100 * crandom();
263 VectorMA(self->velocity, speed, v, chunk->velocity);
264 chunk->movetype = MOVETYPE_BOUNCE;
265 chunk->solid = SOLID_NOT;
266 chunk->avelocity[0] = random() * 600;
267 chunk->avelocity[1] = random() * 600;
268 chunk->avelocity[2] = random() * 600;
269 chunk->think = G_FreeEdict;
270 chunk->nextthink = level.time + 5 + random() * 5;
271 chunk->s.frame = 0;
272 chunk->flags = 0;
273 chunk->classname = "debris";
274 chunk->takedamage = DAMAGE_YES;
275 chunk->die = debris_die;
276 gi.linkentity(chunk);
277 }
278
279
BecomeExplosion1(edict_t * self)280 void BecomeExplosion1(edict_t *self){
281 gi.WriteByte(svc_temp_entity);
282 gi.WriteByte(TE_EXPLOSION1);
283 gi.WritePosition(self->s.origin);
284 gi.multicast(self->s.origin, MULTICAST_PVS);
285
286 G_FreeEdict(self);
287 }
288
289
BecomeExplosion2(edict_t * self)290 void BecomeExplosion2(edict_t *self){
291 gi.WriteByte(svc_temp_entity);
292 gi.WriteByte(TE_EXPLOSION2);
293 gi.WritePosition(self->s.origin);
294 gi.multicast(self->s.origin, MULTICAST_PVS);
295
296 G_FreeEdict(self);
297 }
298
299
300 /*QUAKED path_corner(.5 .3 0)(-8 -8 -8)(8 8 8) TELEPORT
301 Target: next path corner
302 Pathtarget: gets used when an entity that has
303 this path_corner targeted touches it
304 */
305
path_corner_touch(edict_t * self,edict_t * other,cplane_t * plane,csurface_t * surf)306 void path_corner_touch(edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf){
307 vec3_t v;
308 edict_t *next;
309
310 if(other->movetarget != self)
311 return;
312
313 if(other->enemy)
314 return;
315
316 if(self->pathtarget){
317 char *savetarget;
318
319 savetarget = self->target;
320 self->target = self->pathtarget;
321 G_UseTargets(self, other);
322 self->target = savetarget;
323 }
324
325 if(self->target)
326 next = G_PickTarget(self->target);
327 else
328 next = NULL;
329
330 if((next) &&(next->spawnflags & 1)){
331 VectorCopy(next->s.origin, v);
332 v[2] += next->mins[2];
333 v[2] -= other->mins[2];
334 VectorCopy(v, other->s.origin);
335 next = G_PickTarget(next->target);
336 other->s.event = EV_OTHER_TELEPORT;
337 }
338
339 other->goalentity = other->movetarget = next;
340
341 if(self->wait){
342 other->monsterinfo.pausetime = level.time + self->wait;
343 other->monsterinfo.stand(other);
344 return;
345 }
346
347 if(!other->movetarget){
348 other->monsterinfo.pausetime = level.time + 100000000;
349 other->monsterinfo.stand(other);
350 } else {
351 VectorSubtract(other->goalentity->s.origin, other->s.origin, v);
352 other->ideal_yaw = vectoyaw(v);
353 }
354 }
355
SP_path_corner(edict_t * self)356 void SP_path_corner(edict_t *self){
357 if(!self->targetname){
358 gi.dprintf("path_corner with no targetname at %s\n", vtos(self->s.origin));
359 G_FreeEdict(self);
360 return;
361 }
362
363 self->solid = SOLID_TRIGGER;
364 self->touch = path_corner_touch;
365 VectorSet(self->mins, -8, -8, -8);
366 VectorSet(self->maxs, 8, 8, 8);
367 self->svflags |= SVF_NOCLIENT;
368 gi.linkentity(self);
369 }
370
371
372 /*QUAKED point_combat(0.5 0.3 0)(-8 -8 -8)(8 8 8) Hold
373 Makes this the target of a monster and it will head here
374 when first activated before going after the activator. If
375 hold is selected, it will stay here.
376 */
point_combat_touch(edict_t * self,edict_t * other,cplane_t * plane,csurface_t * surf)377 void point_combat_touch(edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf){
378 edict_t *activator;
379
380 if(other->movetarget != self)
381 return;
382
383 if(self->target){
384 other->target = self->target;
385 other->goalentity = other->movetarget = G_PickTarget(other->target);
386 if(!other->goalentity){
387 gi.dprintf("%s at %s target %s does not exist\n", self->classname, vtos(self->s.origin), self->target);
388 other->movetarget = self;
389 }
390 self->target = NULL;
391 } else if((self->spawnflags & 1) && !(other->flags &(FL_SWIM | FL_FLY))){
392 other->monsterinfo.pausetime = level.time + 100000000;
393 other->monsterinfo.aiflags |= AI_STAND_GROUND;
394 other->monsterinfo.stand(other);
395 }
396
397 if(other->movetarget == self){
398 other->target = NULL;
399 other->movetarget = NULL;
400 other->goalentity = other->enemy;
401 other->monsterinfo.aiflags &= ~AI_COMBAT_POINT;
402 }
403
404 if(self->pathtarget){
405 char *savetarget;
406
407 savetarget = self->target;
408 self->target = self->pathtarget;
409 if(other->enemy && other->enemy->client)
410 activator = other->enemy;
411 else if(other->oldenemy && other->oldenemy->client)
412 activator = other->oldenemy;
413 else if(other->activator && other->activator->client)
414 activator = other->activator;
415 else
416 activator = other;
417 G_UseTargets(self, activator);
418 self->target = savetarget;
419 }
420 }
421
SP_point_combat(edict_t * self)422 void SP_point_combat(edict_t *self){
423 if(deathmatch->value){
424 G_FreeEdict(self);
425 return;
426 }
427 self->solid = SOLID_TRIGGER;
428 self->touch = point_combat_touch;
429 VectorSet(self->mins, -8, -8, -16);
430 VectorSet(self->maxs, 8, 8, 16);
431 self->svflags = SVF_NOCLIENT;
432 gi.linkentity(self);
433 }
434
435
436 /*QUAKED viewthing(0 .5 .8)(-8 -8 -8)(8 8 8)
437 Just for the debugging level. Don't use
438 */
TH_viewthing(edict_t * ent)439 void TH_viewthing(edict_t *ent){
440 ent->s.frame =(ent->s.frame + 1) % 7;
441 ent->nextthink = level.time + FRAMETIME;
442 }
443
SP_viewthing(edict_t * ent)444 void SP_viewthing(edict_t *ent){
445 gi.dprintf("viewthing spawned\n");
446
447 ent->movetype = MOVETYPE_NONE;
448 ent->solid = SOLID_BBOX;
449 ent->s.renderfx = RF_FRAMELERP;
450 VectorSet(ent->mins, -16, -16, -24);
451 VectorSet(ent->maxs, 16, 16, 32);
452 ent->s.modelindex = gi.modelindex("models/objects/banner/tris.md2");
453 gi.linkentity(ent);
454 ent->nextthink = level.time + 0.5;
455 ent->think = TH_viewthing;
456 return;
457 }
458
459
460 /*QUAKED info_null(0 0.5 0)(-4 -4 -4)(4 4 4)
461 Used as a positional target for spotlights, etc.
462 */
SP_info_null(edict_t * self)463 void SP_info_null(edict_t *self){
464 G_FreeEdict(self);
465 }
466
467
468 /*QUAKED info_notnull(0 0.5 0)(-4 -4 -4)(4 4 4)
469 Used as a positional target for lightning.
470 */
SP_info_notnull(edict_t * self)471 void SP_info_notnull(edict_t *self){
472 VectorCopy(self->s.origin, self->absmin);
473 VectorCopy(self->s.origin, self->absmax);
474 }
475
476
477 /*QUAKED light(0 1 0)(-8 -8 -8)(8 8 8) START_OFF
478 Non-displayed light.
479 Default light value is 300.
480 Default style is 0.
481 If targeted, will toggle between on and off.
482 Default _cone value is 10(used to set size of light for spotlights)
483 */
484
485 #define START_OFF 1
486
light_use(edict_t * self,edict_t * other,edict_t * activator)487 static void light_use(edict_t *self, edict_t *other, edict_t *activator){
488 if(self->spawnflags & START_OFF){
489 gi.configstring(CS_LIGHTS + self->style, "m");
490 self->spawnflags &= ~START_OFF;
491 } else {
492 gi.configstring(CS_LIGHTS + self->style, "a");
493 self->spawnflags |= START_OFF;
494 }
495 }
496
SP_light(edict_t * self)497 void SP_light(edict_t *self){
498 // no targeted lights in deathmatch, because they cause global messages
499 if(!self->targetname || deathmatch->value){
500 G_FreeEdict(self);
501 return;
502 }
503
504 if(self->style >= 32){
505 self->use = light_use;
506 if(self->spawnflags & START_OFF)
507 gi.configstring(CS_LIGHTS + self->style, "a");
508 else
509 gi.configstring(CS_LIGHTS + self->style, "m");
510 }
511 }
512
513
514 /*QUAKED func_wall(0 .5 .8) ? TRIGGER_SPAWN TOGGLE START_ON ANIMATED ANIMATED_FAST
515 This is just a solid wall if not inhibited
516
517 TRIGGER_SPAWN the wall will not be present until triggered
518 it will then blink in to existance; it will
519 kill anything that was in it's way
520
521 TOGGLE only valid for TRIGGER_SPAWN walls
522 this allows the wall to be turned on and off
523
524 START_ON only valid for TRIGGER_SPAWN walls
525 the wall will initially be present
526 */
527
func_wall_use(edict_t * self,edict_t * other,edict_t * activator)528 void func_wall_use(edict_t *self, edict_t *other, edict_t *activator){
529 if(self->solid == SOLID_NOT){
530 self->solid = SOLID_BSP;
531 self->svflags &= ~SVF_NOCLIENT;
532 KillBox(self);
533 } else {
534 self->solid = SOLID_NOT;
535 self->svflags |= SVF_NOCLIENT;
536 }
537 gi.linkentity(self);
538
539 if(!(self->spawnflags & 2))
540 self->use = NULL;
541 }
542
SP_func_wall(edict_t * self)543 void SP_func_wall(edict_t *self){
544 self->movetype = MOVETYPE_PUSH;
545 gi.setmodel(self, self->model);
546
547 if(self->spawnflags & 8)
548 self->s.effects |= EF_ANIM_ALL;
549 if(self->spawnflags & 16)
550 self->s.effects |= EF_ANIM_ALLFAST;
551
552 // just a wall
553 if((self->spawnflags & 7) == 0){
554 self->solid = SOLID_BSP;
555 gi.linkentity(self);
556 return;
557 }
558
559 // it must be TRIGGER_SPAWN
560 if(!(self->spawnflags & 1)){
561 // gi.dprintf("func_wall missing TRIGGER_SPAWN\n");
562 self->spawnflags |= 1;
563 }
564
565 // yell if the spawnflags are odd
566 if(self->spawnflags & 4){
567 if(!(self->spawnflags & 2)){
568 gi.dprintf("func_wall START_ON without TOGGLE\n");
569 self->spawnflags |= 2;
570 }
571 }
572
573 self->use = func_wall_use;
574 if(self->spawnflags & 4){
575 self->solid = SOLID_BSP;
576 } else {
577 self->solid = SOLID_NOT;
578 self->svflags |= SVF_NOCLIENT;
579 }
580 gi.linkentity(self);
581 }
582
583
584 /*QUAKED func_object(0 .5 .8) ? TRIGGER_SPAWN ANIMATED ANIMATED_FAST
585 This is solid bmodel that will fall if it's support it removed.
586 */
587
func_object_touch(edict_t * self,edict_t * other,cplane_t * plane,csurface_t * surf)588 void func_object_touch(edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf){
589 // only squash thing we fall on top of
590 if(!plane)
591 return;
592 if(plane->normal[2] < 1.0)
593 return;
594 if(other->takedamage == DAMAGE_NO)
595 return;
596 T_Damage(other, self, self, vec3_origin, self->s.origin, vec3_origin, self->dmg, 1, 0, MOD_CRUSH);
597 }
598
func_object_release(edict_t * self)599 void func_object_release(edict_t *self){
600 self->movetype = MOVETYPE_TOSS;
601 self->touch = func_object_touch;
602 }
603
func_object_use(edict_t * self,edict_t * other,edict_t * activator)604 void func_object_use(edict_t *self, edict_t *other, edict_t *activator){
605 self->solid = SOLID_BSP;
606 self->svflags &= ~SVF_NOCLIENT;
607 self->use = NULL;
608 KillBox(self);
609 func_object_release(self);
610 }
611
SP_func_object(edict_t * self)612 void SP_func_object(edict_t *self){
613 gi.setmodel(self, self->model);
614
615 self->mins[0] += 1;
616 self->mins[1] += 1;
617 self->mins[2] += 1;
618 self->maxs[0] -= 1;
619 self->maxs[1] -= 1;
620 self->maxs[2] -= 1;
621
622 if(!self->dmg)
623 self->dmg = 100;
624
625 if(self->spawnflags == 0){
626 self->solid = SOLID_BSP;
627 self->movetype = MOVETYPE_PUSH;
628 self->think = func_object_release;
629 self->nextthink = level.time + 2 * FRAMETIME;
630 } else {
631 self->solid = SOLID_NOT;
632 self->movetype = MOVETYPE_PUSH;
633 self->use = func_object_use;
634 self->svflags |= SVF_NOCLIENT;
635 }
636
637 if(self->spawnflags & 2)
638 self->s.effects |= EF_ANIM_ALL;
639 if(self->spawnflags & 4)
640 self->s.effects |= EF_ANIM_ALLFAST;
641
642 self->clipmask = MASK_MONSTERSOLID;
643
644 gi.linkentity(self);
645 }
646
647
648 /*QUAKED func_explosive(0 .5 .8) ? Trigger_Spawn ANIMATED ANIMATED_FAST
649 Any brush that you want to explode or break apart. If you want an
650 ex0plosion, set dmg and it will do a radius explosion of that amount
651 at the center of the bursh.
652
653 If targeted it will not be shootable.
654
655 health defaults to 100.
656
657 mass defaults to 75. This determines how much debris is emitted when
658 it explodes. You get one large chunk per 100 of mass(up to 8) and
659 one small chunk per 25 of mass(up to 16). So 800 gives the most.
660 */
func_explosive_explode(edict_t * self,edict_t * inflictor,edict_t * attacker,int damage,vec3_t point)661 void func_explosive_explode(edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point){
662 vec3_t origin;
663 vec3_t chunkorigin;
664 vec3_t size;
665 int count;
666 int mass;
667
668 // bmodel origins are(0 0 0), we need to adjust that here
669 VectorScale(self->size, 0.5, size);
670 VectorAdd(self->absmin, size, origin);
671 VectorCopy(origin, self->s.origin);
672
673 self->takedamage = DAMAGE_NO;
674
675 if(self->dmg)
676 T_RadiusDamage(self, attacker, self->dmg, NULL, self->dmg + 40, MOD_EXPLOSIVE);
677
678 VectorSubtract(self->s.origin, inflictor->s.origin, self->velocity);
679 VectorNormalize(self->velocity);
680 VectorScale(self->velocity, 150, self->velocity);
681
682 // start chunks towards the center
683 VectorScale(size, 0.5, size);
684
685 mass = self->mass;
686 if(!mass)
687 mass = 75;
688
689 // big chunks
690 if(mass >= 100){
691 count = mass / 100;
692 if(count > 8)
693 count = 8;
694 while(count--){
695 chunkorigin[0] = origin[0] + crandom() * size[0];
696 chunkorigin[1] = origin[1] + crandom() * size[1];
697 chunkorigin[2] = origin[2] + crandom() * size[2];
698 ThrowDebris(self, "models/objects/debris1/tris.md2", 1, chunkorigin);
699 }
700 }
701
702 // small chunks
703 count = mass / 25;
704 if(count > 16)
705 count = 16;
706 while(count--){
707 chunkorigin[0] = origin[0] + crandom() * size[0];
708 chunkorigin[1] = origin[1] + crandom() * size[1];
709 chunkorigin[2] = origin[2] + crandom() * size[2];
710 ThrowDebris(self, "models/objects/debris2/tris.md2", 2, chunkorigin);
711 }
712
713 G_UseTargets(self, attacker);
714
715 if(self->dmg)
716 BecomeExplosion1(self);
717 else
718 G_FreeEdict(self);
719 }
720
func_explosive_use(edict_t * self,edict_t * other,edict_t * activator)721 void func_explosive_use(edict_t *self, edict_t *other, edict_t *activator){
722 func_explosive_explode(self, self, other, self->health, vec3_origin);
723 }
724
func_explosive_spawn(edict_t * self,edict_t * other,edict_t * activator)725 void func_explosive_spawn(edict_t *self, edict_t *other, edict_t *activator){
726 self->solid = SOLID_BSP;
727 self->svflags &= ~SVF_NOCLIENT;
728 self->use = NULL;
729 KillBox(self);
730 gi.linkentity(self);
731 }
732
SP_func_explosive(edict_t * self)733 void SP_func_explosive(edict_t *self){
734 if(deathmatch->value){ // auto-remove for deathmatch
735 G_FreeEdict(self);
736 return;
737 }
738
739 self->movetype = MOVETYPE_PUSH;
740
741 gi.modelindex("models/objects/debris1/tris.md2");
742 gi.modelindex("models/objects/debris2/tris.md2");
743
744 gi.setmodel(self, self->model);
745
746 if(self->spawnflags & 1){
747 self->svflags |= SVF_NOCLIENT;
748 self->solid = SOLID_NOT;
749 self->use = func_explosive_spawn;
750 } else {
751 self->solid = SOLID_BSP;
752 if(self->targetname)
753 self->use = func_explosive_use;
754 }
755
756 if(self->spawnflags & 2)
757 self->s.effects |= EF_ANIM_ALL;
758 if(self->spawnflags & 4)
759 self->s.effects |= EF_ANIM_ALLFAST;
760
761 if(self->use != func_explosive_use){
762 if(!self->health)
763 self->health = 100;
764 self->die = func_explosive_explode;
765 self->takedamage = DAMAGE_YES;
766 }
767
768 gi.linkentity(self);
769 }
770
771
772 /*QUAKED misc_explobox(0 .5 .8)(-16 -16 0)(16 16 40)
773 Large exploding box. You can override its mass(100),
774 health(80), and dmg(150).
775 */
776
barrel_touch(edict_t * self,edict_t * other,cplane_t * plane,csurface_t * surf)777 void barrel_touch(edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
778 {
779 float ratio;
780 vec3_t v;
781
782 if((!other->groundentity) ||(other->groundentity == self))
783 return;
784
785 ratio =(float)other->mass /(float)self->mass;
786 VectorSubtract(self->s.origin, other->s.origin, v);
787 M_walkmove(self, vectoyaw(v), 20 * ratio * FRAMETIME);
788 }
789
barrel_explode(edict_t * self)790 void barrel_explode(edict_t *self){
791 vec3_t org;
792 float spd;
793 vec3_t save;
794
795 T_RadiusDamage(self, self->activator, self->dmg, NULL, self->dmg + 40, MOD_BARREL);
796
797 VectorCopy(self->s.origin, save);
798 VectorMA(self->absmin, 0.5, self->size, self->s.origin);
799
800 // a few big chunks
801 spd = 1.5 *(float)self->dmg / 200.0;
802 org[0] = self->s.origin[0] + crandom() * self->size[0];
803 org[1] = self->s.origin[1] + crandom() * self->size[1];
804 org[2] = self->s.origin[2] + crandom() * self->size[2];
805 ThrowDebris(self, "models/objects/debris1/tris.md2", spd, org);
806 org[0] = self->s.origin[0] + crandom() * self->size[0];
807 org[1] = self->s.origin[1] + crandom() * self->size[1];
808 org[2] = self->s.origin[2] + crandom() * self->size[2];
809 ThrowDebris(self, "models/objects/debris1/tris.md2", spd, org);
810
811 // bottom corners
812 spd = 1.75 *(float)self->dmg / 200.0;
813 VectorCopy(self->absmin, org);
814 ThrowDebris(self, "models/objects/debris3/tris.md2", spd, org);
815 VectorCopy(self->absmin, org);
816 org[0] += self->size[0];
817 ThrowDebris(self, "models/objects/debris3/tris.md2", spd, org);
818 VectorCopy(self->absmin, org);
819 org[1] += self->size[1];
820 ThrowDebris(self, "models/objects/debris3/tris.md2", spd, org);
821 VectorCopy(self->absmin, org);
822 org[0] += self->size[0];
823 org[1] += self->size[1];
824 ThrowDebris(self, "models/objects/debris3/tris.md2", spd, org);
825
826 // a bunch of little chunks
827 spd = 2 * self->dmg / 200;
828 org[0] = self->s.origin[0] + crandom() * self->size[0];
829 org[1] = self->s.origin[1] + crandom() * self->size[1];
830 org[2] = self->s.origin[2] + crandom() * self->size[2];
831 ThrowDebris(self, "models/objects/debris2/tris.md2", spd, org);
832 org[0] = self->s.origin[0] + crandom() * self->size[0];
833 org[1] = self->s.origin[1] + crandom() * self->size[1];
834 org[2] = self->s.origin[2] + crandom() * self->size[2];
835 ThrowDebris(self, "models/objects/debris2/tris.md2", spd, org);
836 org[0] = self->s.origin[0] + crandom() * self->size[0];
837 org[1] = self->s.origin[1] + crandom() * self->size[1];
838 org[2] = self->s.origin[2] + crandom() * self->size[2];
839 ThrowDebris(self, "models/objects/debris2/tris.md2", spd, org);
840 org[0] = self->s.origin[0] + crandom() * self->size[0];
841 org[1] = self->s.origin[1] + crandom() * self->size[1];
842 org[2] = self->s.origin[2] + crandom() * self->size[2];
843 ThrowDebris(self, "models/objects/debris2/tris.md2", spd, org);
844 org[0] = self->s.origin[0] + crandom() * self->size[0];
845 org[1] = self->s.origin[1] + crandom() * self->size[1];
846 org[2] = self->s.origin[2] + crandom() * self->size[2];
847 ThrowDebris(self, "models/objects/debris2/tris.md2", spd, org);
848 org[0] = self->s.origin[0] + crandom() * self->size[0];
849 org[1] = self->s.origin[1] + crandom() * self->size[1];
850 org[2] = self->s.origin[2] + crandom() * self->size[2];
851 ThrowDebris(self, "models/objects/debris2/tris.md2", spd, org);
852 org[0] = self->s.origin[0] + crandom() * self->size[0];
853 org[1] = self->s.origin[1] + crandom() * self->size[1];
854 org[2] = self->s.origin[2] + crandom() * self->size[2];
855 ThrowDebris(self, "models/objects/debris2/tris.md2", spd, org);
856 org[0] = self->s.origin[0] + crandom() * self->size[0];
857 org[1] = self->s.origin[1] + crandom() * self->size[1];
858 org[2] = self->s.origin[2] + crandom() * self->size[2];
859 ThrowDebris(self, "models/objects/debris2/tris.md2", spd, org);
860
861 VectorCopy(save, self->s.origin);
862 if(self->groundentity)
863 BecomeExplosion2(self);
864 else
865 BecomeExplosion1(self);
866 }
867
barrel_delay(edict_t * self,edict_t * inflictor,edict_t * attacker,int damage,vec3_t point)868 void barrel_delay(edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point){
869 self->takedamage = DAMAGE_NO;
870 self->nextthink = level.time + 2 * FRAMETIME;
871 self->think = barrel_explode;
872 self->activator = attacker;
873 }
874
SP_misc_explobox(edict_t * self)875 void SP_misc_explobox(edict_t *self){
876 if(deathmatch->value){ // auto-remove for deathmatch
877 G_FreeEdict(self);
878 return;
879 }
880
881 gi.modelindex("models/objects/debris1/tris.md2");
882 gi.modelindex("models/objects/debris2/tris.md2");
883 gi.modelindex("models/objects/debris3/tris.md2");
884
885 self->solid = SOLID_BBOX;
886 self->movetype = MOVETYPE_STEP;
887
888 self->model = "models/objects/barrels/tris.md2";
889 self->s.modelindex = gi.modelindex(self->model);
890 VectorSet(self->mins, -16, -16, 0);
891 VectorSet(self->maxs, 16, 16, 40);
892
893 if(!self->mass)
894 self->mass = 400;
895 if(!self->health)
896 self->health = 10;
897 if(!self->dmg)
898 self->dmg = 150;
899
900 self->die = barrel_delay;
901 self->takedamage = DAMAGE_YES;
902 self->monsterinfo.aiflags = AI_NOSTEP;
903
904 self->touch = barrel_touch;
905
906 self->think = M_droptofloor;
907 self->nextthink = level.time + 2 * FRAMETIME;
908
909 gi.linkentity(self);
910 }
911
912
913 // miscellaneous specialty items
914
915 /*QUAKED misc_blackhole(1 .5 0)(-8 -8 -8)(8 8 8)
916 */
917
misc_blackhole_use(edict_t * ent,edict_t * other,edict_t * activator)918 void misc_blackhole_use(edict_t *ent, edict_t *other, edict_t *activator){
919 /*
920 gi.WriteByte(svc_temp_entity);
921 gi.WriteByte(TE_BOSSTPORT);
922 gi.WritePosition(ent->s.origin);
923 gi.multicast(ent->s.origin, MULTICAST_PVS);
924 */
925 G_FreeEdict(ent);
926 }
927
misc_blackhole_think(edict_t * self)928 void misc_blackhole_think(edict_t *self){
929 if(++self->s.frame < 19)
930 self->nextthink = level.time + FRAMETIME;
931 else {
932 self->s.frame = 0;
933 self->nextthink = level.time + FRAMETIME;
934 }
935 }
936
SP_misc_blackhole(edict_t * ent)937 void SP_misc_blackhole(edict_t *ent){
938 ent->movetype = MOVETYPE_NONE;
939 ent->solid = SOLID_NOT;
940 VectorSet(ent->mins, -64, -64, 0);
941 VectorSet(ent->maxs, 64, 64, 8);
942 ent->s.modelindex = gi.modelindex("models/objects/black/tris.md2");
943 ent->s.renderfx = RF_TRANSLUCENT;
944 ent->use = misc_blackhole_use;
945 ent->think = misc_blackhole_think;
946 ent->nextthink = level.time + 2 * FRAMETIME;
947 gi.linkentity(ent);
948 }
949
950 /*QUAKED misc_eastertank(1 .5 0)(-32 -32 -16)(32 32 32)
951 */
952
misc_eastertank_think(edict_t * self)953 void misc_eastertank_think(edict_t *self){
954 if(++self->s.frame < 293)
955 self->nextthink = level.time + FRAMETIME;
956 else {
957 self->s.frame = 254;
958 self->nextthink = level.time + FRAMETIME;
959 }
960 }
961
SP_misc_eastertank(edict_t * ent)962 void SP_misc_eastertank(edict_t *ent){
963 ent->movetype = MOVETYPE_NONE;
964 ent->solid = SOLID_BBOX;
965 VectorSet(ent->mins, -32, -32, -16);
966 VectorSet(ent->maxs, 32, 32, 32);
967 ent->s.modelindex = gi.modelindex("models/monsters/tank/tris.md2");
968 ent->s.frame = 254;
969 ent->think = misc_eastertank_think;
970 ent->nextthink = level.time + 2 * FRAMETIME;
971 gi.linkentity(ent);
972 }
973
974 /*QUAKED misc_easterchick(1 .5 0)(-32 -32 0)(32 32 32)
975 */
976
977
misc_easterchick_think(edict_t * self)978 void misc_easterchick_think(edict_t *self){
979 if(++self->s.frame < 247)
980 self->nextthink = level.time + FRAMETIME;
981 else {
982 self->s.frame = 208;
983 self->nextthink = level.time + FRAMETIME;
984 }
985 }
986
SP_misc_easterchick(edict_t * ent)987 void SP_misc_easterchick(edict_t *ent){
988 ent->movetype = MOVETYPE_NONE;
989 ent->solid = SOLID_BBOX;
990 VectorSet(ent->mins, -32, -32, 0);
991 VectorSet(ent->maxs, 32, 32, 32);
992 ent->s.modelindex = gi.modelindex("models/monsters/bitch/tris.md2");
993 ent->s.frame = 208;
994 ent->think = misc_easterchick_think;
995 ent->nextthink = level.time + 2 * FRAMETIME;
996 gi.linkentity(ent);
997 }
998
999 /*QUAKED misc_easterchick2(1 .5 0)(-32 -32 0)(32 32 32)
1000 */
1001
1002
misc_easterchick2_think(edict_t * self)1003 void misc_easterchick2_think(edict_t *self){
1004 if(++self->s.frame < 287)
1005 self->nextthink = level.time + FRAMETIME;
1006 else {
1007 self->s.frame = 248;
1008 self->nextthink = level.time + FRAMETIME;
1009 }
1010 }
1011
SP_misc_easterchick2(edict_t * ent)1012 void SP_misc_easterchick2(edict_t *ent){
1013 ent->movetype = MOVETYPE_NONE;
1014 ent->solid = SOLID_BBOX;
1015 VectorSet(ent->mins, -32, -32, 0);
1016 VectorSet(ent->maxs, 32, 32, 32);
1017 ent->s.modelindex = gi.modelindex("models/monsters/bitch/tris.md2");
1018 ent->s.frame = 248;
1019 ent->think = misc_easterchick2_think;
1020 ent->nextthink = level.time + 2 * FRAMETIME;
1021 gi.linkentity(ent);
1022 }
1023
1024
1025 /*QUAKED monster_commander_body(1 .5 0)(-32 -32 0)(32 32 48)
1026 Not really a monster, this is the Tank Commander's decapitated body.
1027 There should be a item_commander_head that has this as it's target.
1028 */
1029
commander_body_think(edict_t * self)1030 void commander_body_think(edict_t *self){
1031 if(++self->s.frame < 24)
1032 self->nextthink = level.time + FRAMETIME;
1033 else
1034 self->nextthink = 0;
1035
1036 if(self->s.frame == 22)
1037 gi.sound(self, CHAN_BODY, gi.soundindex("tank/thud.wav"), 1, ATTN_NORM, 0);
1038 }
1039
commander_body_use(edict_t * self,edict_t * other,edict_t * activator)1040 void commander_body_use(edict_t *self, edict_t *other, edict_t *activator){
1041 self->think = commander_body_think;
1042 self->nextthink = level.time + FRAMETIME;
1043 gi.sound(self, CHAN_BODY, gi.soundindex("tank/pain.wav"), 1, ATTN_NORM, 0);
1044 }
1045
commander_body_drop(edict_t * self)1046 void commander_body_drop(edict_t *self){
1047 self->movetype = MOVETYPE_TOSS;
1048 self->s.origin[2] += 2;
1049 }
1050
SP_monster_commander_body(edict_t * self)1051 void SP_monster_commander_body(edict_t *self){
1052 self->movetype = MOVETYPE_NONE;
1053 self->solid = SOLID_BBOX;
1054 self->model = "models/monsters/commandr/tris.md2";
1055 self->s.modelindex = gi.modelindex(self->model);
1056 VectorSet(self->mins, -32, -32, 0);
1057 VectorSet(self->maxs, 32, 32, 48);
1058 self->use = commander_body_use;
1059 self->takedamage = DAMAGE_YES;
1060 self->flags = FL_GODMODE;
1061 self->s.renderfx |= RF_FRAMELERP;
1062 gi.linkentity(self);
1063
1064 gi.soundindex("tank/thud.wav");
1065 gi.soundindex("tank/pain.wav");
1066
1067 self->think = commander_body_drop;
1068 self->nextthink = level.time + 5 * FRAMETIME;
1069 }
1070
1071
1072 /*QUAKED misc_banner(1 .5 0)(-4 -4 -4)(4 4 4)
1073 The origin is the bottom of the banner.
1074 The banner is 128 tall.
1075 */
misc_banner_think(edict_t * ent)1076 void misc_banner_think(edict_t *ent){
1077 ent->s.frame =(ent->s.frame + 1) % 16;
1078 ent->nextthink = level.time + FRAMETIME;
1079 }
1080
SP_misc_banner(edict_t * ent)1081 void SP_misc_banner(edict_t *ent){
1082 ent->movetype = MOVETYPE_NONE;
1083 ent->solid = SOLID_NOT;
1084 ent->s.modelindex = gi.modelindex("models/objects/banner/tris.md2");
1085 ent->s.frame = rand() % 16;
1086 gi.linkentity(ent);
1087
1088 ent->think = misc_banner_think;
1089 ent->nextthink = level.time + FRAMETIME;
1090 }
1091
1092 /*QUAKED misc_deadsoldier(1 .5 0)(-16 -16 0)(16 16 16) ON_BACK ON_STOMACH BACK_DECAP FETAL_POS SIT_DECAP IMPALED
1093 This is the dead player model. Comes in 6 exciting different poses!
1094 */
misc_deadsoldier_die(edict_t * self,edict_t * inflictor,edict_t * attacker,int damage,vec3_t point)1095 void misc_deadsoldier_die(edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point){
1096 int n;
1097
1098 if(self->health > -80)
1099 return;
1100
1101 gi.sound(self, CHAN_BODY, gi.soundindex("misc/udeath.wav"), 1, ATTN_NORM, 0);
1102 for(n = 0; n < 4; n++)
1103 ThrowGib(self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC);
1104 ThrowHead(self, "models/objects/gibs/head2/tris.md2", damage, GIB_ORGANIC);
1105 }
1106
SP_misc_deadsoldier(edict_t * ent)1107 void SP_misc_deadsoldier(edict_t *ent){
1108 if(deathmatch->value){ // auto-remove for deathmatch
1109 G_FreeEdict(ent);
1110 return;
1111 }
1112
1113 ent->movetype = MOVETYPE_NONE;
1114 ent->solid = SOLID_BBOX;
1115 ent->s.modelindex = gi.modelindex("models/deadbods/dude/tris.md2");
1116
1117 // Defaults to frame 0
1118 if(ent->spawnflags & 2)
1119 ent->s.frame = 1;
1120 else if(ent->spawnflags & 4)
1121 ent->s.frame = 2;
1122 else if(ent->spawnflags & 8)
1123 ent->s.frame = 3;
1124 else if(ent->spawnflags & 16)
1125 ent->s.frame = 4;
1126 else if(ent->spawnflags & 32)
1127 ent->s.frame = 5;
1128 else
1129 ent->s.frame = 0;
1130
1131 VectorSet(ent->mins, -16, -16, 0);
1132 VectorSet(ent->maxs, 16, 16, 16);
1133 ent->deadflag = DEAD_DEAD;
1134 ent->takedamage = DAMAGE_YES;
1135 ent->svflags |= SVF_MONSTER | SVF_DEADMONSTER;
1136 ent->die = misc_deadsoldier_die;
1137 ent->monsterinfo.aiflags |= AI_GOOD_GUY;
1138
1139 gi.linkentity(ent);
1140 }
1141
1142 /*QUAKED misc_viper(1 .5 0)(-16 -16 0)(16 16 32)
1143 This is the Viper for the flyby bombing.
1144 It is trigger_spawned, so you must have something use it for it to show up.
1145 There must be a path for it to follow once it is activated.
1146
1147 "speed" How fast the Viper should fly
1148 */
1149
1150 extern void train_use(edict_t *self, edict_t *other, edict_t *activator);
1151 extern void func_train_find(edict_t *self);
1152
misc_viper_use(edict_t * self,edict_t * other,edict_t * activator)1153 void misc_viper_use(edict_t *self, edict_t *other, edict_t *activator){
1154 self->svflags &= ~SVF_NOCLIENT;
1155 self->use = train_use;
1156 train_use(self, other, activator);
1157 }
1158
SP_misc_viper(edict_t * ent)1159 void SP_misc_viper(edict_t *ent){
1160 if(!ent->target){
1161 gi.dprintf("misc_viper without a target at %s\n", vtos(ent->absmin));
1162 G_FreeEdict(ent);
1163 return;
1164 }
1165
1166 if(!ent->speed)
1167 ent->speed = 300;
1168
1169 ent->movetype = MOVETYPE_PUSH;
1170 ent->solid = SOLID_NOT;
1171 ent->s.modelindex = gi.modelindex("models/ships/viper/tris.md2");
1172 VectorSet(ent->mins, -16, -16, 0);
1173 VectorSet(ent->maxs, 16, 16, 32);
1174
1175 ent->think = func_train_find;
1176 ent->nextthink = level.time + FRAMETIME;
1177 ent->use = misc_viper_use;
1178 ent->svflags |= SVF_NOCLIENT;
1179 ent->moveinfo.accel = ent->moveinfo.decel = ent->moveinfo.speed = ent->speed;
1180
1181 gi.linkentity(ent);
1182 }
1183
1184
1185 /*QUAKED misc_bigviper(1 .5 0)(-176 -120 -24)(176 120 72)
1186 This is a large stationary viper as seen in Paul's intro
1187 */
SP_misc_bigviper(edict_t * ent)1188 void SP_misc_bigviper(edict_t *ent){
1189 ent->movetype = MOVETYPE_NONE;
1190 ent->solid = SOLID_BBOX;
1191 VectorSet(ent->mins, -176, -120, -24);
1192 VectorSet(ent->maxs, 176, 120, 72);
1193 ent->s.modelindex = gi.modelindex("models/ships/bigviper/tris.md2");
1194 gi.linkentity(ent);
1195 }
1196
1197
1198 /*QUAKED misc_viper_bomb(1 0 0)(-8 -8 -8)(8 8 8)
1199 "dmg" how much boom should the bomb make?
1200 */
misc_viper_bomb_touch(edict_t * self,edict_t * other,cplane_t * plane,csurface_t * surf)1201 void misc_viper_bomb_touch(edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf){
1202 G_UseTargets(self, self->activator);
1203
1204 self->s.origin[2] = self->absmin[2] + 1;
1205 T_RadiusDamage(self, self, self->dmg, NULL, self->dmg + 40, MOD_BOMB);
1206 BecomeExplosion2(self);
1207 }
1208
misc_viper_bomb_prethink(edict_t * self)1209 void misc_viper_bomb_prethink(edict_t *self){
1210 vec3_t v;
1211 float diff;
1212
1213 self->groundentity = NULL;
1214
1215 diff = self->timestamp - level.time;
1216 if(diff < -1.0)
1217 diff = -1.0;
1218
1219 VectorScale(self->moveinfo.dir, 1.0 + diff, v);
1220 v[2] = diff;
1221
1222 diff = self->s.angles[2];
1223 vectoangles(v, self->s.angles);
1224 self->s.angles[2] = diff + 10;
1225 }
1226
misc_viper_bomb_use(edict_t * self,edict_t * other,edict_t * activator)1227 void misc_viper_bomb_use(edict_t *self, edict_t *other, edict_t *activator){
1228 edict_t *viper;
1229
1230 self->solid = SOLID_BBOX;
1231 self->svflags &= ~SVF_NOCLIENT;
1232 self->s.effects |= EF_ROCKET;
1233 self->use = NULL;
1234 self->movetype = MOVETYPE_TOSS;
1235 self->prethink = misc_viper_bomb_prethink;
1236 self->touch = misc_viper_bomb_touch;
1237 self->activator = activator;
1238
1239 viper = G_Find(NULL, FOFS(classname), "misc_viper");
1240 VectorScale(viper->moveinfo.dir, viper->moveinfo.speed, self->velocity);
1241
1242 self->timestamp = level.time;
1243 VectorCopy(viper->moveinfo.dir, self->moveinfo.dir);
1244 }
1245
SP_misc_viper_bomb(edict_t * self)1246 void SP_misc_viper_bomb(edict_t *self){
1247 self->movetype = MOVETYPE_NONE;
1248 self->solid = SOLID_NOT;
1249 VectorSet(self->mins, -8, -8, -8);
1250 VectorSet(self->maxs, 8, 8, 8);
1251
1252 self->s.modelindex = gi.modelindex("models/objects/bomb/tris.md2");
1253
1254 if(!self->dmg)
1255 self->dmg = 1000;
1256
1257 self->use = misc_viper_bomb_use;
1258 self->svflags |= SVF_NOCLIENT;
1259
1260 gi.linkentity(self);
1261 }
1262
1263
1264 /*QUAKED misc_strogg_ship(1 .5 0)(-16 -16 0)(16 16 32)
1265 This is a Storgg ship for the flybys.
1266 It is trigger_spawned, so you must have something use it for it to show up.
1267 There must be a path for it to follow once it is activated.
1268
1269 "speed" How fast it should fly
1270 */
1271
1272 extern void train_use(edict_t *self, edict_t *other, edict_t *activator);
1273 extern void func_train_find(edict_t *self);
1274
misc_strogg_ship_use(edict_t * self,edict_t * other,edict_t * activator)1275 void misc_strogg_ship_use(edict_t *self, edict_t *other, edict_t *activator){
1276 self->svflags &= ~SVF_NOCLIENT;
1277 self->use = train_use;
1278 train_use(self, other, activator);
1279 }
1280
SP_misc_strogg_ship(edict_t * ent)1281 void SP_misc_strogg_ship(edict_t *ent){
1282 if(!ent->target){
1283 gi.dprintf("%s without a target at %s\n", ent->classname, vtos(ent->absmin));
1284 G_FreeEdict(ent);
1285 return;
1286 }
1287
1288 if(!ent->speed)
1289 ent->speed = 300;
1290
1291 ent->movetype = MOVETYPE_PUSH;
1292 ent->solid = SOLID_NOT;
1293 ent->s.modelindex = gi.modelindex("models/ships/strogg1/tris.md2");
1294 VectorSet(ent->mins, -16, -16, 0);
1295 VectorSet(ent->maxs, 16, 16, 32);
1296
1297 ent->think = func_train_find;
1298 ent->nextthink = level.time + FRAMETIME;
1299 ent->use = misc_strogg_ship_use;
1300 ent->svflags |= SVF_NOCLIENT;
1301 ent->moveinfo.accel = ent->moveinfo.decel = ent->moveinfo.speed = ent->speed;
1302
1303 gi.linkentity(ent);
1304 }
1305
1306
1307 /*QUAKED misc_satellite_dish(1 .5 0)(-64 -64 0)(64 64 128)
1308 */
misc_satellite_dish_think(edict_t * self)1309 void misc_satellite_dish_think(edict_t *self){
1310 self->s.frame++;
1311 if(self->s.frame < 38)
1312 self->nextthink = level.time + FRAMETIME;
1313 }
1314
misc_satellite_dish_use(edict_t * self,edict_t * other,edict_t * activator)1315 void misc_satellite_dish_use(edict_t *self, edict_t *other, edict_t *activator){
1316 self->s.frame = 0;
1317 self->think = misc_satellite_dish_think;
1318 self->nextthink = level.time + FRAMETIME;
1319 }
1320
SP_misc_satellite_dish(edict_t * ent)1321 void SP_misc_satellite_dish(edict_t *ent){
1322 ent->movetype = MOVETYPE_NONE;
1323 ent->solid = SOLID_BBOX;
1324 VectorSet(ent->mins, -64, -64, 0);
1325 VectorSet(ent->maxs, 64, 64, 128);
1326 ent->s.modelindex = gi.modelindex("models/objects/satellite/tris.md2");
1327 ent->use = misc_satellite_dish_use;
1328 gi.linkentity(ent);
1329 }
1330
1331
1332 /*QUAKED light_mine1(0 1 0)(-2 -2 -12)(2 2 12)
1333 */
SP_light_mine1(edict_t * ent)1334 void SP_light_mine1(edict_t *ent){
1335 ent->movetype = MOVETYPE_NONE;
1336 ent->solid = SOLID_BBOX;
1337 ent->s.modelindex = gi.modelindex("models/objects/minelite/light1/tris.md2");
1338 gi.linkentity(ent);
1339 }
1340
1341
1342 /*QUAKED light_mine2(0 1 0)(-2 -2 -12)(2 2 12)
1343 */
SP_light_mine2(edict_t * ent)1344 void SP_light_mine2(edict_t *ent){
1345 ent->movetype = MOVETYPE_NONE;
1346 ent->solid = SOLID_BBOX;
1347 ent->s.modelindex = gi.modelindex("models/objects/minelite/light2/tris.md2");
1348 gi.linkentity(ent);
1349 }
1350
1351
1352 /*QUAKED misc_gib_arm(1 0 0)(-8 -8 -8)(8 8 8)
1353 Intended for use with the target_spawner
1354 */
SP_misc_gib_arm(edict_t * ent)1355 void SP_misc_gib_arm(edict_t *ent){
1356 gi.setmodel(ent, "models/objects/gibs/arm/tris.md2");
1357 ent->solid = SOLID_NOT;
1358 ent->s.effects |= EF_GIB;
1359 ent->takedamage = DAMAGE_YES;
1360 ent->die = gib_die;
1361 ent->movetype = MOVETYPE_TOSS;
1362 ent->svflags |= SVF_MONSTER;
1363 ent->deadflag = DEAD_DEAD;
1364 ent->avelocity[0] = random() * 200;
1365 ent->avelocity[1] = random() * 200;
1366 ent->avelocity[2] = random() * 200;
1367 ent->think = G_FreeEdict;
1368 ent->nextthink = level.time + 30;
1369 gi.linkentity(ent);
1370 }
1371
1372 /*QUAKED misc_gib_leg(1 0 0)(-8 -8 -8)(8 8 8)
1373 Intended for use with the target_spawner
1374 */
SP_misc_gib_leg(edict_t * ent)1375 void SP_misc_gib_leg(edict_t *ent){
1376 gi.setmodel(ent, "models/objects/gibs/leg/tris.md2");
1377 ent->solid = SOLID_NOT;
1378 ent->s.effects |= EF_GIB;
1379 ent->takedamage = DAMAGE_YES;
1380 ent->die = gib_die;
1381 ent->movetype = MOVETYPE_TOSS;
1382 ent->svflags |= SVF_MONSTER;
1383 ent->deadflag = DEAD_DEAD;
1384 ent->avelocity[0] = random() * 200;
1385 ent->avelocity[1] = random() * 200;
1386 ent->avelocity[2] = random() * 200;
1387 ent->think = G_FreeEdict;
1388 ent->nextthink = level.time + 30;
1389 gi.linkentity(ent);
1390 }
1391
1392 /*QUAKED misc_gib_head(1 0 0)(-8 -8 -8)(8 8 8)
1393 Intended for use with the target_spawner
1394 */
SP_misc_gib_head(edict_t * ent)1395 void SP_misc_gib_head(edict_t *ent){
1396 gi.setmodel(ent, "models/objects/gibs/head/tris.md2");
1397 ent->solid = SOLID_NOT;
1398 ent->s.effects |= EF_GIB;
1399 ent->takedamage = DAMAGE_YES;
1400 ent->die = gib_die;
1401 ent->movetype = MOVETYPE_TOSS;
1402 ent->svflags |= SVF_MONSTER;
1403 ent->deadflag = DEAD_DEAD;
1404 ent->avelocity[0] = random() * 200;
1405 ent->avelocity[1] = random() * 200;
1406 ent->avelocity[2] = random() * 200;
1407 ent->think = G_FreeEdict;
1408 ent->nextthink = level.time + 30;
1409 gi.linkentity(ent);
1410 }
1411
1412
1413 /*QUAKED target_character(0 0 1) ?
1414 used with target_string(must be on same "team")
1415 "count" is position in the string(starts at 1)
1416 */
1417
SP_target_character(edict_t * self)1418 void SP_target_character(edict_t *self){
1419 self->movetype = MOVETYPE_PUSH;
1420 gi.setmodel(self, self->model);
1421 self->solid = SOLID_BSP;
1422 self->s.frame = 12;
1423 gi.linkentity(self);
1424 return;
1425 }
1426
1427
1428 /*QUAKED target_string(0 0 1)(-8 -8 -8)(8 8 8)
1429 */
1430
target_string_use(edict_t * self,edict_t * other,edict_t * activator)1431 void target_string_use(edict_t *self, edict_t *other, edict_t *activator){
1432 edict_t *e;
1433 int n, l;
1434 char c;
1435
1436 l = strlen(self->message);
1437 for(e = self->teammaster; e; e = e->teamchain){
1438 if(!e->count)
1439 continue;
1440 n = e->count - 1;
1441 if(n > l){
1442 e->s.frame = 12;
1443 continue;
1444 }
1445
1446 c = self->message[n];
1447 if(c >= '0' && c <= '9')
1448 e->s.frame = c - '0';
1449 else if(c == '-')
1450 e->s.frame = 10;
1451 else if(c == ':')
1452 e->s.frame = 11;
1453 else
1454 e->s.frame = 12;
1455 }
1456 }
1457
SP_target_string(edict_t * self)1458 void SP_target_string(edict_t *self){
1459 if(!self->message)
1460 self->message = "";
1461 self->use = target_string_use;
1462 }
1463
1464
1465 /*QUAKED func_clock(0 0 1)(-8 -8 -8)(8 8 8) TIMER_UP TIMER_DOWN START_OFF MULTI_USE
1466 target a target_string with this
1467
1468 The default is to be a time of day clock
1469
1470 TIMER_UP and TIMER_DOWN run for "count" seconds and the fire "pathtarget"
1471 If START_OFF, this entity must be used before it starts
1472
1473 "style" 0 "xx"
1474 1 "xx:xx"
1475 2 "xx:xx:xx"
1476 */
1477
1478 #define CLOCK_MESSAGE_SIZE 16
1479
1480 // don't let field width of any clock messages change, or it
1481 // could cause an overwrite after a game load
1482
func_clock_reset(edict_t * self)1483 static void func_clock_reset(edict_t *self){
1484 self->activator = NULL;
1485 if(self->spawnflags & 1){
1486 self->health = 0;
1487 self->wait = self->count;
1488 } else if(self->spawnflags & 2){
1489 self->health = self->count;
1490 self->wait = 0;
1491 }
1492 }
1493
func_clock_format_countdown(edict_t * self)1494 static void func_clock_format_countdown(edict_t *self){
1495 if(self->style == 0){
1496 Com_sprintf(self->message, CLOCK_MESSAGE_SIZE, "%2i", self->health);
1497 return;
1498 }
1499
1500 if(self->style == 1){
1501 Com_sprintf(self->message, CLOCK_MESSAGE_SIZE, "%2i:%2i", self->health / 60, self->health % 60);
1502 if(self->message[3] == ' ')
1503 self->message[3] = '0';
1504 return;
1505 }
1506
1507 if(self->style == 2){
1508 Com_sprintf(self->message, CLOCK_MESSAGE_SIZE, "%2i:%2i:%2i", self->health / 3600,(self->health -(self->health / 3600) * 3600) / 60, self->health % 60);
1509 if(self->message[3] == ' ')
1510 self->message[3] = '0';
1511 if(self->message[6] == ' ')
1512 self->message[6] = '0';
1513 return;
1514 }
1515 }
1516
func_clock_think(edict_t * self)1517 void func_clock_think(edict_t *self){
1518 if(!self->enemy){
1519 self->enemy = G_Find(NULL, FOFS(targetname), self->target);
1520 if(!self->enemy)
1521 return;
1522 }
1523
1524 if(self->spawnflags & 1){
1525 func_clock_format_countdown(self);
1526 self->health++;
1527 } else if(self->spawnflags & 2){
1528 func_clock_format_countdown(self);
1529 self->health--;
1530 } else {
1531 struct tm *ltime;
1532 time_t gmtime;
1533
1534 time(&gmtime);
1535 ltime = localtime(&gmtime);
1536 Com_sprintf(self->message, CLOCK_MESSAGE_SIZE, "%2i:%2i:%2i", ltime->tm_hour, ltime->tm_min, ltime->tm_sec);
1537 if(self->message[3] == ' ')
1538 self->message[3] = '0';
1539 if(self->message[6] == ' ')
1540 self->message[6] = '0';
1541 }
1542
1543 self->enemy->message = self->message;
1544 self->enemy->use(self->enemy, self, self);
1545
1546 if(((self->spawnflags & 1) &&(self->health > self->wait)) ||
1547 ((self->spawnflags & 2) &&(self->health < self->wait))){
1548 if(self->pathtarget){
1549 char *savetarget;
1550 char *savemessage;
1551
1552 savetarget = self->target;
1553 savemessage = self->message;
1554 self->target = self->pathtarget;
1555 self->message = NULL;
1556 G_UseTargets(self, self->activator);
1557 self->target = savetarget;
1558 self->message = savemessage;
1559 }
1560
1561 if(!(self->spawnflags & 8))
1562 return;
1563
1564 func_clock_reset(self);
1565
1566 if(self->spawnflags & 4)
1567 return;
1568 }
1569
1570 self->nextthink = level.time + 1;
1571 }
1572
func_clock_use(edict_t * self,edict_t * other,edict_t * activator)1573 void func_clock_use(edict_t *self, edict_t *other, edict_t *activator){
1574 if(!(self->spawnflags & 8))
1575 self->use = NULL;
1576 if(self->activator)
1577 return;
1578 self->activator = activator;
1579 self->think(self);
1580 }
1581
SP_func_clock(edict_t * self)1582 void SP_func_clock(edict_t *self){
1583 if(!self->target){
1584 gi.dprintf("%s with no target at %s\n", self->classname, vtos(self->s.origin));
1585 G_FreeEdict(self);
1586 return;
1587 }
1588
1589 if((self->spawnflags & 2) &&(!self->count)){
1590 gi.dprintf("%s with no count at %s\n", self->classname, vtos(self->s.origin));
1591 G_FreeEdict(self);
1592 return;
1593 }
1594
1595 if((self->spawnflags & 1) &&(!self->count))
1596 self->count = 60 * 60;
1597 ;
1598
1599 func_clock_reset(self);
1600
1601 self->message = gi.TagMalloc(CLOCK_MESSAGE_SIZE, TAG_LEVEL);
1602
1603 self->think = func_clock_think;
1604
1605 if(self->spawnflags & 4)
1606 self->use = func_clock_use;
1607 else
1608 self->nextthink = level.time + 1;
1609 }
1610
1611
teleporter_touch(edict_t * self,edict_t * other,cplane_t * plane,csurface_t * surf)1612 void teleporter_touch(edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf){
1613 edict_t *dest;
1614 int i;
1615
1616 if(!other->client)
1617 return;
1618 dest = G_Find(NULL, FOFS(targetname), self->target);
1619 if(!dest){
1620 gi.dprintf("Couldn't find destination\n");
1621 return;
1622 }
1623
1624 // unlink to make sure it can't possibly interfere with KillBox
1625 gi.unlinkentity(other);
1626
1627 VectorCopy(dest->s.origin, other->s.origin);
1628 VectorCopy(dest->s.origin, other->s.old_origin);
1629 other->s.origin[2] += 10;
1630
1631 // clear the velocity and hold them in place briefly
1632 VectorClear(other->velocity);
1633 other->client->ps.pmove.pm_time = 160 >> 3; // hold time
1634 other->client->ps.pmove.pm_flags |= PMF_TIME_TELEPORT;
1635
1636 // draw the teleport splash at source and on the player
1637 self->owner->s.event = EV_PLAYER_TELEPORT;
1638 other->s.event = EV_PLAYER_TELEPORT;
1639
1640 // set angles
1641 for(i = 0; i < 3; i++){
1642 other->client->ps.pmove.delta_angles[i] = ANGLE2SHORT(dest->s.angles[i] - other->client->resp.cmd_angles[i]);
1643 }
1644
1645 VectorClear(other->s.angles);
1646 VectorClear(other->client->ps.viewangles);
1647 VectorClear(other->client->v_angle);
1648
1649 // kill anything at the destination
1650 KillBox(other);
1651
1652 gi.linkentity(other);
1653 }
1654
1655 /*QUAKED misc_teleporter(1 0 0)(-32 -32 -24)(32 32 -16)
1656 Stepping onto this disc will teleport players to the targeted misc_teleporter_dest object.
1657 */
SP_misc_teleporter(edict_t * ent)1658 void SP_misc_teleporter(edict_t *ent){
1659 edict_t *trig;
1660
1661 if(!ent->target){
1662 gi.dprintf("teleporter without a target.\n");
1663 G_FreeEdict(ent);
1664 return;
1665 }
1666
1667 gi.setmodel(ent, "models/objects/dmspot/tris.md2");
1668 ent->s.skinnum = 1;
1669 ent->s.effects = EF_TELEPORTER;
1670 ent->s.sound = gi.soundindex("world/amb10.wav");
1671 ent->solid = SOLID_BBOX;
1672
1673 VectorSet(ent->mins, -32, -32, -24);
1674 VectorSet(ent->maxs, 32, 32, -16);
1675 gi.linkentity(ent);
1676
1677 trig = G_Spawn();
1678 trig->touch = teleporter_touch;
1679 trig->solid = SOLID_TRIGGER;
1680 trig->target = ent->target;
1681 trig->owner = ent;
1682 VectorCopy(ent->s.origin, trig->s.origin);
1683 VectorSet(trig->mins, -8, -8, 8);
1684 VectorSet(trig->maxs, 8, 8, 24);
1685 gi.linkentity(trig);
1686
1687 }
1688
1689 /*QUAKED misc_teleporter_dest(1 0 0)(-32 -32 -24)(32 32 -16)
1690 Point teleporters at these.
1691 */
SP_misc_teleporter_dest(edict_t * ent)1692 void SP_misc_teleporter_dest(edict_t *ent){
1693 gi.setmodel(ent, "models/objects/dmspot/tris.md2");
1694 ent->s.skinnum = 0;
1695 ent->solid = SOLID_BBOX;
1696 // ent->s.effects |= EF_FLIES;
1697 VectorSet(ent->mins, -32, -32, -24);
1698 VectorSet(ent->maxs, 32, 32, -16);
1699 gi.linkentity(ent);
1700 }
1701
1702