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