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