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