g_turret.c (11278B)
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 25 void AnglesNormalize(vec3_t vec) 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 78 void turret_breach_fire (edict_t *self) 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 96 void turret_breach_think (edict_t *self) 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 201 void turret_breach_finish_init (edict_t *self) 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 220 void SP_turret_breach (edict_t *self) 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 259 void SP_turret_base (edict_t *self) 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 300 void turret_driver_think (edict_t *self) 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 354 void turret_driver_link (edict_t *self) 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 387 void SP_turret_driver (edict_t *self) 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 }