Quake-2

Quake 2 GPL Source Release
Log | Files | Refs

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 }