home *** CD-ROM | disk | FTP | other *** search
/ Gambler 19 / GAMBLERCD19.BIN / UTILS / 3D / BRONIE / DUAL_LAU.ZIP / src / g_turret.c < prev    next >
C/C++ Source or Header  |  1997-11-23  |  11KB  |  414 lines

  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);
  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. }
  414.