|
|
1.1 root 1: // g_turret.c
2:
3: #include "g_local.h"
4:
5: void SpawnTargetingSystem (edict_t *turret); // PGM
6:
7: void AnglesNormalize(vec3_t vec)
8: {
9: while(vec[0] > 360)
10: vec[0] -= 360;
11: while(vec[0] < 0)
12: vec[0] += 360;
13: while(vec[1] > 360)
14: vec[1] -= 360;
15: while(vec[1] < 0)
16: vec[1] += 360;
17: }
18:
19: float SnapToEights(float x)
20: {
21: x *= 8.0;
22: if (x > 0.0)
23: x += 0.5;
24: else
25: x -= 0.5;
26: return 0.125 * (int)x;
27: }
28:
29:
30: void turret_blocked(edict_t *self, edict_t *other)
31: {
32: edict_t *attacker;
33:
34: if (other->takedamage)
35: {
36: if (self->teammaster->owner)
37: attacker = self->teammaster->owner;
38: else
39: attacker = self->teammaster;
40: T_Damage (other, self, attacker, vec3_origin, other->s.origin, vec3_origin, self->teammaster->dmg, 10, 0, MOD_CRUSH);
41: }
42: }
43:
44: /*QUAKED turret_breach (0 0 0) ?
45: This portion of the turret can change both pitch and yaw.
46: The model should be made with a flat pitch.
47: It (and the associated base) need to be oriented towards 0.
48: Use "angle" to set the starting angle.
49:
50: "speed" default 50
51: "dmg" default 10
52: "angle" point this forward
53: "target" point this at an info_notnull at the muzzle tip
54: "minpitch" min acceptable pitch angle : default -30
55: "maxpitch" max acceptable pitch angle : default 30
56: "minyaw" min acceptable yaw angle : default 0
57: "maxyaw" max acceptable yaw angle : default 360
58: */
59:
60: void turret_breach_fire (edict_t *self)
61: {
62: vec3_t f, r, u;
63: vec3_t start;
64: int damage;
65: int speed;
66:
67: AngleVectors (self->s.angles, f, r, u);
68: VectorMA (self->s.origin, self->move_origin[0], f, start);
69: VectorMA (start, self->move_origin[1], r, start);
70: VectorMA (start, self->move_origin[2], u, start);
71:
72: damage = 100 + random() * 50;
73: speed = 550 + 50 * skill->value;
74: fire_rocket (self->teammaster->owner, start, f, damage, speed, 150, damage);
75: gi.positioned_sound (start, self, CHAN_WEAPON, gi.soundindex("weapons/rocklf1a.wav"), 1, ATTN_NORM, 0);
76: }
77:
78: void turret_breach_think (edict_t *self)
79: {
80: edict_t *ent;
81: vec3_t current_angles;
82: vec3_t delta;
83:
84: VectorCopy (self->s.angles, current_angles);
85: AnglesNormalize(current_angles);
86:
87: AnglesNormalize(self->move_angles);
88: if (self->move_angles[PITCH] > 180)
89: self->move_angles[PITCH] -= 360;
90:
91: // clamp angles to mins & maxs
92: if (self->move_angles[PITCH] > self->pos1[PITCH])
93: self->move_angles[PITCH] = self->pos1[PITCH];
94: else if (self->move_angles[PITCH] < self->pos2[PITCH])
95: self->move_angles[PITCH] = self->pos2[PITCH];
96:
97: if ((self->move_angles[YAW] < self->pos1[YAW]) || (self->move_angles[YAW] > self->pos2[YAW]))
98: {
99: float dmin, dmax;
100:
101: dmin = fabs(self->pos1[YAW] - self->move_angles[YAW]);
102: if (dmin < -180)
103: dmin += 360;
104: else if (dmin > 180)
105: dmin -= 360;
106: dmax = fabs(self->pos2[YAW] - self->move_angles[YAW]);
107: if (dmax < -180)
108: dmax += 360;
109: else if (dmax > 180)
110: dmax -= 360;
111: if (fabs(dmin) < fabs(dmax))
112: self->move_angles[YAW] = self->pos1[YAW];
113: else
114: self->move_angles[YAW] = self->pos2[YAW];
115: }
116:
117: VectorSubtract (self->move_angles, current_angles, delta);
118: if (delta[0] < -180)
119: delta[0] += 360;
120: else if (delta[0] > 180)
121: delta[0] -= 360;
122: if (delta[1] < -180)
123: delta[1] += 360;
124: else if (delta[1] > 180)
125: delta[1] -= 360;
126: delta[2] = 0;
127:
128: if (delta[0] > self->speed * FRAMETIME)
129: delta[0] = self->speed * FRAMETIME;
130: if (delta[0] < -1 * self->speed * FRAMETIME)
131: delta[0] = -1 * self->speed * FRAMETIME;
132: if (delta[1] > self->speed * FRAMETIME)
133: delta[1] = self->speed * FRAMETIME;
134: if (delta[1] < -1 * self->speed * FRAMETIME)
135: delta[1] = -1 * self->speed * FRAMETIME;
136:
137: VectorScale (delta, 1.0/FRAMETIME, self->avelocity);
138:
139: self->nextthink = level.time + FRAMETIME;
140:
141: for (ent = self->teammaster; ent; ent = ent->teamchain)
142: ent->avelocity[1] = self->avelocity[1];
143:
144: // if we have adriver, adjust his velocities
145: if (self->owner)
146: {
147: float angle;
148: float target_z;
149: float diff;
150: vec3_t target;
151: vec3_t dir;
152:
153: // angular is easy, just copy ours
154: self->owner->avelocity[0] = self->avelocity[0];
155: self->owner->avelocity[1] = self->avelocity[1];
156:
157: // x & y
158: angle = self->s.angles[1] + self->owner->move_origin[1];
159: angle *= (M_PI*2 / 360);
160: target[0] = SnapToEights(self->s.origin[0] + cos(angle) * self->owner->move_origin[0]);
161: target[1] = SnapToEights(self->s.origin[1] + sin(angle) * self->owner->move_origin[0]);
162: target[2] = self->owner->s.origin[2];
163:
164: VectorSubtract (target, self->owner->s.origin, dir);
165: self->owner->velocity[0] = dir[0] * 1.0 / FRAMETIME;
166: self->owner->velocity[1] = dir[1] * 1.0 / FRAMETIME;
167:
168: // z
169: angle = self->s.angles[PITCH] * (M_PI*2 / 360);
170: target_z = SnapToEights(self->s.origin[2] + self->owner->move_origin[0] * tan(angle) + self->owner->move_origin[2]);
171:
172: diff = target_z - self->owner->s.origin[2];
173: self->owner->velocity[2] = diff * 1.0 / FRAMETIME;
174:
175: if (self->spawnflags & 65536)
176: {
177: turret_breach_fire (self);
178: self->spawnflags &= ~65536;
179: }
180: }
181: }
182:
183: void turret_breach_finish_init (edict_t *self)
184: {
185: // get and save info for muzzle location
186: if (!self->target)
187: {
188: gi.dprintf("%s at %s needs a target\n", self->classname, vtos(self->s.origin));
189: }
190: else
191: {
192: self->target_ent = G_PickTarget (self->target);
193: if(self->target_ent)
194: {
195: VectorSubtract (self->target_ent->s.origin, self->s.origin, self->move_origin);
196: G_FreeEdict(self->target_ent);
197: }
198: else
199: gi.dprintf("could not find target entity for %s at %s\n", self->classname, vtos(self->s.origin));
200: }
201:
202: self->teammaster->dmg = self->dmg;
203: self->think = turret_breach_think;
204: self->think (self);
205: }
206:
207: void SP_turret_breach (edict_t *self)
208: {
209: self->solid = SOLID_BSP;
210: self->movetype = MOVETYPE_PUSH;
211: gi.setmodel (self, self->model);
212:
213: if (!self->speed)
214: self->speed = 50;
215: if (!self->dmg)
216: self->dmg = 10;
217:
218: if (!st.minpitch)
219: st.minpitch = -30;
220: if (!st.maxpitch)
221: st.maxpitch = 30;
222: if (!st.maxyaw)
223: st.maxyaw = 360;
224:
225: self->pos1[PITCH] = -1 * st.minpitch;
226: self->pos1[YAW] = st.minyaw;
227: self->pos2[PITCH] = -1 * st.maxpitch;
228: self->pos2[YAW] = st.maxyaw;
229:
230: self->ideal_yaw = self->s.angles[YAW];
231: self->move_angles[YAW] = self->ideal_yaw;
232:
233: self->blocked = turret_blocked;
234:
235: self->think = turret_breach_finish_init;
236: self->nextthink = level.time + FRAMETIME;
237: gi.linkentity (self);
238: }
239:
240:
241: /*QUAKED turret_base (0 0 0) ?
242: This portion of the turret changes yaw only.
243: MUST be teamed with a turret_breach.
244: */
245:
246: void SP_turret_base (edict_t *self)
247: {
248: self->solid = SOLID_BSP;
249: self->movetype = MOVETYPE_PUSH;
250: gi.setmodel (self, self->model);
251: self->blocked = turret_blocked;
252: gi.linkentity (self);
253: }
254:
255:
256: /*QUAKED turret_driver (1 .5 0) (-16 -16 -24) (16 16 32)
257: Must NOT be on the team with the rest of the turret parts.
258: Instead it must target the turret_breach.
259: */
260:
261: void infantry_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage);
262: void infantry_stand (edict_t *self);
263: void monster_use (edict_t *self, edict_t *other, edict_t *activator);
264:
265: void turret_driver_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
266: {
267: edict_t *ent;
268:
269: // level the gun
270: self->target_ent->move_angles[0] = 0;
271:
272: // remove the driver from the end of them team chain
273: for (ent = self->target_ent->teammaster; ent->teamchain != self; ent = ent->teamchain)
274: ;
275: ent->teamchain = NULL;
276: self->teammaster = NULL;
277: self->flags &= ~FL_TEAMSLAVE;
278:
279: self->target_ent->owner = NULL;
280: self->target_ent->teammaster->owner = NULL;
281:
282: infantry_die (self, inflictor, attacker, damage);
283: }
284:
285: qboolean FindTarget (edict_t *self);
286:
287: void turret_driver_think (edict_t *self)
288: {
289: vec3_t target;
290: vec3_t dir;
291: float reaction_time;
292:
293: self->nextthink = level.time + FRAMETIME;
294:
295: if (self->enemy && (!self->enemy->inuse || self->enemy->health <= 0))
296: self->enemy = NULL;
297:
298: if (!self->enemy)
299: {
300: if (!FindTarget (self))
301: return;
302: self->monsterinfo.trail_time = level.time;
303: self->monsterinfo.aiflags &= ~AI_LOST_SIGHT;
304: }
305: else
306: {
307: if (visible (self, self->enemy))
308: {
309: if (self->monsterinfo.aiflags & AI_LOST_SIGHT)
310: {
311: self->monsterinfo.trail_time = level.time;
312: self->monsterinfo.aiflags &= ~AI_LOST_SIGHT;
313: }
314: }
315: else
316: {
317: self->monsterinfo.aiflags |= AI_LOST_SIGHT;
318: return;
319: }
320: }
321:
322: // let the turret know where we want it to aim
323: VectorCopy (self->enemy->s.origin, target);
324: target[2] += self->enemy->viewheight;
325: VectorSubtract (target, self->target_ent->s.origin, dir);
326: vectoangles (dir, self->target_ent->move_angles);
327:
328: // decide if we should shoot
329: if (level.time < self->monsterinfo.attack_finished)
330: return;
331:
332: reaction_time = (3 - skill->value) * 1.0;
333: if ((level.time - self->monsterinfo.trail_time) < reaction_time)
334: return;
335:
336: self->monsterinfo.attack_finished = level.time + reaction_time + 1.0;
337: //FIXME how do we really want to pass this along?
338: self->target_ent->spawnflags |= 65536;
339: }
340:
341: void turret_driver_link (edict_t *self)
342: {
343: vec3_t vec;
344: edict_t *ent;
345:
346: self->think = turret_driver_think;
347: self->nextthink = level.time + FRAMETIME;
348:
349: self->target_ent = G_PickTarget (self->target);
350: self->target_ent->owner = self;
351: self->target_ent->teammaster->owner = self;
352: VectorCopy (self->target_ent->s.angles, self->s.angles);
353:
354: vec[0] = self->target_ent->s.origin[0] - self->s.origin[0];
355: vec[1] = self->target_ent->s.origin[1] - self->s.origin[1];
356: vec[2] = 0;
357: self->move_origin[0] = VectorLength(vec);
358:
359: VectorSubtract (self->s.origin, self->target_ent->s.origin, vec);
360: vectoangles (vec, vec);
361: AnglesNormalize(vec);
362: self->move_origin[1] = vec[1];
363:
364: self->move_origin[2] = self->s.origin[2] - self->target_ent->s.origin[2];
365:
366: // add the driver to the end of them team chain
367: for (ent = self->target_ent->teammaster; ent->teamchain; ent = ent->teamchain)
368: ;
369: ent->teamchain = self;
370: self->teammaster = self->target_ent->teammaster;
371: self->flags |= FL_TEAMSLAVE;
372: }
373:
374: void SP_turret_driver (edict_t *self)
375: {
376: if (deathmatch->value)
377: {
378: G_FreeEdict (self);
379: return;
380: }
381:
382: self->movetype = MOVETYPE_PUSH;
383: self->solid = SOLID_BBOX;
384: self->s.modelindex = gi.modelindex("models/monsters/infantry/tris.md2");
385: VectorSet (self->mins, -16, -16, -24);
386: VectorSet (self->maxs, 16, 16, 32);
387:
388: self->health = 100;
389: self->gib_health = 0;
390: self->mass = 200;
391: self->viewheight = 24;
392:
393: self->die = turret_driver_die;
394: self->monsterinfo.stand = infantry_stand;
395:
396: self->flags |= FL_NO_KNOCKBACK;
397:
398: level.total_monsters++;
399:
400: self->svflags |= SVF_MONSTER;
401: self->s.renderfx |= RF_FRAMELERP;
402: self->takedamage = DAMAGE_AIM;
403: self->use = monster_use;
404: self->clipmask = MASK_MONSTERSOLID;
405: VectorCopy (self->s.origin, self->s.old_origin);
406: self->monsterinfo.aiflags |= AI_STAND_GROUND|AI_DUCKED;
407:
408: if (st.item)
409: {
410: self->item = FindItemByClassname (st.item);
411: if (!self->item)
412: gi.dprintf("%s at %s has bad item: %s\n", self->classname, vtos(self->s.origin), st.item);
413: }
414:
415: self->think = turret_driver_link;
416: self->nextthink = level.time + FRAMETIME;
417:
418: gi.linkentity (self);
419: }
420:
421: //============
422: // ROGUE
423:
424: // invisible turret drivers so we can have unmanned turrets.
425: // originally designed to shoot at func_trains and such, so they
426: // fire at the center of the bounding box, rather than the entity's
427: // origin.
428:
429: void turret_brain_think (edict_t *self)
430: {
431: vec3_t target;
432: vec3_t dir;
433: vec3_t endpos;
434: float reaction_time;
435: trace_t trace;
436:
437: self->nextthink = level.time + FRAMETIME;
438:
439: if (self->enemy)
440: {
441: if(!self->enemy->inuse)
442: self->enemy = NULL;
443: else if(self->enemy->takedamage && self->enemy->health <= 0)
444: self->enemy = NULL;
445: }
446:
447: if (!self->enemy)
448: {
449: if (!FindTarget (self))
450: return;
451: self->monsterinfo.trail_time = level.time;
452: self->monsterinfo.aiflags &= ~AI_LOST_SIGHT;
453: }
454: else
455: {
456: VectorAdd (self->enemy->absmax, self->enemy->absmin, endpos);
457: VectorScale (endpos, 0.5, endpos);
458:
459: trace = gi.trace (self->target_ent->s.origin, vec3_origin, vec3_origin, endpos, self->target_ent, MASK_SHOT);
460: if(trace.fraction == 1 || trace.ent == self->enemy)
461: {
462: if (self->monsterinfo.aiflags & AI_LOST_SIGHT)
463: {
464: self->monsterinfo.trail_time = level.time;
465: self->monsterinfo.aiflags &= ~AI_LOST_SIGHT;
466: }
467: }
468: else
469: {
470: self->monsterinfo.aiflags |= AI_LOST_SIGHT;
471: return;
472: }
473: }
474:
475: // let the turret know where we want it to aim
476: VectorCopy (endpos, target);
477: VectorSubtract (target, self->target_ent->s.origin, dir);
478: vectoangles (dir, self->target_ent->move_angles);
479:
480: // decide if we should shoot
481: if (level.time < self->monsterinfo.attack_finished)
482: return;
483:
484: if(self->delay)
485: reaction_time = self->delay;
486: else
487: reaction_time = (3 - skill->value) * 1.0;
488: if ((level.time - self->monsterinfo.trail_time) < reaction_time)
489: return;
490:
491: self->monsterinfo.attack_finished = level.time + reaction_time + 1.0;
492: //FIXME how do we really want to pass this along?
493: self->target_ent->spawnflags |= 65536;
494: }
495:
496: // =================
497: // =================
498: void turret_brain_link (edict_t *self)
499: {
500: vec3_t vec;
501: edict_t *ent;
502:
503: if (self->killtarget)
504: {
505: self->enemy = G_PickTarget (self->killtarget);
506: }
507:
508: self->think = turret_brain_think;
509: self->nextthink = level.time + FRAMETIME;
510:
511: self->target_ent = G_PickTarget (self->target);
512: self->target_ent->owner = self;
513: self->target_ent->teammaster->owner = self;
514: VectorCopy (self->target_ent->s.angles, self->s.angles);
515:
516: vec[0] = self->target_ent->s.origin[0] - self->s.origin[0];
517: vec[1] = self->target_ent->s.origin[1] - self->s.origin[1];
518: vec[2] = 0;
519: self->move_origin[0] = VectorLength(vec);
520:
521: VectorSubtract (self->s.origin, self->target_ent->s.origin, vec);
522: vectoangles (vec, vec);
523: AnglesNormalize(vec);
524: self->move_origin[1] = vec[1];
525:
526: self->move_origin[2] = self->s.origin[2] - self->target_ent->s.origin[2];
527:
528: // add the driver to the end of them team chain
529: for (ent = self->target_ent->teammaster; ent->teamchain; ent = ent->teamchain)
530: ;
531: ent->teamchain = self;
532: self->teammaster = self->target_ent->teammaster;
533: self->flags |= FL_TEAMSLAVE;
534: }
535:
536: // =================
537: // =================
538: void turret_brain_deactivate (edict_t *self, edict_t *other, edict_t *activator)
539: {
540: self->think = NULL;
541: self->nextthink = 0;
542: }
543:
544: // =================
545: // =================
546: void turret_brain_activate (edict_t *self, edict_t *other, edict_t *activator)
547: {
548: if (!self->enemy)
549: {
550: self->enemy = activator;
551: }
552:
553: // wait at least 3 seconds to fire.
554: self->monsterinfo.attack_finished = level.time + 3;
555: self->use = turret_brain_deactivate;
556:
557: self->think = turret_brain_link;
558: self->nextthink = level.time + FRAMETIME;
559: }
560:
561: /*QUAKED turret_invisible_brain (1 .5 0) (-16 -16 -16) (16 16 16)
562: Invisible brain to drive the turret.
563:
564: Does not search for targets. If targeted, can only be turned on once
565: and then off once. After that they are completely disabled.
566:
567: "delay" the delay between firing (default ramps for skill level)
568: "Target" the turret breach
569: "Killtarget" the item you want it to attack.
570: Target the brain if you want it activated later, instead of immediately. It will wait 3 seconds
571: before firing to acquire the target.
572: */
573: void SP_turret_invisible_brain (edict_t *self)
574: {
575: if (!self->killtarget)
576: {
577: gi.dprintf("turret_invisible_brain with no killtarget!\n");
578: G_FreeEdict (self);
579: return;
580: }
581: if (!self->target)
582: {
583: gi.dprintf("turret_invisible_brain with no target!\n");
584: G_FreeEdict (self);
585: return;
586: }
587:
588: if (self->targetname)
589: {
590: self->use = turret_brain_activate;
591: }
592: else
593: {
594: self->think = turret_brain_link;
595: self->nextthink = level.time + FRAMETIME;
596: }
597:
598: self->movetype = MOVETYPE_PUSH;
599: gi.linkentity (self);
600: }
601:
602: // ROGUE
603: //============
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.