home *** CD-ROM | disk | FTP | other *** search
/ Qu-ake / Qu-ake.iso / qu_ke / monster / 005 / BLAZE.QC < prev    next >
Encoding:
Text File  |  1996-11-26  |  15.2 KB  |  624 lines

  1. //=============================================================================
  2. // Blaze Gun version 1.1
  3. //
  4. // 11/27/96 by Jonathan E. Wright <nelno@interpath.com>
  5. //
  6. // all original code, not based on anything except the original Quake C
  7. // code from id software.
  8. //=============================================================================
  9.  
  10. // How it works:
  11. // The flame is simply your basic flame model turned 90 degress on it's side
  12. // when it is shot.  When it impacts something that has health > 0 and DAMAGE_AIM
  13. // it sets it's location to that entity's origin, and it's next think becomes
  14. // blaze_stick.  blaze_stick is responsible for following the burning entity,
  15. // checking for water, determining if there is another entity nearby to burn
  16. // and burning the entity every so often.  The time counter for each burn is kept
  17. // in the flame's attack_finished field, so the entity does not have to be burnt
  18. // each time the flame follows it.  When the entity dies, or the flame is submersed
  19. // in water the flame is removed and no more damage is done from that flame.
  20.  
  21. // if you have solid bodies that are respawnable (ie. in the BodyQue) you
  22. // need to call blaze_switchentity when the bodies are copied or else a
  23. // respawned entity can come back with flames on it.
  24.  
  25. /*
  26. ================
  27. macros
  28. ================
  29. */
  30. $frame nailatt1 nailatt2
  31.  
  32. /*
  33. ================
  34. function prototypes
  35. ================
  36. */
  37.  
  38. void () player_run;
  39. void () BecomeExplosion;
  40. void () SuperDamageSound;
  41.  
  42. /*
  43. ================
  44. global vars
  45. ================
  46. */
  47.  
  48. // keeps track of the number of flames so that a maximum of flames
  49. // will not be exceeded, causing Quake to bog down
  50.  
  51. float   current_flames;
  52.  
  53. /*
  54. ============
  55. functions
  56. ============
  57. */
  58.  
  59. void () blaze_precache =
  60. {
  61.   precache_model2 ("progs/flame2.mdl");
  62.   precache_sound2 ("blaze/flhit1.wav");
  63.   precache_sound2 ("blaze/flshot1.wav");
  64.   precache_sound2 ("blaze/flhiss1.wav");
  65. };
  66.  
  67. /*
  68. ===============
  69. blaze_extinguishentity
  70.  
  71. called when a respawnable entity is removed without being killed (ie.
  72. when Cujo is deactivated).  Prevents the respawned entity from burning.
  73. ===============
  74. */
  75.  
  76. void (entity old_ent) blaze_extinguishentity =
  77. {
  78.   local   entity  head, temp;
  79.  
  80.   head = nextent (world);
  81.  
  82.   while (head)
  83.   {
  84.     head = find (head, classname, "fire");
  85.  
  86.     if ((head != world) && (head.enemy == old_ent))
  87.     {
  88.       temp = nextent (head);
  89.       remove (head);
  90.       head = temp;
  91.     }
  92.     else if (head != world) head = nextent (head);
  93.   }
  94. };
  95.  
  96.  
  97. /*
  98. ===============
  99. blaze_switchentity
  100.  
  101. called when an entities corpse is copied to the body que, prevents the
  102. respawning entity from being still engulfed in flames while the body
  103. can still burn
  104. ===============
  105. */
  106.  
  107. void (entity old_ent, entity new_ent) blaze_switchentity =
  108. {
  109.   local   entity  head;
  110.  
  111.   head = nextent (world);
  112.  
  113.   while (head)
  114.   {
  115.     head = find (head, classname, "fire");
  116.  
  117.     if ((head != world) && (head.enemy == old_ent))
  118.     {
  119.       head.enemy = new_ent;
  120.       setorigin (head, new_ent.origin);
  121.     }
  122.     else if (head != world) head = nextent (head);
  123.   }
  124. };
  125.  
  126. /*
  127. ===============
  128. blaze_spread
  129.  
  130. called by blaze_stick.
  131.  
  132. creates a new flame entity which will stick to and burn entity "fuel"
  133. ===============
  134. */
  135.  
  136. void () blaze_stick;
  137.  
  138. void (entity fuel) blaze_spread =
  139. {
  140.   local   entity   new_flame;
  141.  
  142.   // don't catch the same entity on fire again!
  143.   // don't catch anything that can't take damage
  144.   if ((fuel.takedamage != DAMAGE_AIM) || (current_flames >= 30))
  145.   {
  146.      return;
  147.   }
  148.   else if ((fuel.classname == "player") && (random () > 0.75))
  149.   {
  150.     return;
  151.   }
  152.   else if (random () > 0.5)
  153.   {
  154.     return;
  155.   }
  156.  
  157.   current_flames = current_flames + 1;
  158.  
  159.   if (fuel.classname == "player")
  160.   {
  161.     bprint (fuel.netname);
  162.     bprint (" is on fire!!\n");
  163.   }
  164.   else if (fuel.classname == "cujo")
  165.     sprint (fuel.owner, "Your dog is aflame!!\n");
  166.  
  167.   new_flame = spawn ();
  168.  
  169.   // set the sucker on fire!
  170.   new_flame.enemy = fuel;
  171.   new_flame.solid = SOLID_NOT;
  172.   new_flame.angles_x = 0;
  173.  
  174.   new_flame.movetype = MOVETYPE_NONE;
  175.   new_flame.effects = EF_DIMLIGHT;
  176.   new_flame.takedamage = DAMAGE_NO;
  177.   new_flame.classname = "fire";
  178.   new_flame.targetname = "";
  179.  
  180.   new_flame.think = blaze_stick;
  181.   new_flame.nextthink = time;
  182.  
  183.   // always points back to the player who fired the original flame
  184.   // so monsters get mad at player, not at flames!
  185.   new_flame.owner = self.owner;
  186.  
  187.   setmodel (new_flame, "progs/flame2.mdl");
  188.   setsize (new_flame, '2 2 2', '2 2 2');
  189.   setorigin (new_flame, fuel.origin);
  190. };
  191.  
  192. /*
  193. ===============
  194. blaze_findfuel
  195.  
  196. called by blaze_stick
  197.  
  198. searches for the closest entity within a certain radius.  If a qualifying entity is
  199. found, and a random chance met, the entity is caught on fire by calling blaze_spread.
  200.  
  201. Notice there is a greater chance for players to be caught on fire, since they tend to
  202. move around quite a bit more than monsters, and are less likely to remain near a flame
  203. ===============
  204. */
  205.  
  206. void (float dist) blaze_findfuel =
  207. {
  208.   local   entity   head;
  209.   local   entity   fuel;
  210.   local   float    lastd;
  211.  
  212.   // anything dist units away can catch flame
  213.   head = findradius (self.origin, dist);
  214.   fuel = world;
  215.  
  216.   while (head)
  217.   {
  218.     if ((head != self.enemy) && (head.takedamage == DAMAGE_AIM))
  219.     {
  220.       // find the distance to the current entity, so we can choose the closest
  221.       // entity within the radius
  222.       lastd = vlen (self.origin - head.origin);
  223.       if (lastd < dist)
  224.       {
  225.         fuel = head;
  226.         dist = lastd;
  227.       }
  228.     }
  229.     head = head.chain;
  230.   }
  231.  
  232.   if ((fuel != world) && (random () < 0.1))
  233.   {
  234.     blaze_spread (fuel);
  235.   }
  236. };
  237.  
  238. /*
  239. ===============
  240. blaze_stick
  241.  
  242. called every frame as the flame's think function.  responsible for control
  243. the flame once it has attached to an entity -- not in flight from the blaze gun!
  244.  
  245. checks for water submersion each frame
  246. ===============
  247. */
  248.  
  249. void () burn_entity;
  250.  
  251. void () blaze_stick =
  252. {
  253.   local   vector   org;
  254.   local   vector   fuelsize, xv, yv, zv;
  255.   local   float    pc;
  256.   local   float    average_width;
  257.  
  258.   pc = pointcontents (self.origin);
  259.   if ((pc == CONTENT_WATER) || (pc == CONTENT_SLIME))
  260.   {
  261.     // water extinguished the flame
  262.     sound (self, CHAN_WEAPON, "blaze/flhiss1.wav", 1, ATTN_NORM);
  263.     // reset inflictor so enities don't keep going for water
  264.     self.enemy.dmg_inflictor = self.enemy;
  265.     current_flames = current_flames - 1;
  266.     remove (self);
  267.     return;
  268.   }
  269.  
  270.   // a dead player -- put out the flames before they respawn!
  271.   // changed to flame search in CopyBodyToQue
  272. /*
  273.   if (self.enemy.deadflag == DEAD_RESPAWNABLE))
  274.   {
  275.     current_flames = current_flames - 1;
  276.     remove (self);
  277.     return;
  278.   }
  279. */
  280.  
  281.   fuelsize = self.enemy.size;
  282.  
  283.   // it's really the average of x_width and y_width * 2;
  284.   average_width = (fuelsize_x + fuelsize_y) * 0.5;
  285.   blaze_findfuel (average_width);
  286.  
  287.   // make the flames dance, baby! dance!
  288.  
  289.   if (self.enemy.classname == "monster_shambler")
  290.     pc = 0.4;
  291.   else
  292.     pc = 0.1;
  293.  
  294.   fuelsize_x = fuelsize_x * pc * random ();
  295.   if (random () < 0.5)
  296.     fuelsize_x = fuelsize_x * -1;
  297.   fuelsize_y = fuelsize_y * pc * random ();
  298.   if (random () < 0.5)
  299.     fuelsize_y = fuelsize_y * -1;
  300.   fuelsize_z = fuelsize_z * pc * random ();
  301.   if ((random () < 0.5) && (self.enemy.classname != "monster_shambler"))
  302.     fuelsize_z = fuelsize_z * -1;
  303.  
  304.   org = self.enemy.origin + fuelsize;
  305.   setorigin (self, org);
  306.  
  307.   if (self.velocity == '0 0 0') self.avelocity = '0 0 0';
  308.  
  309.   self.think = blaze_stick;
  310.   self.nextthink = time;
  311.  
  312.   // targetname is used as a flag here to determine if the entity
  313.   // has already been determined to be dead and the attack_finished time
  314.   // already increased -- without the flag, the flames won't burn out
  315.   // because the attack_finished time will always be increasing
  316.  
  317.   if ((self.enemy.health <= 0) && (self.targetname == "") &&
  318.        (self.enemy.classname != "monster_zombie"))
  319. //       (self.enemy.classname != "player"))
  320.   {
  321.     self.attack_finished = 7 * random () + time;
  322.     self.targetname = "already_dead";
  323. //    bprint (self.enemy.classname);
  324. //    bprint (" is dead and burning\n");
  325.   }
  326.  
  327.   if (self.attack_finished <= time) burn_entity ();
  328. };
  329.  
  330. /*
  331. ===============
  332. burn_entity -- called by the flame
  333.  
  334. called each time attack_finished <= time, currently every second
  335. responsible for damaging the entity the flame is attached to
  336.  
  337. zombies are a special case and recieve quite a bit more damage than
  338. a normal monster or player so that a few flames and sometimes a single
  339. flame, can gib them
  340. ===============
  341. */
  342.  
  343. void () burn_entity =
  344. {
  345.   if ((self.enemy.health <= 0) || (self.enemy.deadflag == DEAD_RESPAWNABLE))
  346.   {
  347.     current_flames = current_flames - 1;
  348.     remove (self);
  349.     return;
  350.   }
  351.  
  352.   if (self.enemy.classname != "monster_zombie")
  353.     T_Damage (self.enemy, self, self.owner, 6 * random () + 1);
  354.   else
  355.     T_Damage (self.enemy, self, self.owner, 50 * random () + 10);
  356.  
  357.   if (self.enemy.classname == "player")
  358.     centerprint (self.enemy, "You're on fire!\nFind some water!");
  359.  
  360. //  self.think = blaze_stick;
  361. //  self.nextthink = time;
  362.   self.attack_finished = time + 1;
  363. };
  364.  
  365. /*
  366. ===============
  367. blaze_watercheck
  368.  
  369. called as the flame's thin function from the time it is fired until it hits
  370. and is removed or catches and entity on fire
  371. ===============
  372. */
  373.  
  374. void() blaze_watercheck =
  375. {
  376.   local float  pc;
  377.  
  378.   pc = pointcontents (self.origin);
  379.   if ((pc == CONTENT_WATER) || (pc == CONTENT_SLIME) || (self.attack_finished <= time))
  380.   {
  381.     sound (self, CHAN_WEAPON, "blaze/flhiss1.wav", 1, ATTN_NORM);
  382.     // reset inflictor so enities don't keep going for water
  383.     remove (self);
  384.  
  385.     // make the little grey particles
  386.     WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
  387.     WriteByte (MSG_BROADCAST, TE_GUNSHOT);
  388.     WriteCoord (MSG_BROADCAST, self.origin_x);
  389.     WriteCoord (MSG_BROADCAST, self.origin_y);
  390.     WriteCoord (MSG_BROADCAST, self.origin_z);
  391.  
  392.     return;
  393.   }
  394.  
  395.   self.think = blaze_watercheck;
  396.   self.nextthink = time;
  397. };
  398.  
  399. /*
  400. ===============
  401. blaze_touch
  402.  
  403. the touch function of an in-flight flame.  responsible for setting an
  404. entity aflame when it hits it (if the random chance is met)
  405. ===============
  406. */
  407.  
  408. void() blaze_touch =
  409. {
  410.   local vector org;
  411.  local float  pc;
  412.  
  413.   pc = pointcontents (self.origin);
  414.   if (pc == CONTENT_SKY)
  415.   {
  416.     remove (self);
  417.     return;
  418.   }
  419.   else if ((pc == CONTENT_WATER) || (pc == CONTENT_SLIME))
  420.   {
  421.     remove (self);
  422.     return;
  423.   }
  424.  
  425.   sound (self, CHAN_WEAPON, "blaze/flhit1.wav", 1, ATTN_NORM);
  426.  
  427.   org = self.origin - 8*normalize(self.velocity);
  428.  
  429.   if ((other.health) && (other.takedamage == DAMAGE_AIM))
  430.     T_Damage (other, self, self.owner, 15);
  431.   else
  432.   {
  433.     // makes a little debris appear for one frame
  434.     WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
  435.     WriteByte (MSG_BROADCAST, TE_GUNSHOT);
  436.     WriteCoord (MSG_BROADCAST, org_x);
  437.     WriteCoord (MSG_BROADCAST, org_y);
  438.     WriteCoord (MSG_BROADCAST, org_z);
  439.   }
  440.  
  441.   // does some radius damage, < 1/3 that of a rocket
  442.   T_RadiusDamage (self, self.owner, 35, other);
  443.  
  444. // debugging purposes
  445. /*
  446.   bprint ("health=");
  447.   bprint (ftos (other.health));
  448.   if (other.takedamage == DAMAGE_AIM)
  449.     bprint (", DAMAGE_AIM\n");
  450.   else
  451.     bprint (", no damage\n");
  452. */
  453.   if ((other.health) && (other.takedamage == DAMAGE_AIM))
  454.   {
  455.     if ((random () < 0.6) && (current_flames < 30))
  456.     {
  457.       // set the sucker on fire!
  458.       self.enemy = other;
  459. // used for debugging deathmatch respawns
  460. //      self.enemy = self.owner;
  461.       self.solid = SOLID_NOT;
  462.       self.movetype = MOVETYPE_NONE;
  463.       self.origin = self.enemy.origin;
  464.       self.think = blaze_stick;
  465.       self.nextthink = time;
  466.       self.attack_finished = time + 1;
  467.       self.classname = "fire";
  468.       self.targetname = "";
  469.       // point flame upright
  470.       self.angles_x = 0;
  471.       current_flames = current_flames + 1;
  472.  
  473.       return;
  474.     }
  475.   }
  476.  
  477.   BecomeExplosion ();
  478. };
  479.  
  480.  
  481. /*
  482. ===============
  483. blaze_launch
  484.  
  485. the main firing routine, determines where the flame shoots from and where it will
  486. go to, determines it's velocity, etc.
  487. ===============
  488. */
  489.  
  490. void(float ox) blaze_launch =
  491. {
  492.   local   entity   old;
  493.   local   vector   start, finish, dir;
  494.   local   float    pc;
  495.  
  496.   start = self.origin + '0 0 16' + v_right*ox;
  497.  
  498.   makevectors (self.v_angle);
  499.  
  500.   if (self.ammo_nails < 1)
  501.   {
  502.     self.weapon = W_BestWeapon ();
  503.     W_SetCurrentAmmo ();
  504.     return;
  505.   }
  506.  
  507.   self.currentammo = self.ammo_nails = self.ammo_nails - 1;
  508.   makevectors (self.v_angle);        // is this still used
  509.  
  510.   finish = start + v_forward*600;
  511.   dir = finish - start;
  512.  
  513. // launch the flame
  514.   sound (self, CHAN_WEAPON, "blaze/flshot1.wav", 1, ATTN_NORM);
  515.  
  516.   dir = normalize(dir);
  517.  
  518.   newmis = spawn();
  519.   newmis.owner = self;
  520.   newmis.movetype = MOVETYPE_TOSS;
  521.   newmis.solid = SOLID_BBOX;
  522.   newmis.effects = EF_DIMLIGHT;
  523.   newmis.takedamage = DAMAGE_NO;
  524.   newmis.classname = "blaze_shot";
  525.  
  526.   setmodel (newmis, "progs/flame2.mdl");
  527.   setsize (newmis, '2 2 2', '2 2 2');
  528.  
  529.   setorigin (newmis, start);
  530.  
  531.   // add player's velocity to the flames velocity for maximum effect
  532.   newmis.velocity = (self.velocity * 0.5) + (dir * 1000);
  533.   newmis.angles = vectoangles(newmis.velocity);
  534.   // turn the flame on it's side, widest end away from player
  535.   newmis.angles_x = newmis.angles_x + 90;
  536.  
  537.   // give the player a little kickback
  538.   self.velocity = self.velocity - (dir * 125);
  539.  
  540.   // will burn for 8 seconds
  541.   newmis.attack_finished = time + 8;
  542.   // continously check for water
  543.   newmis.nextthink = time + 0.1;
  544.   newmis.think = blaze_watercheck;
  545.   newmis.touch = blaze_touch;
  546.  
  547. //  launch_flame (start, dir);
  548.   self.punchangle_x = -2;
  549. };
  550.  
  551. void () blaze_attack1 = [$nailatt1, blaze_attack2]
  552. {
  553.   self.effects = self.effects | EF_MUZZLEFLASH;
  554.  
  555.   if (!self.button0)
  556.   {
  557.     player_run ();
  558.     return;
  559.   }
  560.  
  561.   self.weaponframe = self.weaponframe + 1;
  562.   if (self.weaponframe == 9) self.weaponframe = 1;
  563.  
  564.   SuperDamageSound();
  565.  
  566.   blaze_launch (4);
  567.  
  568.   self.attack_finished = time + 0.2;
  569. };
  570.  
  571. //============================================================================
  572.  
  573. void () blaze_attack2 = [$nailatt1, blaze_attack3]
  574. {
  575.   if (!self.button0)
  576.   {
  577.     player_run ();
  578.     return;
  579.   }
  580.  
  581.   self.weaponframe = self.weaponframe + 1;
  582.   if (self.weaponframe == 9) self.weaponframe = 1;
  583.  
  584.   self.attack_finished = time + 0.2;
  585. };
  586.  
  587. //============================================================================
  588.  
  589. void () blaze_attack3 = [$nailatt2, blaze_attack4]
  590. {
  591.   self.effects = self.effects | EF_MUZZLEFLASH;
  592.  
  593.   if (!self.button0)
  594.   {
  595.     player_run ();
  596.     return;
  597.   }
  598.  
  599.   self.weaponframe = self.weaponframe + 1;
  600.   if (self.weaponframe == 9) self.weaponframe = 1;
  601.  
  602.   SuperDamageSound();
  603.  
  604.   blaze_launch (-4);
  605.  
  606.   self.attack_finished = time + 0.2;
  607. };
  608.  
  609. //============================================================================
  610.  
  611. void () blaze_attack4 = [$nailatt2, blaze_attack1]
  612. {
  613.   if (!self.button0)
  614.   {
  615.     player_run ();
  616.     return;
  617.   }
  618.  
  619.   self.weaponframe = self.weaponframe + 1;
  620.   if (self.weaponframe == 9) self.weaponframe = 1;
  621.  
  622.   self.attack_finished = time + 0.2;
  623. };
  624.