|
|
1.1 root 1: // g_turret.c
2:
3: #include "g_local.h"
4:
5:
6: void AnglesNormalize(vec3_t vec)
7: {
8: while(vec[0] > 360)
9: vec[0] -= 360;
10: while(vec[0] < 0)
11: vec[0] += 360;
12: while(vec[1] > 360)
13: vec[1] -= 360;
14: while(vec[1] < 0)
15: vec[1] += 360;
16: }
17:
18: float SnapToEights(float x)
19: {
20: x *= 8.0;
21: if (x > 0.0)
22: x += 0.5;
23: else
24: x -= 0.5;
25: return 0.125 * (int)x;
26: }
27:
28:
29: void turret_blocked(edict_t *self, edict_t *other)
30: {
31: edict_t *attacker;
32:
33: if (other->takedamage)
34: {
35: if (self->teammaster->owner)
36: attacker = self->teammaster->owner;
37: else
38: attacker = self->teammaster;
39: T_Damage (other, self, attacker, vec3_origin, other->s.origin, vec3_origin, self->teammaster->dmg, 10, 0, MOD_CRUSH);
40: }
41: }
42:
43: /*QUAKED turret_breach (0 0 0) ?
44: This portion of the turret can change both pitch and yaw.
45: The model should be made with a flat pitch.
46: It (and the associated base) need to be oriented towards 0.
47: Use "angle" to set the starting angle.
48:
49: "speed" default 50
50: "dmg" default 10
51: "angle" point this forward
52: "target" point this at an info_notnull at the muzzle tip
53: "minpitch" min acceptable pitch angle : default -30
54: "maxpitch" max acceptable pitch angle : default 30
55: "minyaw" min acceptable yaw angle : default 0
56: "maxyaw" max acceptable yaw angle : default 360
57: */
58:
59: void turret_breach_fire (edict_t *self)
60: {
61: vec3_t f, r, u;
62: vec3_t start;
63: int damage;
64: int speed;
65:
66: AngleVectors (self->s.angles, f, r, u);
67: VectorMA (self->s.origin, self->move_origin[0], f, start);
68: VectorMA (start, self->move_origin[1], r, start);
69: VectorMA (start, self->move_origin[2], u, start);
70:
71: damage = 100 + random() * 50;
72: speed = 550 + 50 * skill->value;
73: fire_rocket (self->teammaster->owner, start, f, damage, speed, 150, damage);
74: gi.positioned_sound (start, self, CHAN_WEAPON, gi.soundindex("weapons/rocklf1a.wav"), 1, ATTN_NORM, 0);
75: }
76:
77: void turret_breach_think (edict_t *self)
78: {
79: edict_t *ent;
80: vec3_t current_angles;
81: vec3_t delta;
82:
83: VectorCopy (self->s.angles, current_angles);
84: AnglesNormalize(current_angles);
85:
86: AnglesNormalize(self->move_angles);
87: if (self->move_angles[PITCH] > 180)
88: self->move_angles[PITCH] -= 360;
89:
90: // clamp angles to mins & maxs
91: if (self->move_angles[PITCH] > self->pos1[PITCH])
92: self->move_angles[PITCH] = self->pos1[PITCH];
93: else if (self->move_angles[PITCH] < self->pos2[PITCH])
94: self->move_angles[PITCH] = self->pos2[PITCH];
95:
96: if ((self->move_angles[YAW] < self->pos1[YAW]) || (self->move_angles[YAW] > self->pos2[YAW]))
97: {
98: float dmin, dmax;
99:
100: dmin = fabs(self->pos1[YAW] - self->move_angles[YAW]);
101: if (dmin < -180)
102: dmin += 360;
103: else if (dmin > 180)
104: dmin -= 360;
105: dmax = fabs(self->pos2[YAW] - self->move_angles[YAW]);
106: if (dmax < -180)
107: dmax += 360;
108: else if (dmax > 180)
109: dmax -= 360;
110: if (fabs(dmin) < fabs(dmax))
111: self->move_angles[YAW] = self->pos1[YAW];
112: else
113: self->move_angles[YAW] = self->pos2[YAW];
114: }
115:
116: VectorSubtract (self->move_angles, current_angles, delta);
117: if (delta[0] < -180)
118: delta[0] += 360;
119: else if (delta[0] > 180)
120: delta[0] -= 360;
121: if (delta[1] < -180)
122: delta[1] += 360;
123: else if (delta[1] > 180)
124: delta[1] -= 360;
125: delta[2] = 0;
126:
127: if (delta[0] > self->speed * FRAMETIME)
128: delta[0] = self->speed * FRAMETIME;
129: if (delta[0] < -1 * self->speed * FRAMETIME)
130: delta[0] = -1 * self->speed * FRAMETIME;
131: if (delta[1] > self->speed * FRAMETIME)
132: delta[1] = self->speed * FRAMETIME;
133: if (delta[1] < -1 * self->speed * FRAMETIME)
134: delta[1] = -1 * self->speed * FRAMETIME;
135:
136: VectorScale (delta, 1.0/FRAMETIME, self->avelocity);
137:
138: self->nextthink = level.time + FRAMETIME;
139:
140: for (ent = self->teammaster; ent; ent = ent->teamchain)
141: ent->avelocity[1] = self->avelocity[1];
142:
143: // if we have adriver, adjust his velocities
144: if (self->owner)
145: {
146: float angle;
147: float target_z;
148: float diff;
149: vec3_t target;
150: vec3_t dir;
151:
152: // angular is easy, just copy ours
153: self->owner->avelocity[0] = self->avelocity[0];
154: self->owner->avelocity[1] = self->avelocity[1];
155:
156: // x & y
157: angle = self->s.angles[1] + self->owner->move_origin[1];
158: angle *= (M_PI*2 / 360);
159: target[0] = SnapToEights(self->s.origin[0] + cos(angle) * self->owner->move_origin[0]);
160: target[1] = SnapToEights(self->s.origin[1] + sin(angle) * self->owner->move_origin[0]);
161: target[2] = self->owner->s.origin[2];
162:
163: VectorSubtract (target, self->owner->s.origin, dir);
164: self->owner->velocity[0] = dir[0] * 1.0 / FRAMETIME;
165: self->owner->velocity[1] = dir[1] * 1.0 / FRAMETIME;
166:
167: // z
168: angle = self->s.angles[PITCH] * (M_PI*2 / 360);
169: target_z = SnapToEights(self->s.origin[2] + self->owner->move_origin[0] * tan(angle) + self->owner->move_origin[2]);
170:
171: diff = target_z - self->owner->s.origin[2];
172: self->owner->velocity[2] = diff * 1.0 / FRAMETIME;
173:
174: if (self->spawnflags & 65536)
175: {
176: turret_breach_fire (self);
177: self->spawnflags &= ~65536;
178: }
179: }
180: }
181:
182: void turret_breach_finish_init (edict_t *self)
183: {
184: // get and save info for muzzle location
185: if (!self->target)
186: {
187: gi.dprintf("%s at %s needs a target\n", self->classname, vtos(self->s.origin));
188: }
189: else
190: {
191: self->target_ent = G_PickTarget (self->target);
192: VectorSubtract (self->target_ent->s.origin, self->s.origin, self->move_origin);
193: G_FreeEdict(self->target_ent);
194: }
195:
196: self->teammaster->dmg = self->dmg;
197: self->think = turret_breach_think;
198: self->think (self);
199: }
200:
201: void SP_turret_breach (edict_t *self)
202: {
203: self->solid = SOLID_BSP;
204: self->movetype = MOVETYPE_PUSH;
205: gi.setmodel (self, self->model);
206:
207: if (!self->speed)
208: self->speed = 50;
209: if (!self->dmg)
210: self->dmg = 10;
211:
212: if (!st.minpitch)
213: st.minpitch = -30;
214: if (!st.maxpitch)
215: st.maxpitch = 30;
216: if (!st.maxyaw)
217: st.maxyaw = 360;
218:
219: self->pos1[PITCH] = -1 * st.minpitch;
220: self->pos1[YAW] = st.minyaw;
221: self->pos2[PITCH] = -1 * st.maxpitch;
222: self->pos2[YAW] = st.maxyaw;
223:
224: self->ideal_yaw = self->s.angles[YAW];
225: self->move_angles[YAW] = self->ideal_yaw;
226:
227: self->blocked = turret_blocked;
228:
229: self->think = turret_breach_finish_init;
230: self->nextthink = level.time + FRAMETIME;
231: gi.linkentity (self);
232: }
233:
234:
235: /*QUAKED turret_base (0 0 0) ?
236: This portion of the turret changes yaw only.
237: MUST be teamed with a turret_breach.
238: */
239:
240: void SP_turret_base (edict_t *self)
241: {
242: self->solid = SOLID_BSP;
243: self->movetype = MOVETYPE_PUSH;
244: gi.setmodel (self, self->model);
245: self->blocked = turret_blocked;
246: gi.linkentity (self);
247: }
248:
249:
250: /*QUAKED turret_driver (1 .5 0) (-16 -16 -24) (16 16 32)
251: Must NOT be on the team with the rest of the turret parts.
252: Instead it must target the turret_breach.
253: */
254:
255: void infantry_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage);
256: void infantry_stand (edict_t *self);
257: void monster_use (edict_t *self, edict_t *other, edict_t *activator);
258:
259: void turret_driver_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
260: {
261: edict_t *ent;
262:
263: // level the gun
264: self->target_ent->move_angles[0] = 0;
265:
266: // remove the driver from the end of them team chain
267: for (ent = self->target_ent->teammaster; ent->teamchain != self; ent = ent->teamchain)
268: ;
269: ent->teamchain = NULL;
270: self->teammaster = NULL;
271: self->flags &= ~FL_TEAMSLAVE;
272:
273: self->target_ent->owner = NULL;
274: self->target_ent->teammaster->owner = NULL;
275:
276: infantry_die (self, inflictor, attacker, damage);
277: }
278:
279: qboolean FindTarget (edict_t *self);
280:
281: void turret_driver_think (edict_t *self)
282: {
283: vec3_t target;
284: vec3_t dir;
285: float reaction_time;
286:
287: self->nextthink = level.time + FRAMETIME;
288:
289: if (self->enemy && (!self->enemy->inuse || self->enemy->health <= 0))
290: self->enemy = NULL;
291:
292: if (!self->enemy)
293: {
294: if (!FindTarget (self))
295: return;
296: self->monsterinfo.trail_time = level.time;
297: self->monsterinfo.aiflags &= ~AI_LOST_SIGHT;
298: }
299: else
300: {
301: if (visible (self, self->enemy))
302: {
303: if (self->monsterinfo.aiflags & AI_LOST_SIGHT)
304: {
305: self->monsterinfo.trail_time = level.time;
306: self->monsterinfo.aiflags &= ~AI_LOST_SIGHT;
307: }
308: }
309: else
310: {
311: self->monsterinfo.aiflags |= AI_LOST_SIGHT;
312: return;
313: }
314: }
315:
316: // let the turret know where we want it to aim
317: VectorCopy (self->enemy->s.origin, target);
318: target[2] += self->enemy->viewheight;
319: VectorSubtract (target, self->target_ent->s.origin, dir);
320: vectoangles (dir, self->target_ent->move_angles);
321:
322: // decide if we should shoot
323: if (level.time < self->monsterinfo.attack_finished)
324: return;
325:
326: reaction_time = (3 - skill->value) * 1.0;
327: if ((level.time - self->monsterinfo.trail_time) < reaction_time)
328: return;
329:
330: self->monsterinfo.attack_finished = level.time + reaction_time + 1.0;
331: //FIXME how do we really want to pass this along?
332: self->target_ent->spawnflags |= 65536;
333: }
334:
335: void turret_driver_link (edict_t *self)
336: {
337: vec3_t vec;
338: edict_t *ent;
339:
340: self->think = turret_driver_think;
341: self->nextthink = level.time + FRAMETIME;
342:
343: self->target_ent = G_PickTarget (self->target);
344: self->target_ent->owner = self;
345: self->target_ent->teammaster->owner = self;
346: VectorCopy (self->target_ent->s.angles, self->s.angles);
347:
348: vec[0] = self->target_ent->s.origin[0] - self->s.origin[0];
349: vec[1] = self->target_ent->s.origin[1] - self->s.origin[1];
350: vec[2] = 0;
351: self->move_origin[0] = VectorLength(vec);
352:
353: VectorSubtract (self->s.origin, self->target_ent->s.origin, vec);
354: vectoangles (vec, vec);
355: AnglesNormalize(vec);
356: self->move_origin[1] = vec[1];
357:
358: self->move_origin[2] = self->s.origin[2] - self->target_ent->s.origin[2];
359:
360: // add the driver to the end of them team chain
361: for (ent = self->target_ent->teammaster; ent->teamchain; ent = ent->teamchain)
362: ;
363: ent->teamchain = self;
364: self->teammaster = self->target_ent->teammaster;
365: self->flags |= FL_TEAMSLAVE;
366: }
367:
368: void SP_turret_driver (edict_t *self)
369: {
370: if (deathmatch->value)
371: {
372: G_FreeEdict (self);
373: return;
374: }
375:
376: self->movetype = MOVETYPE_PUSH;
377: self->solid = SOLID_BBOX;
378: self->s.modelindex = gi.modelindex("models/monsters/infantry/tris.md2");
379: VectorSet (self->mins, -16, -16, -24);
380: VectorSet (self->maxs, 16, 16, 32);
381:
382: self->health = 100;
383: self->gib_health = 0;
384: self->mass = 200;
385: self->viewheight = 24;
386:
387: self->die = turret_driver_die;
388: self->monsterinfo.stand = infantry_stand;
389:
390: self->flags |= FL_NO_KNOCKBACK;
391:
392: level.total_monsters++;
393:
394: self->svflags |= SVF_MONSTER;
395: self->s.renderfx |= RF_FRAMELERP;
396: self->takedamage = DAMAGE_AIM;
397: self->use = monster_use;
398: self->clipmask = MASK_MONSTERSOLID;
399: VectorCopy (self->s.origin, self->s.old_origin);
400: self->monsterinfo.aiflags |= AI_STAND_GROUND|AI_DUCKED;
401:
402: if (st.item)
403: {
404: self->item = FindItemByClassname (st.item);
405: if (!self->item)
406: gi.dprintf("%s at %s has bad item: %s\n", self->classname, vtos(self->s.origin), st.item);
407: }
408:
409: self->think = turret_driver_link;
410: self->nextthink = level.time + FRAMETIME;
411:
412: gi.linkentity (self);
413: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.