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

  1. /*══════════════════════════════════════════════════════════════════════╗
  2.   ║                                                                       ║
  3.   ║                            CUJO BOT ver 1.2                           ║
  4.   ║                                                                       ║
  5.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  6.  
  7. // distance from player before cujo initiates a teleport
  8. float   teleport_dist  = 1500;
  9.  
  10. // Prototypes
  11.  
  12. float   ()                                             CUJO_FindTarget; //
  13. void    ()                                             CUJO_ai_stand;   //
  14. void    ()                                             CUJO_pain;       //
  15. void    ()                                             CUJO_die;        //
  16. void    ()                                             CUJO_SightSound; //
  17. void    ()                                             CUJO_FoundTarget;//
  18. void    ()                                             CUJO_HuntTarget; //
  19. void    (float dist)                                   CUJO_ai_walk;    //
  20. void    ()                                             CUJO_ai_turn;    //
  21. void    (float dist)                                   CUJO_ai_run;     //
  22. void    (float dist)                                   CUJO_ai_follow;  //
  23. void    ()                                             CUJO_ai_face;
  24. void    (float d)                                      CUJO_ai_charge;
  25. void    (void () thinkst)                              CUJO_CheckRefire;//
  26. void    ()                                             CUJO_SelfDeactivate;//
  27.  
  28. void    ()                                             CUJO_TeleportToOwner;//
  29. vector  ()                                             CUJO_SpawnPos;
  30. float   (vector SpawnPos)                              CUJO_CheckSpawnPos;
  31.  
  32. // Cujo Prototypes - called by player
  33.  
  34. void () CUJO_Precache;
  35. void () CUJO_Activate;        // Activate Cujo
  36. void () CUJO_Deactivate;    // DeActivate Cujo
  37. void () CUJO_Toggle;        // Toggle Cujo on and off
  38. void () CUJO_AttackToggle;    // Toggle Cujo's auto-firing in auto mode
  39. /*
  40. void ()    CUJO_Attack;        // Have Cujo fire at its current target in auto mode
  41. */
  42. void ()    CUJO_TeleportHome;    // Have Cujo teleport back to its owner
  43. void ()    CUJO_LightToggle;    // Have Cujo teleport back to its owner
  44. void () CUJO_SetDogView;
  45. void () CUJO_SetPlayerView;
  46.  
  47. // Global Variables
  48.  
  49. float   goal_range;
  50. float   goal_vis;
  51. float   goal_infront;
  52. float   goal_yaw;
  53. vector  old_player_angles;
  54.  
  55. // new for Cujo jumping AI
  56. float   forward_jump_vel;
  57. float   upward_jump_vel;
  58.  
  59. // Constants
  60.  
  61. float   nextthinktime = 0.01;
  62.  
  63. $cd /raid/quake/id1/models/dog
  64. $origin 0 0 24
  65. $base base
  66. $skin skin
  67.  
  68. $frame attack1 attack2 attack3 attack4 attack5 attack6 attack7 attack8
  69.  
  70. $frame death1 death2 death3 death4 death5 death6 death7 death8 death9
  71.  
  72. $frame deathb1 deathb2 deathb3 deathb4 deathb5 deathb6 deathb7 deathb8
  73. $frame deathb9
  74.  
  75. $frame pain1 pain2 pain3 pain4 pain5 pain6
  76.  
  77. $frame painb1 painb2 painb3 painb4 painb5 painb6 painb7 painb8 painb9 painb10
  78. $frame painb11 painb12 painb13 painb14 painb15 painb16
  79.  
  80. $frame run1 run2 run3 run4 run5 run6 run7 run8 run9 run10 run11 run12
  81.  
  82. $frame leap1 leap2 leap3 leap4 leap5 leap6 leap7 leap8 leap9
  83.  
  84. $frame stand1 stand2 stand3 stand4 stand5 stand6 stand7 stand8 stand9
  85.  
  86. $frame walk1 walk2 walk3 walk4 walk5 walk6 walk7 walk8
  87.  
  88.  
  89. void() dog_leap1;
  90. void() dog_run1;
  91.  
  92.  
  93. /*╔═══════════════════════════════════════════════════════════════════════╗
  94.   ║                                                                       ║
  95.   ║                 Routines called by Cujo, self = Cujo                  ║
  96.   ║                                                                       ║
  97.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  98.  
  99. /*╔═══════════════════════════════════════════════════════════════════════╗
  100.   ║                                                                       ║
  101.   ║ CUJO_PrintCujoStatus                                                  ║
  102.   ║                                                                       ║
  103.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  104.  
  105. void () CUJO_PrintCujoStatus =
  106.  
  107. {
  108.   if (!deathmatch)
  109.     sprint (self, "Cujo is not here.\n");
  110.   else
  111.   {
  112.     if (self.Cujo_avail)
  113.       sprint (self, "Cujo is not here.\n");
  114.     else
  115.       sprint (self, "Cujo is not available.\n");
  116.   }
  117. };
  118.  
  119. /*╔═══════════════════════════════════════════════════════════════════════╗
  120.   ║                                                                       ║
  121.   ║ CUJO_ResetGoalEntity                                                  ║
  122.   ║                                                                       ║
  123.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  124.  
  125. void () CUJO_ResetGoalEntity =
  126.  
  127. {
  128.   self.oldenemy = self.enemy;
  129.   self.enemy = world;
  130.  
  131.   self.goalentity = self.owner;
  132.   self.movetarget = self.owner;
  133. };
  134.  
  135. /*╔═══════════════════════════════════╗
  136.   ║                                   ║
  137.   ║ Cujo_bite                         ║
  138.   ║                                   ║
  139.   ╚═══════════════════════════════════╝*/
  140.  
  141. void() CUJO_bite =
  142. {
  143.   local vector    delta;
  144.   local float     ldmg;
  145.  
  146.   if (!self.enemy) return;
  147.  
  148.   CUJO_ai_charge(10);
  149.  
  150.   if (!CanDamage (self.enemy, self)) return;
  151.  
  152.   delta = self.enemy.origin - self.origin;
  153.  
  154.   if (vlen(delta) > 100) return;
  155.  
  156.   // does twice the damage of a normal dog and has a random chance
  157.   // of gibbing!
  158.  
  159.   if ((self.enemy.health < 10) && (random () < 0.1))
  160.   {
  161.     {
  162.       ldmg = 50 * random ();
  163.     }
  164.   }
  165.   else
  166.   {
  167.     ldmg = (random() + random() + random()) * 16;
  168.   }
  169.  
  170.   T_Damage (self.enemy, self, self, ldmg);
  171. };
  172.  
  173. /*╔═══════════════════════════════════╗
  174.   ║                                   ║
  175.   ║ Cujo_ChangeYaw                    ║
  176.   ║                                   ║
  177.   ╚═══════════════════════════════════╝*/
  178.  
  179. void() CUJO_ChangeYaw =
  180. {
  181.   local float        ideal, move;
  182.  
  183.   current_yaw = anglemod (self.angles_y);
  184.   ideal = self.ideal_yaw;
  185.  
  186.   if (current_yaw == ideal)
  187.     return;
  188.  
  189.   move = ideal - current_yaw;
  190.   if (ideal > current_yaw)
  191.   {
  192.     if (move > 180)
  193.       move = move - 360;
  194.   }
  195.   else
  196.   {
  197.     if (move < -180)
  198.       move = move + 360;
  199.   }
  200.  
  201.   if (move > 0)
  202.   {
  203.     if (move > self.yaw_speed)
  204.     move = self.yaw_speed;
  205.   }
  206.   else
  207.   {
  208.     if (move < 0-self.yaw_speed )
  209.     move = 0-self.yaw_speed;
  210.   }
  211.  
  212.   current_yaw = anglemod (current_yaw + move);
  213.   if (fabs (ideal - current_yaw) < self.yaw_speed)
  214.     current_yaw = ideal;
  215.  
  216.  
  217.   self.angles_y = current_yaw;
  218. };
  219.  
  220. /*╔═══════════════════════════════════╗
  221.   ║                                   ║
  222.   ║ Cujo_jumptouch                    ║
  223.   ║                                   ║
  224.   ╚═══════════════════════════════════╝*/
  225.  
  226. void()    CUJO_JumpTouch =
  227. {
  228.   local    float    ldmg;
  229.  
  230.   if (self.health <= 0) return;
  231.  
  232.   if (other.takedamage)
  233.   {
  234.     if ( vlen(self.velocity) > 300 )
  235.     {
  236.       // Cujo now has a chance to gib on a jumping attack
  237.       ldmg = 20 + 20*random();
  238.  
  239.       T_Damage (other, self, self, ldmg);
  240.     }
  241.   }
  242. /*
  243.   if (!checkbottom(self))
  244.   {
  245.  
  246.     if (self.flags & FL_ONGROUND)
  247.     {    // jump randomly to not get hung up
  248.       //dprint ("popjump\n");
  249.       self.touch = SUB_Null;
  250.       self.think = dog_leap1;
  251.       self.nextthink = time + nextthinktime;
  252.     }
  253.     return;    // not on ground yet
  254.   }
  255. */
  256.   self.touch = SUB_Null;
  257.   self.think = dog_run1;
  258.   self.nextthink = time + nextthinktime;
  259. };
  260.  
  261.  
  262. void() CUJO_stand1    =[    $stand1,    CUJO_stand2    ] {CUJO_ai_stand();};
  263. void() CUJO_stand2    =[    $stand2,    CUJO_stand3    ] {CUJO_ai_stand();};
  264. void() CUJO_stand3    =[    $stand3,    CUJO_stand4    ] {CUJO_ai_stand();};
  265. void() CUJO_stand4    =[    $stand4,    CUJO_stand5    ] {CUJO_ai_stand();};
  266. void() CUJO_stand5    =[    $stand5,    CUJO_stand6    ] {CUJO_ai_stand();};
  267. void() CUJO_stand6    =[    $stand6,    CUJO_stand7    ] {CUJO_ai_stand();};
  268. void() CUJO_stand7    =[    $stand7,    CUJO_stand8    ] {CUJO_ai_stand();};
  269. void() CUJO_stand8    =[    $stand8,    CUJO_stand9    ] {CUJO_ai_stand();};
  270. void() CUJO_stand9    =[    $stand9,    CUJO_stand1    ] {CUJO_ai_stand();};
  271.  
  272. /*╔═══════════════════════════════════╗
  273.   ║                                   ║
  274.   ║ Cujo_walk1                        ║
  275.   ║                                   ║
  276.   ╚═══════════════════════════════════╝*/
  277.  
  278.  
  279. void() CUJO_walk1    =[    $walk1 ,    CUJO_walk2    ]
  280. {
  281.   if (random() < 0.2)
  282.     sound (self, CHAN_VOICE, "dog/idle.wav", 1, ATTN_IDLE);
  283.  
  284.   CUJO_ai_walk (8);
  285. };
  286.  
  287.  
  288. void() CUJO_walk2    =[    $walk2 ,    CUJO_walk3    ] {CUJO_ai_walk(8);};
  289. void() CUJO_walk3    =[    $walk3 ,    CUJO_walk4    ] {CUJO_ai_walk(8);};
  290. void() CUJO_walk4    =[    $walk4 ,    CUJO_walk5    ] {CUJO_ai_walk(8);};
  291. void() CUJO_walk5    =[    $walk5 ,    CUJO_walk6    ] {CUJO_ai_walk(8);};
  292. void() CUJO_walk6    =[    $walk6 ,    CUJO_walk7    ] {CUJO_ai_walk(8);};
  293. void() CUJO_walk7    =[    $walk7 ,    CUJO_walk8    ] {CUJO_ai_walk(8);};
  294. void() CUJO_walk8    =[    $walk8 ,    CUJO_walk1    ] {CUJO_ai_walk(8);};
  295.  
  296. /*╔═══════════════════════════════════╗
  297.   ║                                   ║
  298.   ║ Cujo_run1                         ║
  299.   ║                                   ║
  300.   ╚═══════════════════════════════════╝*/
  301.  
  302. void() CUJO_run1        =[    $run1  ,    CUJO_run2    ]
  303. {
  304.   if (random() < 0.2)
  305.     sound (self, CHAN_VOICE, "dog/idle.wav", 1, ATTN_IDLE);
  306.  
  307.   CUJO_ai_run (16);
  308. };
  309.  
  310. void() CUJO_run2        =[    $run2  ,    CUJO_run3    ] {CUJO_ai_run(32);};
  311. void() CUJO_run3        =[    $run3  ,    CUJO_run4    ] {CUJO_ai_run(32);};
  312. void() CUJO_run4        =[    $run4  ,    CUJO_run5    ] {CUJO_ai_run(20);};
  313. void() CUJO_run5        =[    $run5  ,    CUJO_run6    ] {CUJO_ai_run(64);};
  314. void() CUJO_run6        =[    $run6  ,    CUJO_run7    ] {CUJO_ai_run(32);};
  315. void() CUJO_run7        =[    $run7  ,    CUJO_run8    ] {CUJO_ai_run(16);};
  316. void() CUJO_run8        =[    $run8  ,    CUJO_run9    ] {CUJO_ai_run(32);};
  317. void() CUJO_run9        =[    $run9  ,    CUJO_run10    ] {CUJO_ai_run(32);};
  318. void() CUJO_run10    =[    $run10  ,    CUJO_run11    ] {CUJO_ai_run(20);};
  319. void() CUJO_run11    =[    $run11  ,    CUJO_run12    ] {CUJO_ai_run(64);};
  320. void() CUJO_run12    =[    $run12  ,    CUJO_run1    ] {CUJO_ai_run(32);};
  321.  
  322. void()    CUJO_follow1    =[    $run1  ,    CUJO_follow2    ]
  323. {
  324.   if (random() < 0.2)
  325.     sound (self, CHAN_VOICE, "dog/idle.wav", 1, ATTN_IDLE);
  326.  
  327.   CUJO_ResetGoalEntity ();
  328.  
  329.   CUJO_ai_follow (18);
  330. };
  331. void()    CUJO_follow2    =[    $run2  ,    CUJO_follow3    ] {CUJO_ai_follow (32);};
  332. void()    CUJO_follow3    =[    $run3  ,    CUJO_follow4    ] {CUJO_ai_follow (32);};
  333. void()    CUJO_follow4    =[    $run4  ,    CUJO_follow5    ] {CUJO_ai_follow (20);};
  334. void()    CUJO_follow5    =[    $run5  ,    CUJO_follow6    ] {CUJO_ai_follow (64);};
  335. void()    CUJO_follow6    =[    $run6  ,    CUJO_follow7    ] {CUJO_ai_follow (32);};
  336. void()    CUJO_follow7    =[    $run7  ,    CUJO_follow8    ] {CUJO_ai_follow (16);};
  337. void()    CUJO_follow8    =[    $run8  ,    CUJO_follow9    ] {CUJO_ai_follow (32);};
  338. void()  CUJO_follow9    =[    $run9  ,    CUJO_follow10    ] {CUJO_ai_follow(32);};
  339. void()  CUJO_follow10    =[    $run10  ,    CUJO_follow11    ] {CUJO_ai_follow(20);};
  340. void()  CUJO_follow11    =[    $run11  ,    CUJO_follow12    ] {CUJO_ai_follow(64);};
  341. void()  CUJO_follow12    =[    $run12  ,    CUJO_follow1    ] {CUJO_ai_follow(32);};
  342.  
  343.  
  344. /*╔═══════════════════════════════════╗
  345.   ║                                   ║
  346.   ║ Cujo_atta1                        ║
  347.   ║                                   ║
  348.   ╚═══════════════════════════════════╝*/
  349.  
  350.  
  351. void() CUJO_atta1    =[    $attack1,    CUJO_atta2    ] {ai_charge(10);};
  352. void() CUJO_atta2    =[    $attack2,    CUJO_atta3    ] {ai_charge(10);};
  353. void() CUJO_atta3    =[    $attack3,    CUJO_atta4    ] {ai_charge(10);};
  354.  
  355. void() CUJO_atta4    =[    $attack4,    CUJO_atta5    ]
  356. {
  357.   sound (self, CHAN_VOICE, "dog/dattack1.wav", 1, ATTN_NORM);
  358.   CUJO_bite();
  359. };
  360.  
  361. void() CUJO_atta5    =[    $attack5,    CUJO_atta6    ] {ai_charge(10);};
  362. void() CUJO_atta6    =[    $attack6,    CUJO_atta7    ] {ai_charge(10);};
  363. void() CUJO_atta7    =[    $attack7,    CUJO_atta8    ] {ai_charge(10);};
  364. void() CUJO_atta8    =[    $attack8,    CUJO_run1       ]
  365. {
  366.   local   float   r;
  367.  
  368.   if (self.goalentity.classname == "dog_food")
  369.   {
  370.     // CUJO 1.3 - fix for BodyQue bug
  371.     // don't remove a cujo or a player head, because they are in the
  372.     // body que, simply change their classname to "head" and model to
  373.     // "" and cujo will ignore them
  374.     if ((self.goalentity.model == "progs/h_cujo.mdl") ||
  375.        (self.goalentity.model == "progs/player.mdl"))
  376.     {
  377.       self.goalentity.classname = "head";
  378.       setmodel (self.goalentity, "");
  379.     }
  380.     else
  381.       remove (self.goalentity);
  382.     self.health = self.health + 15;
  383.  
  384.     if ((deathmatch) && (self.health > 100))
  385.       self.health = 100;
  386.     else if (self.health > 200)
  387.       self.health = 200;
  388.  
  389.     r = random ();
  390.  
  391.     if (r < 0.25)
  392.       sprint (self.owner, "Cujo likes his Gibby-chow!\n");
  393.     else if (r < 0.5)
  394.       sprint (self.owner, "Dogs love a Gibby-Treat!\n");
  395.     else if (r < 0.75)
  396.       sprint (self.owner, "Cujo ate some gibs.\n");
  397.     else
  398.       sprint (self.owner, "Cujo's eating his enemies again...\n");
  399.  
  400.   }
  401.   else
  402.     CUJO_ai_charge (10);
  403.  
  404.   CUJO_CheckRefire (CUJO_atta1);
  405. };
  406.  
  407. /*╔═══════════════════════════════════╗
  408.   ║                                   ║
  409.   ║ Cujo_leap1                        ║
  410.   ║                                   ║
  411.   ╚═══════════════════════════════════╝*/
  412.  
  413. void() CUJO_leap1    =[    $leap1,        CUJO_leap2    ] {ai_face();};
  414. void() CUJO_leap2    =[    $leap2,        CUJO_leap3    ]
  415. {
  416.   CUJO_ai_face();
  417.   self.touch = CUJO_JumpTouch;
  418.  
  419.   makevectors (self.angles);
  420.  
  421.   self.origin_z = self.origin_z + 1;
  422.   self.velocity = v_forward * forward_jump_vel + v_up * upward_jump_vel;
  423. /*
  424.   bprint ("fv=");
  425.   bprint (ftos (forward_jump_vel));
  426.   bprint (", uv=");
  427.   bprint (ftos (upward_jump_vel));
  428.   bprint ("\n");
  429. */
  430.   if (self.flags & FL_ONGROUND)
  431.     self.flags = self.flags - FL_ONGROUND;
  432. };
  433.  
  434. void() CUJO_leap3    =[    $leap3,        CUJO_leap4    ] {};
  435. void() CUJO_leap4    =[    $leap4,        CUJO_leap5    ] {};
  436. void() CUJO_leap5    =[    $leap5,        CUJO_leap6    ] {};
  437. void() CUJO_leap6    =[    $leap6,        CUJO_leap7    ] {};
  438. void() CUJO_leap7    =[    $leap7,        CUJO_leap8    ] {};
  439. void() CUJO_leap8    =[    $leap8,        CUJO_leap9    ] {};
  440. void() CUJO_leap9    =[    $leap9,        CUJO_leap9    ] {};
  441.  
  442. /*╔═══════════════════════════════════╗
  443.   ║                                   ║
  444.   ║ Cujo_pain1                        ║
  445.   ║                                   ║
  446.   ╚═══════════════════════════════════╝*/
  447.  
  448. void() CUJO_pain1    =[    $pain1 ,    CUJO_pain2    ] {};
  449. void() CUJO_pain2    =[    $pain2 ,    CUJO_pain3    ] {};
  450. void() CUJO_pain3    =[    $pain3 ,    CUJO_pain4    ] {};
  451. void() CUJO_pain4    =[    $pain4 ,    CUJO_pain5    ] {};
  452. void() CUJO_pain5    =[    $pain5 ,    CUJO_pain6    ] {};
  453. void() CUJO_pain6    =[    $pain6 ,    CUJO_run1    ] {};
  454.  
  455. void() CUJO_painb1    =[    $painb1 ,    CUJO_painb2    ] {};
  456. void() CUJO_painb2    =[    $painb2 ,    CUJO_painb3    ] {};
  457. void() CUJO_painb3    =[    $painb3 ,    CUJO_painb4    ] {ai_pain(4);};
  458. void() CUJO_painb4    =[    $painb4 ,    CUJO_painb5    ] {ai_pain(12);};
  459. void() CUJO_painb5    =[    $painb5 ,    CUJO_painb6    ] {ai_pain(12);};
  460. void() CUJO_painb6    =[    $painb6 ,    CUJO_painb7    ] {ai_pain(2);};
  461. void() CUJO_painb7    =[    $painb7 ,    CUJO_painb8    ] {};
  462. void() CUJO_painb8    =[    $painb8 ,    CUJO_painb9    ] {ai_pain(4);};
  463. void() CUJO_painb9    =[    $painb9 ,    CUJO_painb10    ] {};
  464. void() CUJO_painb10    =[    $painb10 ,    CUJO_painb11    ] {ai_pain(10);};
  465. void() CUJO_painb11    =[    $painb11 ,    CUJO_painb12    ] {};
  466. void() CUJO_painb12    =[    $painb12 ,    CUJO_painb13    ] {};
  467. void() CUJO_painb13    =[    $painb13 ,    CUJO_painb14    ] {};
  468. void() CUJO_painb14    =[    $painb14 ,    CUJO_painb15    ] {};
  469. void() CUJO_painb15    =[    $painb15 ,    CUJO_painb16    ] {};
  470. void() CUJO_painb16    =[    $painb16 ,    CUJO_run1    ] {};
  471.  
  472. void() CUJO_pain =
  473. {
  474.   if (self.dmg_inflictor.classname == "fire")
  475.   {
  476.     if (random() > 0.5)
  477.     {
  478.       if (self.pain_finished > time)
  479.         return;
  480.  
  481.       if (random () > 0.2)
  482.       {
  483.         CUJO_pain1 ();
  484.         self.pain_finished = time + 0.6;
  485.       }
  486.       else
  487.       {
  488.         self.pain_finished = time + 1.6;
  489.         CUJO_painb1 ();
  490.       }
  491.  
  492.       sound (self, CHAN_VOICE, "dog/dpain1.wav", 1, ATTN_NORM);
  493.     }
  494.   }
  495.   else
  496.   {
  497.     sound (self, CHAN_VOICE, "dog/dpain1.wav", 1, ATTN_NORM);
  498.  
  499.     if (random() > 0.5)
  500.       CUJO_pain1 ();
  501.     else
  502.       CUJO_painb1 ();
  503.   }
  504. };
  505.  
  506. /*╔═══════════════════════════════════╗
  507.   ║                                   ║
  508.   ║ Cujo_die                          ║
  509.   ║                                   ║
  510.   ║ Cujo deactivates self in last     ║
  511.   ║ frame                             ║
  512.   ║                                   ║
  513.   ╚═══════════════════════════════════╝*/
  514.  
  515. void() CUJO_die1        =[    $death1,    CUJO_die2    ] {};
  516. void() CUJO_die2        =[    $death2,    CUJO_die3    ] {};
  517. void() CUJO_die3        =[    $death3,    CUJO_die4    ] {};
  518. void() CUJO_die4        =[    $death4,    CUJO_die5    ] {};
  519. void() CUJO_die5        =[    $death5,    CUJO_die6    ] {};
  520. void() CUJO_die6        =[    $death6,    CUJO_die7    ] {};
  521. void() CUJO_die7        =[    $death7,    CUJO_die8    ] {};
  522. void() CUJO_die8        =[    $death8,    CUJO_die9    ] {};
  523. void() CUJO_die9        =[    $death9,    CUJO_die9    ] {CUJO_SelfDeactivate ();};
  524. /*
  525. // used to set the dead-entity body to the correct final death frame
  526. // then set up the next think to make the body disappear
  527. void() CUJO_die10        =[    $death9,    CUJO_die10    ]
  528. {
  529.   self.nextthink = time + 120;  // wait 2 minutes till body disappears
  530.   self.think = SUB_Remove;
  531. };
  532. */
  533. void() CUJO_dieb1        =[    $deathb1,    CUJO_dieb2    ] {};
  534. void() CUJO_dieb2        =[    $deathb2,    CUJO_dieb3    ] {};
  535. void() CUJO_dieb3        =[    $deathb3,    CUJO_dieb4    ] {};
  536. void() CUJO_dieb4        =[    $deathb4,    CUJO_dieb5    ] {};
  537. void() CUJO_dieb5        =[    $deathb5,    CUJO_dieb6    ] {};
  538. void() CUJO_dieb6        =[    $deathb6,    CUJO_dieb7    ] {};
  539. void() CUJO_dieb7        =[    $deathb7,    CUJO_dieb8    ] {};
  540. void() CUJO_dieb8        =[    $deathb8,    CUJO_dieb9    ] {};
  541. void() CUJO_dieb9        =[    $deathb9,    CUJO_dieb9    ] {CUJO_SelfDeactivate ();};
  542. /*
  543. // used to set the dead-entity body to the correct final death frame
  544. // then set up the next think to make the body disappear
  545. void() CUJO_dieb10            =[    $deathb9,    CUJO_dieb10    ]
  546. {
  547.   self.nextthink = time + 120;  // wait 2 minutes till body disappears
  548.   self.think = SUB_Remove;
  549. };
  550. */
  551.  
  552. void() CUJO_die =
  553. {
  554. // check for gib
  555.   if (self.health < -35)
  556.   {
  557.     sound (self, CHAN_VOICE, "player/udeath.wav", 1, ATTN_NORM);
  558.     ThrowGib ("progs/gib3.mdl", self.health);
  559.     ThrowGib ("progs/gib3.mdl", self.health);
  560.     ThrowGib ("progs/gib3.mdl", self.health);
  561.  
  562.     ThrowHead ("progs/h_cujo.mdl", self.health);
  563.  
  564.     // Cujo heads only lay around for 2 minutes, then POOF!
  565. //    self.nextthink = time + 120;
  566. //    self.think = SUB_Remove;
  567.  
  568.     CUJO_SelfDeactivate ();
  569.  
  570.     return;
  571.   }
  572.  
  573. // regular death
  574.   sound (self, CHAN_VOICE, "dog/ddeath.wav", 1, ATTN_NORM);
  575.   self.solid = SOLID_NOT;
  576.  
  577.   self.deadflag = DEAD_DYING;
  578.  
  579.   if (random() > 0.5)
  580.     CUJO_die1 ();
  581.   else
  582.     CUJO_dieb1 ();
  583. };
  584.  
  585. /*╔═══════════════════════════════════════════════════════════════════════╗
  586.   ║                                                                       ║
  587.   ║ Cujo_checkmelee                                                       ║
  588.   ║                                                                       ║
  589.   ║ Returns TRUE if a melee attack would hit right now                    ║
  590.   ║                                                                       ║
  591.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  592.  
  593. float()    CUJO_CheckMelee =
  594. {
  595.   local   float   dist;
  596.  
  597.   dist = vlen (self.goalentity.origin - self.origin);
  598.  
  599.   if ( ((self.goalentity.classname == "dog_food") && (dist < 100)) ||
  600.       ((self.goalentity.classname != "dog_food") && (dist < 50)) )
  601. //  if (goal_range == RANGE_MELEE)
  602.   {
  603.     self.attack_state = AS_MELEE;
  604.  
  605. //    bprint ("CUJO_CheckMelee says TRUE!\n");
  606.  
  607.     return TRUE;
  608.   }
  609.  
  610.   return FALSE;
  611. };
  612.  
  613. /*╔═══════════════════════════════════════════════════════════════════════╗
  614.   ║                                                                       ║
  615.   ║ Cujo_checkjump                                                        ║
  616.   ║                                                                       ║
  617.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  618.  
  619. float()    CUJO_CheckJump =
  620. {
  621.   local    vector    dist;
  622.   local    float    d;
  623.  
  624.   if (self.origin_z + self.mins_z > self.goalentity.origin_z + self.goalentity.mins_z
  625.   + 0.75 * self.goalentity.size_z)
  626.     return FALSE;
  627.  
  628.   if (self.origin_z + self.maxs_z < self.goalentity.origin_z + self.goalentity.mins_z
  629.   + 0.25 * self.goalentity.size_z)
  630.     return FALSE;
  631.  
  632.   dist = self.goalentity.origin - self.origin;
  633.   dist_z = 0;
  634.  
  635.   d = vlen(dist);
  636.  
  637.   if (d < 80) return FALSE;
  638.  
  639.   if (d > 150) return FALSE;
  640.  
  641.   return TRUE;
  642. };
  643.  
  644. /*╔═══════════════════════════════════════════════════════════════════════╗
  645.   ║                                                                       ║
  646.   ║ CUJO_SpawnMarker                                                      ║
  647.   ║                                                                       ║
  648.   ║ For debugging:  spawns a temporary marker at the passed location      ║
  649.   ║                                                                       ║
  650.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  651.  
  652. void (vector org, string mdl_name) CUJO_SpawnMarker =
  653. {
  654.   local   entity  marker;
  655.  
  656.   marker = spawn ();
  657.  
  658.   marker.solid = SOLID_NOT;
  659.   marker.movetype = MOVETYPE_NONE;
  660.   marker.think = SUB_Remove;
  661.   marker.nextthink = time + 10;
  662.   marker.takedamage = DAMAGE_NO;
  663.   marker.classname = "debug_marker";
  664.  
  665.   setsize (marker, '0 0 0', '0 0 0');
  666.   setorigin (marker, org);
  667.   setmodel (marker, mdl_name);
  668. };
  669.  
  670. /*╔═══════════════════════════════════════════════════════════════════════╗
  671.   ║                                                                       ║
  672.   ║ CUJO_FindWater                                                        ║
  673.   ║                                                                       ║
  674.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  675.  
  676. float () CUJO_FindWater =
  677.  
  678. {
  679.   local   float   i, found_water, highest;
  680.   local   vector  org, end;
  681.  
  682.   makevectors (self.angles);
  683.  
  684.   // check to see if cujo is already in water
  685.   org = self.origin;
  686.   end = org - (v_up * 1000);
  687.   traceline (org, end, TRUE, self);
  688.   if ((trace_inwater) && (!trace_inopen))
  689.     return 0;
  690.  
  691.   found_water = FALSE;
  692.   i = 0;
  693.   while ((i <= 270) && (!found_water))
  694.   {
  695.     // check the four corners of a 30x30 unit square i units in front of cujo
  696.     // upper right corner
  697.  
  698.     org = self.origin + (v_forward * (i + 30)) + (v_right * 15);
  699.     end = org - (v_up * 1000);
  700.     traceline (org, end, FALSE, self);
  701.     highest = trace_fraction;
  702.  
  703.     if (trace_inwater)
  704.     {
  705.       // upper left corner
  706.       org = self.origin + (v_forward * (i + 30)) - (v_right * 15);
  707.       end = org - (v_up * 1000);
  708.       traceline (org, end, FALSE, self);
  709.       if (trace_fraction < highest) highest = trace_fraction;
  710.  
  711.       if (trace_inwater)
  712.       {
  713.         // lower right corner
  714.         org = self.origin + (v_forward * i) + (v_right * 15);
  715.         end = org - (v_up * 1000);
  716.         traceline (org, end, FALSE, self);
  717.         if (trace_fraction < highest) highest = trace_fraction;
  718.  
  719.         if (trace_inwater)
  720.         {
  721.           // lower left corner
  722.           org = self.origin + (v_forward * i) - (v_right * 15);
  723.           end = org - (v_up * 1000);
  724.           traceline (org, end, FALSE, self);
  725.           if (trace_fraction < highest) highest = trace_fraction;
  726.  
  727.           if (trace_inwater)
  728.           {
  729.             // center
  730.             org = self.origin + (v_forward * (i + 15));
  731.             end = org - (v_up * 1000);
  732.             traceline (org, end, FALSE, self);
  733.             if (trace_fraction < highest) highest = trace_fraction;
  734.  
  735.             if (trace_inwater)
  736.             {
  737.               // half way between center and left back corner
  738.               org = self.origin + (v_forward * (i + 7.5)) - (v_right * 7.5);
  739.               end = org - (v_up * 1000);
  740.               traceline (org, end, FALSE, self);
  741.               if (trace_fraction < highest) highest = trace_fraction;
  742.  
  743.               if (trace_inwater)
  744.               {
  745.                 // half way between center and right back corner
  746.                 org = self.origin + (v_forward * (i + 7.5)) + (v_right * 7.5);
  747.                 end = org - (v_up * 1000);
  748.                 traceline (org, end, FALSE, self);
  749.                 if (trace_fraction < highest) highest = trace_fraction;
  750.  
  751.                 if (trace_inwater) found_water = TRUE;
  752.               }
  753.             }
  754.           }
  755.         }
  756.       }
  757.     }
  758.     i = i + 12;
  759.   }
  760.  
  761.   if (found_water)
  762.   {
  763. //    bprint ("water ");
  764. //    bprint (ftos (i));
  765. //    bprint (", ");
  766.  
  767.     return i;
  768.   }
  769.   else
  770.     return 0;
  771. };
  772.  
  773. /*╔═══════════════════════════════════════════════════════════════════════╗
  774.   ║                                                                       ║
  775.   ║ CUJO_Findgap                                                          ║
  776.   ║                                                                       ║
  777.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  778.  
  779. float () CUJO_FindGap =
  780.  
  781. {
  782.   local   float   i, found_gap, gap_depth, min_depth, max_depth, grade, trace_depth;
  783.   local   vector  org, end;
  784.  
  785.   makevectors (self.angles);
  786.  
  787.   found_gap = FALSE;
  788.   gap_depth = 0.016;
  789.   trace_depth = 1000;
  790.  
  791.   i = 0;
  792.   while ((i <= 270) && (!found_gap))
  793.   {
  794.     // check the four corners of a 30x30 unit square i units in front of cujo
  795.     // upper right corner
  796.     org = self.origin + (v_forward * (i + 30)) + (v_right * 15);
  797.     end = org - (v_up * trace_depth);
  798.     traceline (org, end, FALSE, self);
  799.     max_depth = trace_fraction;
  800.     min_depth = trace_fraction;
  801.  
  802.     if (trace_fraction > gap_depth)
  803.     {
  804.       // upper left corner
  805.       org = self.origin + (v_forward * (i + 30)) - (v_right * 15);
  806.       end = org - (v_up * trace_depth);
  807.       traceline (org, end, FALSE, self);
  808.       if (trace_fraction < min_depth) min_depth = trace_fraction;
  809.       if (trace_fraction > max_depth) max_depth = trace_fraction;
  810.  
  811.       if (trace_fraction > gap_depth)
  812.       {
  813.         // lower right corner
  814.         org = self.origin + (v_forward * i) + (v_right * 15);
  815.         end = org - (v_up * trace_depth);
  816.         traceline (org, end, FALSE, self);
  817.         if (trace_fraction < min_depth) min_depth = trace_fraction;
  818.         if (trace_fraction > max_depth) max_depth = trace_fraction;
  819.  
  820.         if (trace_fraction > gap_depth)
  821.         {
  822.           // lower left corner
  823.           org = self.origin + (v_forward * i) - (v_right * 15);
  824.           end = org - (v_up * trace_depth);
  825.           traceline (org, end, FALSE, self);
  826.           if (trace_fraction < min_depth) min_depth = trace_fraction;
  827.           if (trace_fraction > max_depth) max_depth = trace_fraction;
  828.  
  829.           if (trace_fraction > gap_depth)
  830.           {
  831.             // center
  832.             org = self.origin + (v_forward * (i + 15));
  833.             end = org - (v_up * trace_depth);
  834.             traceline (org, end, FALSE, self);
  835.             if (trace_fraction < min_depth) min_depth = trace_fraction;
  836.             if (trace_fraction > max_depth) max_depth = trace_fraction;
  837.  
  838.             if (trace_fraction > gap_depth)
  839.             {
  840.               // half way between center and left back corner
  841.               org = self.origin + (v_forward * (i + 7.5)) - (v_right * 7.5);
  842.               end = org - (v_up * trace_depth);
  843.               traceline (org, end, FALSE, self);
  844.               if (trace_fraction < min_depth) min_depth = trace_fraction;
  845.               if (trace_fraction > max_depth) max_depth = trace_fraction;
  846.  
  847.               if (trace_fraction > gap_depth)
  848.               {
  849.                 // half way between center and right back corner
  850.                 org = self.origin + (v_forward * (i + 7.5)) + (v_right * 7.5);
  851.                 end = org - (v_up * trace_depth);
  852.                 traceline (org, end, FALSE, self);
  853.                 if (trace_fraction < min_depth) min_depth = trace_fraction;
  854.                 if (trace_fraction > max_depth) max_depth = trace_fraction;
  855.  
  856.                 if (trace_fraction > gap_depth)
  857.                 {
  858.                   max_depth = max_depth * trace_depth;
  859.                   min_depth = min_depth * trace_depth;
  860.                   grade = (max_depth - min_depth) / 30;
  861.  
  862.                   if (grade > 1.5)
  863.                     found_gap = TRUE;
  864.                 }
  865.               }
  866.             }
  867.           }
  868.         }
  869.       }
  870.     }
  871.     i = i + 30;
  872.   }
  873.  
  874.   if (found_gap)
  875.   {
  876. /*
  877.     bprint ("gap=");
  878.     bprint (ftos (i));
  879.     bprint (" min=");
  880.     bprint (ftos(min_depth));
  881.     bprint (" max=");
  882.     bprint (ftos(max_depth));
  883.     bprint (", ");
  884. */
  885.     return i;
  886.   }
  887.   else
  888.   {
  889. /*
  890.     bprint ("gap=");
  891.     bprint (ftos (i));
  892.     bprint (" min=");
  893.     bprint (ftos(min_depth));
  894.     bprint (" max=");
  895.     bprint (ftos(max_depth));
  896.     bprint (", ");
  897. */
  898.     return 0;
  899.   }
  900. };
  901.  
  902. /*╔═══════════════════════════════════════════════════════════════════════╗
  903.   ║                                                                       ║
  904.   ║ CUJO_FindLedge                                                        ║
  905.   ║                                                                       ║
  906.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  907.  
  908. float (float start_dist) CUJO_FindLedge =
  909.  
  910. {
  911.   local   float   i;
  912.   local   vector  org, end, above_org;
  913.   local   float   highest, pc;
  914.   local   float   found_ledge;
  915.   local   float   ledge_height;
  916.  
  917.   if (start_dist > 440)
  918.     return 0;
  919.  
  920.   makevectors (self.angles);
  921.   // start origin is 48 units above Cujo's origin
  922.   above_org = self.origin + (v_up * 48);
  923.  
  924.   ledge_height = 0.096;
  925.   found_ledge = FALSE;
  926.   i = start_dist + 30;
  927.  
  928.   while ((i <= 440) && (!found_ledge))
  929.   {
  930.     // check the four corners of a 30x30 unit square i units in front of cujo
  931.     // upper right corner
  932.     org = above_org + (v_forward * (i + 30)) + (v_right * 15);
  933.     end = org - (v_up * 1000);
  934.     traceline (org, end, FALSE, self);
  935.     highest = trace_fraction;
  936.  
  937.     // upper left corner
  938.     if (trace_fraction <= ledge_height)
  939.     {
  940.       org = above_org + (v_forward * (i + 30)) - (v_right * 15);
  941.       end = org - (v_up * 1000);
  942.       traceline (org, end, FALSE, self);
  943.       if (trace_fraction < highest) highest = trace_fraction;
  944.  
  945.       if (trace_fraction <= ledge_height)
  946.       {
  947.         // lower right corner
  948.         org = above_org + (v_forward * i) + (v_right * 15);
  949.         end = org - (v_up * 1000);
  950.         traceline (org, end, FALSE, self);
  951.         if (trace_fraction < highest) highest = trace_fraction;
  952.  
  953.         if (trace_fraction <= ledge_height)
  954.         {
  955.           // lower corner
  956.           org = above_org + (v_forward * i) - (v_right * 15);
  957.           end = org - (v_up * 1000);
  958.           traceline (org, end, FALSE, self);
  959.           if (trace_fraction < highest) highest = trace_fraction;
  960.  
  961.           if (trace_fraction <= ledge_height)
  962.           {
  963.             // center
  964.             org = above_org + (v_forward * (i + 15));
  965.             end = org - (v_up * 1000);
  966.             traceline (org, end, FALSE, self);
  967.             if (trace_fraction < highest) highest = trace_fraction;
  968.  
  969.             if (trace_fraction <= ledge_height)
  970.             {
  971.               // half way between center and left back corner
  972.               org = above_org + (v_forward * (i + 7.5)) - (v_right * 7.5);
  973.               end = org - (v_up * 1000);
  974.               traceline (org, end, FALSE, self);
  975.               if (trace_fraction < highest) highest = trace_fraction;
  976.  
  977.               if (trace_fraction <= ledge_height)
  978.               {
  979.                 // half way between center and right back corner
  980.                 org = above_org + (v_forward * (i + 7.5)) + (v_right * 7.5);
  981.                 end = org - (v_up * 1000);
  982.                 traceline (org, end, FALSE, self);
  983.                 if (trace_fraction < highest) highest = trace_fraction;
  984.  
  985.                 if (trace_fraction <= ledge_height) found_ledge = TRUE;
  986.               }
  987.             }
  988.           }
  989.         }
  990.       }
  991.     }
  992.     i = i + 30;
  993.   }
  994.  
  995.   if (found_ledge)
  996.   {
  997. //    bprint ("ledge ");
  998. //    bprint (ftos (i));
  999. //    bprint (", ");
  1000.  
  1001.     //FIXME: predict velocities based on server gravity
  1002.  
  1003.     highest = highest * 1000;
  1004.     // is ledge above Cujo's origin?
  1005.     if (highest < 48)
  1006.     {
  1007. //      bprint ("-above");
  1008.       upward_jump_vel = 270;
  1009.       forward_jump_vel = i * 1.5;
  1010.     }
  1011.     else
  1012.     {
  1013.       upward_jump_vel = 270;
  1014.       forward_jump_vel = i * 1.5;
  1015.       return i;
  1016.     }
  1017.   }
  1018.   else
  1019.     return 0;
  1020. };
  1021.  
  1022. /*╔═══════════════════════════════════════════════════════════════════════╗
  1023.   ║                                                                       ║
  1024.   ║ CUJO_JumpObstructed                                                   ║
  1025.   ║                                                                       ║
  1026.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  1027.  
  1028. float (float dist) CUJO_JumpObstructed =
  1029.  
  1030. {
  1031.   local   vector  org, end;
  1032.   local   float   pc;
  1033.  
  1034.   makevectors (self.angles);
  1035.   org = self.origin;
  1036.   end = self.origin + (v_forward * dist);
  1037.  
  1038.   traceline (org, end, FALSE, self);
  1039.   pc = pointcontents (trace_endpos);
  1040.  
  1041.   if ((trace_fraction < 1.0) || (pc == CONTENT_SOLID))
  1042.     return TRUE;
  1043.   else
  1044.     return FALSE;
  1045. };
  1046.  
  1047.  
  1048. /*╔═══════════════════════════════════════════════════════════════════════╗
  1049.   ║                                                                       ║
  1050.   ║ CUJO_JumpAI                                                           ║
  1051.   ║                                                                       ║
  1052.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  1053.  
  1054. void () CUJO_JumpAI =
  1055. {
  1056.   local   vector  dir, org, end;
  1057.   local   float   dist;
  1058.  
  1059.   if (!checkbottom (self) || (self.think == self.th_missile))
  1060.     return;
  1061.  
  1062.   org = self.origin;
  1063.   end = self.goalentity.origin;
  1064.  
  1065.   if (self.dmg_inflictor.classname == "fire")
  1066.   {
  1067.     dist = CUJO_FindWater ();
  1068.  
  1069.     if ((dist > 0) && (dist < 125) && (!CUJO_JumpObstructed (dist)))
  1070.     {
  1071.       // Cujo found water he can jump into
  1072.       // FIX: base velocities on server gravity
  1073.       upward_jump_vel = 230;
  1074.       forward_jump_vel = dist * 1.9;
  1075.       self.nextthink = time + 0.1;
  1076.       self.think = self.th_missile;
  1077.  
  1078.       sprint (self.owner, "Swim, Cujo! Swim!\n");
  1079. //      bprint ("\n");
  1080.       return TRUE;
  1081.     }
  1082.     bprint ("\n");
  1083.   }
  1084.   else if (org_z - end_z > 48)
  1085.   {
  1086. //    bprint ("JumpDown:");
  1087.     dist = CUJO_FindWater ();
  1088.  
  1089.     if (dist == 0)
  1090.       dist = CUJO_FindGap ();
  1091.  
  1092.     if ((dist) && (dist < 125) && (!CUJO_JumpObstructed (dist)))
  1093.     {
  1094.       // Cujo found water or a gap he can jump into
  1095.       upward_jump_vel = 230;
  1096.       forward_jump_vel = dist * 1.9;
  1097.       self.nextthink = time + 0.1;
  1098.       self.think = self.th_missile;
  1099.  
  1100. //      bprint ("\n");
  1101.       return TRUE;
  1102.     }
  1103. //    bprint ("\n");
  1104.   }
  1105.   else if ((org_z - end_z <= 48) && (self.ideal_yaw == anglemod (self.angles_y)))
  1106.   {
  1107. //    bprint ("JumpAcross:");
  1108.  
  1109.     // turn Cujo to _exactly_ the direction of his goal entity
  1110.     dir = self.goalentity.origin - self.origin;
  1111.     dir = normalize (dir);
  1112.     self.angles_y = vectoyaw (dir);
  1113.     self.ideal_yaw = vectoyaw (dir);
  1114.  
  1115.     dist = CUJO_FindWater ();
  1116.  
  1117.     if (dist <= 0)
  1118.       dist = CUJO_FindGap ();
  1119.  
  1120.     if ((dist > 0) && (dist < 150) && (vlen (self.goalentity.origin - self.origin) >= dist))
  1121.     {
  1122.       dist = CUJO_FindLedge (dist);
  1123.  
  1124. //      if ((!CUJO_JumpObstructed (dist)) && (dist > 0))
  1125.       if (dist > 0)
  1126.       {
  1127. //          bprint ("jump\n");
  1128.  
  1129.           // Cujo found a barrier he needs to jump
  1130.           self.nextthink = time + 0.1;
  1131.           self.think = self.th_missile;
  1132.  
  1133.           return TRUE;
  1134.       }
  1135.     }
  1136. //    bprint ("\n");
  1137.   }
  1138.  
  1139.   return FALSE;
  1140. };
  1141.  
  1142. /*╔═══════════════════════════════════════════════════════════════════════╗
  1143.   ║                                                                       ║
  1144.   ║ Cujo_checkattack                                                      ║
  1145.   ║                                                                       ║
  1146.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  1147.  
  1148. // this checks the attack against self.goalentity not self.enemy!
  1149.  
  1150. float()    CUJO_CheckAttack =
  1151. {
  1152.   local    vector    vec;
  1153.  
  1154.   if ((((self.goalentity == self.owner) || (self.goalentity.classname == "monster_zombie"))
  1155.       || (self.Cujo_attack == FALSE)) && (self.goalentity.classname != "dog_food"))
  1156.   {
  1157.     CUJO_ResetGoalEntity ();
  1158.  
  1159.     self.think = self.th_stand;
  1160.     self.nextthink = time + nextthinktime;
  1161.  
  1162.     return FALSE;
  1163.   }
  1164.  
  1165.   if (CUJO_CheckMelee ())
  1166.   {
  1167.     self.attack_state = AS_MELEE;
  1168.     return TRUE;
  1169.   }
  1170.  
  1171.   if (self.goalentity.classname != "dog_food")
  1172.   {
  1173.     if (CUJO_CheckJump ())
  1174.     {
  1175.       self.attack_state = AS_MISSILE;
  1176.       return TRUE;
  1177.     }
  1178.   }
  1179.  
  1180.   return FALSE;
  1181.  
  1182. };
  1183.  
  1184. /*╔═══════════════════════════════════════════════════════════════════════╗
  1185.   ║                                                                       ║
  1186.   ║ CUJO_ai_run_melee                                                     ║
  1187.   ║                                                                       ║
  1188.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  1189.  
  1190. void() CUJO_ai_run_melee =
  1191. {
  1192.   CUJO_ai_face ();
  1193.  
  1194.   if (FacingIdeal())
  1195.   {
  1196.     self.th_melee ();
  1197.     self.attack_state = AS_STRAIGHT;
  1198.   }
  1199. };
  1200.  
  1201. /*╔═══════════════════════════════════════════════════════════════════════╗
  1202.   ║                                                                       ║
  1203.   ║ CUJO_ai_run_missile                                                   ║
  1204.   ║                                                                       ║
  1205.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  1206.  
  1207. void() CUJO_ai_run_missile =
  1208. {
  1209.   CUJO_ai_face ();
  1210.  
  1211.   if (FacingIdeal())
  1212.   {
  1213.     upward_jump_vel = 270;
  1214.     forward_jump_vel = 360;
  1215.     self.th_missile ();
  1216.     self.attack_state = AS_STRAIGHT;
  1217.   }
  1218. };
  1219.  
  1220. /*╔═══════════════════════════════════════════════════════════════════════╗
  1221.   ║                                                                       ║
  1222.   ║ Cujo_ai_run                                                           ║
  1223.   ║                                                                       ║
  1224.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  1225.  
  1226. void(float dist) CUJO_ai_run =
  1227. {
  1228.   local    vector    delta;
  1229.   local    float    axis;
  1230.   local    float    direct, ang_rint, ang_floor, ang_ceil, f_temp;
  1231.   local string  str_temp;
  1232.  
  1233. //  sprint (self.owner, "-- enter ai_run --\n");
  1234.  
  1235.   movedist = dist;
  1236.  
  1237. //  sprint (self.owner, "Enemy is ");
  1238. //  sprint (self.owner, self.enemy.classname);
  1239. //  sprint (self.owner, "\n");
  1240. //  sprint (self.owner, "Goalentity is ");
  1241. //  sprint (self.owner, self.goalentity.classname);
  1242. //  sprint (self.owner, "\n");
  1243.  
  1244.   if ((self.goalentity.classname != "dog_food") && (self.goalentity.health <= 0))
  1245.   {
  1246.     CUJO_ResetGoalEntity ();
  1247.  
  1248.     //sprint (self.owner, "CUJO_ai_run: reset goalentity\n");
  1249.  
  1250.     if (self.oldenemy.health > 0)
  1251.     {
  1252.       self.enemy = self.oldenemy;
  1253.       self.goalentity = self.oldenemy;
  1254.  
  1255.       CUJO_HuntTarget ();
  1256.     }
  1257.     else
  1258.     {
  1259.       if (CUJO_FindTarget()) return;
  1260.       else
  1261.       {
  1262.         CUJO_ResetGoalEntity ();
  1263.  
  1264.         //sprint (self.owner, "CUJO_ai_run #2: reset goalentity\n");
  1265.  
  1266.         self.th_walk ();
  1267.  
  1268. //        sprint (self.owner, "-- Left ai_run at return 1 --\n");
  1269.         return;
  1270.       }
  1271.     }
  1272.  
  1273. //    sprint (self.owner, "-- Left ai_run at return 2 --\n");
  1274.     return;
  1275.   }
  1276.  
  1277.   if (self.goalentity.classname != "dog_food")
  1278.     self.show_hostile = time + 1;        // wake up other monsters
  1279.  
  1280.   goal_vis = visible(self.goalentity);
  1281.   if (goal_vis) self.search_time = time + 5;
  1282.  
  1283.   goal_infront = infront (self.goalentity);
  1284.   goal_range = range (self.goalentity);
  1285.   goal_yaw = vectoyaw (self.goalentity.origin - self.origin);
  1286.  
  1287.   if ((self.attack_state == AS_MISSILE) && (self.goalentity.classname != "dog_food"))
  1288.   {
  1289.     CUJO_ai_run_missile ();
  1290.     return;
  1291.   }
  1292.   if (self.attack_state == AS_MELEE)
  1293.   {
  1294.  
  1295. // for debugging
  1296. //    f_temp = vlen (self.origin - self.goalentity.origin);
  1297. //    str_temp = ftos (f_temp);
  1298.  
  1299. //    sprint (self.owner, "Cujo is ");
  1300. //    sprint (self.owner, str_temp);
  1301. //    sprint (self.owner, " units from ");
  1302. //    sprint (self.owner, self.goalentity.classname);
  1303. //    sprint (self.owner, ".\n");
  1304.  
  1305.     CUJO_ai_run_melee ();
  1306.  
  1307.     return;
  1308.   }
  1309.  
  1310.   if (CUJO_JumpAI ()) return;           // needs to jump to goal
  1311.   if (CUJO_CheckAttack ()) return;    // beginning an attack
  1312.  
  1313.   movetogoal (dist);        // done in C code...
  1314.  
  1315. //  sprint (self.owner, "-- exit ai_run --\n");
  1316. };
  1317.  
  1318. /*╔═══════════════════════════════════════════════════════════════════════╗
  1319.   ║                                                                       ║
  1320.   ║ Cujo_SightSound                                                       ║
  1321.   ║                                                                       ║
  1322.   ║ dog stands in place until target acquired or paustime is exceeded     ║
  1323.   ║                                                                       ║
  1324.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  1325.  
  1326. void() CUJO_SightSound =
  1327. {
  1328.   sound (self, CHAN_VOICE, "dog/dsight.wav", 1, ATTN_NORM);
  1329. };
  1330.  
  1331. /*╔═══════════════════════════════════════════════════════════════════════╗
  1332.   ║                                                                       ║
  1333.   ║ Cujo_HuntTarget                                                       ║
  1334.   ║                                                                       ║
  1335.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  1336.  
  1337. void() CUJO_HuntTarget =
  1338. {
  1339. // for debugging
  1340.   //sprint (self.owner, "CUJO_HuntTarget: ");
  1341.   //sprint (self.owner, self.goalentity.classname);
  1342.   //sprint (self.owner, "\n");
  1343.  
  1344.   self.think = self.th_run;
  1345.   self.ideal_yaw = vectoyaw(self.goalentity.origin - self.origin);
  1346.   self.nextthink = time + nextthinktime;
  1347.  
  1348.   SUB_AttackFinished (0.1);
  1349. };
  1350.  
  1351. /*╔═══════════════════════════════════════════════════════════════════════╗
  1352.   ║                                                                       ║
  1353.   ║ Cujo_FoundTarget                                                      ║
  1354.   ║                                                                       ║
  1355.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  1356.  
  1357. void() CUJO_FoundTarget =
  1358. {
  1359.   local float   f_dist;
  1360.   local string  s_dist;
  1361.   local float   r;
  1362.  
  1363.   f_dist = vlen (self.goalentity.origin - self.origin);
  1364.   s_dist = ftos (f_dist);
  1365.  
  1366.   self.show_hostile = time + 1;        // wake up other monsters
  1367.  
  1368.   r = random ();
  1369.  
  1370.   if (r < 0.5)
  1371.     sprint (self.owner, "rrrrRROOF!\n");
  1372.   else
  1373.     sprint (self.owner, "grrrrllll...\n");
  1374.  
  1375. // for debugging
  1376.   //sprint (self.owner, "Cujo seeking target");
  1377.   //sprint (self.owner, self.goalentity.classname);
  1378.   //sprint (self.owner, "\n");
  1379.  
  1380.   CUJO_SightSound ();
  1381.  
  1382.   CUJO_HuntTarget ();
  1383. };
  1384.  
  1385. /*╔═══════════════════════════════════════════════════════════════════════╗
  1386.   ║                                                                       ║
  1387.   ║ Cujo_FindTarget                                                       ║
  1388.   ║                                                                       ║
  1389.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  1390.  
  1391. float() CUJO_FindTarget =
  1392. {
  1393.   local entity   head, selected;
  1394.   local entity   food;
  1395.   local float    dist, lastd;
  1396.   local float    food_dist, food_lastd;
  1397.  
  1398.   // check for any enemies visible to Cujo
  1399.   dist = 600;
  1400.   food_dist = 600;
  1401.   selected = world;
  1402.   food = world;
  1403.  
  1404.   head = findradius(self.origin, dist);
  1405.  
  1406.   // CUJO 1.3 - trace_plane_dist was always equal to zero for some
  1407.   // unknown reason, so the code that used it was replaced
  1408.   while(head)
  1409.   {
  1410.     // search for food
  1411.     if ((head.classname == "dog_food") && (visible (head)))
  1412.     {
  1413.       if ((head != self) && (head != self.owner))
  1414.       {
  1415.         food_lastd = vlen (self.origin - head.origin);
  1416.         if (food_lastd < food_dist)
  1417.         // for efficiency's sake, go after the closest gib
  1418.         {
  1419.           food = head;
  1420.           food_dist = food_lastd;
  1421.         }
  1422.       }
  1423.     }
  1424.  
  1425.     // search for enemy
  1426.     if(!(head.flags & FL_NOTARGET) && ((head.flags & FL_CLIENT) || (head.flags & FL_MONSTER)))
  1427.       if (((teamplay) &&  (head.team != self.owner.team)) || (!teamplay))
  1428.         if ((head.health > 0) && (head != self) && (head != self.owner))
  1429.           if ((visible(head)) && (head.classname != "monster_zombie"))
  1430.           {
  1431.             lastd = vlen (self.origin - head.origin);
  1432.             if (lastd < dist)
  1433.             {
  1434.               selected = head;
  1435.               dist = lastd;
  1436.             }
  1437.           }
  1438.     head = head.chain;
  1439.   }
  1440.  
  1441.   self.enemy = selected;
  1442.  
  1443.   if ((food != world) && (food != self.owner) && (self.Cujo_attack)
  1444.     && ((self.health < 50) || ((deathmatch) && (self.health < 25))))
  1445.   {
  1446.     self.goalentity = food;
  1447.     self.enemy = food;
  1448.  
  1449.     CUJO_FoundTarget ();
  1450.  
  1451.     return TRUE;
  1452.   }
  1453.   else if ((self.enemy == world) || (self.enemy == self.owner))
  1454.   {
  1455.     if ((food != world) && (food != self.owner) && ((self.health < 191) &&
  1456.         (self.Cujo_attack)))
  1457.     {
  1458.       self.goalentity = food;
  1459.       self.enemy = food;
  1460.  
  1461.       CUJO_FoundTarget ();
  1462.  
  1463.       return TRUE;
  1464.     }
  1465.  
  1466.     return FALSE;
  1467.   }
  1468.   else  // an enemy target has been sighted
  1469.   {
  1470.     if (!(self.Cujo_attack) || (self.enemy.classname == "dog_food"))
  1471.     // if not supposed to attack, just growl
  1472.     {
  1473.       if (random () < 0.10)
  1474.       {
  1475.         // make Cujo bark or growl
  1476.         if (random () < 0.5)
  1477.           sound (self, CHAN_VOICE, "dog/dsight.wav", 1, ATTN_NORM);
  1478.         else
  1479.           sound (self, CHAN_VOICE, "dog/dattack1.wav", 1, ATTN_NORM);
  1480.       }
  1481.  
  1482.       CUJO_ResetGoalEntity ();
  1483.  
  1484.       return FALSE;
  1485.     }
  1486.  
  1487.     // if Cujo is staying, make him attack!
  1488.     self.Cujo_stay = FALSE;
  1489.  
  1490.     self.goalentity = self.enemy;
  1491.  
  1492.     CUJO_FoundTarget();
  1493.  
  1494.     return TRUE;
  1495.   }
  1496. };
  1497.  
  1498. /*╔═══════════════════════════════════════════════════════════════════════╗
  1499.   ║                                                                       ║
  1500.   ║ CUJO_ai_face                                                          ║
  1501.   ║                                                                       ║
  1502.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  1503.  
  1504. void() CUJO_ai_face =
  1505. {
  1506.   self.ideal_yaw = vectoyaw (self.goalentity.origin - self.origin);
  1507.   CUJO_ChangeYaw ();
  1508. };
  1509.  
  1510. /*╔═══════════════════════════════════════════════════════════════════════╗
  1511.   ║                                                                       ║
  1512.   ║ CUJO_ai_charge                                                        ║
  1513.   ║                                                                       ║
  1514.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  1515.  
  1516. void (float d) CUJO_ai_charge =
  1517. {
  1518.   CUJO_ai_face ();
  1519.  
  1520.   movetogoal (d);        // done in C code...
  1521. };
  1522.  
  1523. /*╔═══════════════════════════════════════════════════════════════════════╗
  1524.   ║                                                                       ║
  1525.   ║ Cujo_ai_stand                                                         ║
  1526.   ║                                                                       ║
  1527.   ║ dog stands in place until target acquired or paustime is exceeded     ║
  1528.   ║                                                                       ║
  1529.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  1530.  
  1531. void() CUJO_ai_stand =
  1532. {
  1533.   if (CUJO_FindTarget ()) return;
  1534.  
  1535.   if (vlen (self.origin - self.owner.origin) > teleport_dist)
  1536.   {
  1537.     CUJO_TeleportToOwner ();
  1538.   }
  1539.   else if (vlen (self.origin - self.owner.origin) > 100)
  1540.   {
  1541.     // if Cujo is staying, then he shouldn't run in place!
  1542.     if (self.Cujo_stay == TRUE) return;
  1543.  
  1544.     CUJO_follow1 ();
  1545.  
  1546.     return;
  1547.   }
  1548.  
  1549.   if (CUJO_JumpAI ()) return;
  1550. };
  1551.  
  1552. /*╔═══════════════════════════════════════════════════════════════════════╗
  1553.   ║                                                                       ║
  1554.   ║ Cujo_ai_walk                                                          ║
  1555.   ║                                                                       ║
  1556.   ║ Cujo is walking and searching for targets                             ║
  1557.   ║                                                                       ║
  1558.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  1559.  
  1560. void(float dist) CUJO_ai_walk =
  1561. {
  1562.   if (CUJO_FindTarget ()) return;
  1563.  
  1564.   if (vlen (self.origin - self.owner.origin) > 100)
  1565.   {
  1566.     CUJO_follow1();
  1567.  
  1568.     return;
  1569.   }
  1570.   else if (vlen (self.origin - self.owner.origin) > teleport_dist)
  1571.   {
  1572.     CUJO_TeleportToOwner ();
  1573.   }
  1574.  
  1575.   if (CUJO_JumpAI ()) return;
  1576.  
  1577.   movetogoal (dist);  // this is done in C code
  1578. };
  1579.  
  1580. /*╔═══════════════════════════════════════════════════════════════════════╗
  1581.   ║                                                                       ║
  1582.   ║ Cujo_ai_follow                                                        ║
  1583.   ║                                                                       ║
  1584.   ║ Cujo is following player and searching for targets                    ║
  1585.   ║                                                                       ║
  1586.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  1587.  
  1588. void(float dist) CUJO_ai_follow =
  1589. {
  1590.   if (CUJO_FindTarget ()) return;
  1591.  
  1592.   //sprint (self.owner, "Following owner.\n");
  1593.   //sprint (self.owner, "Goalentity is ");
  1594.   //sprint (self.owner, self.goalentity.classname);
  1595.   //sprint (self.owner, "\n");
  1596.   //sprint (self.owner, "Enemy is ");
  1597.   //sprint (self.owner, self.enemy.classname);
  1598.   //sprint (self.owner, "\n");
  1599.   //sprint (self.owner, "Movetarget is ");
  1600.   //sprint (self.owner, self.movetarget.classname);
  1601.   //sprint (self.owner, "\n");
  1602.  
  1603.   // if Cujo is in STAY mode then do not follow the player!
  1604.  
  1605.   if ((vlen (self.origin - self.owner.origin) <= 100) || (self.Cujo_stay))
  1606.   {
  1607.     self.pausetime = time + 2;
  1608.  
  1609.     self.th_stand ();
  1610.  
  1611.     return;
  1612.   }
  1613.   else if ((vlen (self.origin - self.owner.origin) > teleport_dist) &&
  1614.           (!intermission_running))
  1615.   {
  1616.     CUJO_TeleportToOwner ();
  1617.   }
  1618.  
  1619.   if (CUJO_JumpAI ()) return;
  1620.  
  1621.   movetogoal (dist);  // done in C code
  1622. };
  1623.  
  1624. /*╔═══════════════════════════════════════════════════════════════════════╗
  1625.   ║                                                                       ║
  1626.   ║ Cujo_ai_turn                                                          ║
  1627.   ║                                                                       ║
  1628.   ║ Turn Cujo towards ideal_yaw if no enemies were sighted                ║
  1629.   ║                                                                       ║
  1630.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  1631.  
  1632. void() CUJO_ai_turn =
  1633. {
  1634.   if (CUJO_FindTarget ()) return;
  1635.  
  1636.   CUJO_ChangeYaw ();
  1637. };
  1638.  
  1639. /*╔═══════════════════════════════════════════════════════════════════════╗
  1640.   ║                                                                       ║
  1641.   ║ Cujo_CheckRefire                                                      ║
  1642.   ║                                                                       ║
  1643.   ║ Determine if the enemy is still visible for attack                    ║
  1644.   ║                                                                       ║
  1645.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  1646.  
  1647. void (void () thinkst) CUJO_CheckRefire =
  1648. {
  1649.   if (!visible (self.goalentity) || (self.goalentity.health <= 0))
  1650.   {
  1651.     CUJO_ResetGoalEntity ();
  1652.  
  1653.     self.think = self.th_stand;
  1654.     self.nextthink = time + nextthinktime;
  1655.  
  1656.     return;
  1657.   }
  1658.  
  1659. //  self.think = thinkst;  // was thinkst
  1660. };
  1661.  
  1662. /*╔═══════════════════════════════════════════════════════════════════════╗
  1663.   ║                                                                       ║
  1664.   ║ CUJO_CorpsePain                                                       ║
  1665.   ║                                                                       ║
  1666.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  1667.  
  1668. void () CUJO_CorpsePain =
  1669. {
  1670.  SpawnMeatSpray (self.origin, crandom() * 100 * v_right);
  1671. };
  1672.  
  1673. /*╔═══════════════════════════════════════════════════════════════════════╗
  1674.   ║                                                                       ║
  1675.   ║ CUJO_CorpseDie;                                                       ║
  1676.   ║                                                                       ║
  1677.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  1678.  
  1679. void () CUJO_CorpseDie =
  1680. {
  1681.   local   float   i;
  1682.   local   float   j;
  1683.  
  1684.   sound (self, CHAN_VOICE, "player/udeath.wav", 1, ATTN_NORM);
  1685.  
  1686.   bprint ("gibbed ");
  1687.   bprint (self.classname);
  1688.   bprint ("\n");
  1689.  
  1690.   // throw the head -- can be a player head or a cujo head only in this version!
  1691.   ThrowHead (self.weaponmodel, self.health);
  1692.  
  1693.   i = 0;
  1694.  
  1695.   while(i<3)
  1696.   {
  1697.     j = random();
  1698.  
  1699.     if(j > 0.6)
  1700.       ThrowGib ("progs/gib1.mdl", self.health);
  1701.     else if(j > 0.3)
  1702.       ThrowGib ("progs/gib2.mdl", self.health);
  1703.     else
  1704.       ThrowGib ("progs/gib3.mdl", self.health);
  1705.  
  1706.     i = i + 1;
  1707.   }
  1708.  
  1709. // Cujo's head only stays around for 2 minutes...
  1710. //  self.nextthink = time + 120;
  1711. //  self.think = SUB_Remove;
  1712. };
  1713.  
  1714. /*╔═══════════════════════════════════════════════════════════════════════╗
  1715.   ║                                                                       ║
  1716.   ║ Cujo_SelfDeactivate                                                   ║
  1717.   ║                                                                       ║
  1718.   ║ Cujo deactivates himself (when he is killed, basically)               ║
  1719.   ║                                                                       ║
  1720.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  1721.  
  1722. void () CUJO_SelfDeactivate =
  1723. {
  1724.   local   entity  temp_self;
  1725.  
  1726.   temp_self = self;
  1727.   self = self.owner;
  1728.  
  1729.   if (self.Cujo_view)
  1730.     CUJO_SetPlayerView ();
  1731.  
  1732.   self = temp_self;
  1733.  
  1734.   // create a dummy Cujo body... this alleviates the problem of the
  1735.   // original Cujo entity becoming lost if the player reactivates Cujo
  1736.   // before the body has disappeared
  1737.  
  1738. //  self.classname = "cujo_body";
  1739.  
  1740.   // create a body in the que which is solid
  1741.   self.weaponmodel = "progs/h_cujo.mdl";
  1742.   CopyToBodyQue (self);
  1743.  
  1744.   if (self.enemy == self.owner)
  1745.   {
  1746.     if (self.health < -35)
  1747.     {
  1748.       centerprint (self.owner, "You just turned Cujo into puppy chow.\n");
  1749.     }
  1750.     else
  1751.     {
  1752.       centerprint (self.owner, "You buried your dog.\n");
  1753.     }
  1754.   }
  1755.   else
  1756.   {
  1757.     if (self.health < -35)
  1758.     {
  1759.       centerprint (self.owner, "Cujo is kibbles and bits.\n");
  1760.     }
  1761.     else
  1762.     {
  1763.       centerprint (self.owner, "Cujo went to dog heaven.\n");
  1764.     }
  1765.   }
  1766.  
  1767.   self.owner.Cujo_flag = FALSE;
  1768.   remove (self);
  1769. };
  1770.  
  1771. /*╔═══════════════════════════════════════════════════════════════════════╗
  1772.   ║                                                                       ║
  1773.   ║ Cujo_TeleportPos                                                      ║
  1774.   ║                                                                       ║
  1775.   ║ Determines the best position for Cujo to spawn in                     ║
  1776.   ║ This procedure is to be called from Cujo's routines, not the player's ║
  1777.   ║                                                                       ║
  1778.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  1779.  
  1780. vector () CUJO_TeleportPos =
  1781. {
  1782.   local vector  org, temp, right, front;
  1783.  
  1784.   org = self.owner.origin;
  1785.  
  1786.   makevectors (self.owner.angles);
  1787.  
  1788.   // make front equal 50 units in front of the player
  1789.   front = 50 * normalize (v_forward);
  1790.   // and right equal fifty units to the right
  1791.   right = 50 * normalize (v_right);
  1792.  
  1793.  
  1794.   // right of player
  1795.  
  1796.   temp = org + right;
  1797.   if (CUJO_CheckSpawnPos (temp)) return temp;
  1798.  
  1799.   // left of player
  1800.  
  1801.   temp = org - right;
  1802.   if (CUJO_CheckSpawnPos (temp)) return temp;
  1803.  
  1804.   // in front of player
  1805.  
  1806.   temp = org + front;
  1807.   if (CUJO_CheckSpawnPos (temp)) return temp;
  1808.  
  1809.   // behind player
  1810.  
  1811.   temp = org - front;
  1812.   if (CUJO_CheckSpawnPos (temp)) return temp;
  1813.  
  1814. };
  1815.  
  1816.  
  1817. /*╔═══════════════════════════════════════════════════════════════════════╗
  1818.   ║                                                                       ║
  1819.   ║ Cujo_TeleportToOwner                                                  ║
  1820.   ║                                                                       ║
  1821.   ║ Cujo teleports to his owner, the player                               ║
  1822.   ║                                                                       ║
  1823.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  1824.  
  1825. void () CUJO_TeleportToOwner =
  1826. {
  1827.   local vector org;
  1828.  
  1829.   // exit if in intermission
  1830.   if (intermission_running) return;
  1831.  
  1832.   // Check to see if there is room to teleport Cujo here, if not, say so and
  1833.   // exit.
  1834.   org = CUJO_TeleportPos ();
  1835.   if (org == '0 0 0')
  1836.     return;
  1837.  
  1838.   sprint (self.owner, "Whoof!\n");
  1839.   sound (self, CHAN_BODY, "dog/dsight.wav", 1, ATTN_NORM);
  1840.  
  1841.   spawn_tfog (self.origin);
  1842.  
  1843.   self.ideal_yaw = self.angles * '0 1 0';
  1844.   self.pausetime = time + 5;
  1845.   self.nextthink = time + nextthinktime;
  1846.   self.think = self.Cujo.th_stand;
  1847.  
  1848.   setorigin(self, org);
  1849.  
  1850.   spawn_tfog (self.origin);
  1851.   self.nextthink = time + nextthinktime;
  1852.   self.think = self.th_stand;
  1853.  
  1854.   return;
  1855. };
  1856.  
  1857. /*╔═══════════════════════════════════════════════════════════════════════╗
  1858.   ║                                                                       ║
  1859.   ║                 Routines called by player, self=player                ║
  1860.   ║                                                                       ║
  1861.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  1862.  
  1863. /*╔═══════════════════════════════════════════════════════════════════════╗
  1864.   ║                                                                       ║
  1865.   ║ Cujo_Precache                                                         ║
  1866.   ║                                                                       ║
  1867.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  1868.  
  1869. void () CUJO_Precache =
  1870. {
  1871.   // Precache Cujo's sounds
  1872.   precache_model2 ("progs/h_cujo.mdl");
  1873.   precache_model2 ("progs/cujo.mdl");
  1874.  
  1875.   precache_sound2 ("dog/dattack1.wav");
  1876.   precache_sound2 ("dog/ddeath.wav");
  1877.   precache_sound2 ("dog/dpain1.wav");
  1878.   precache_sound2 ("dog/dsight.wav");
  1879.   precache_sound2 ("dog/idle.wav");
  1880. };
  1881.  
  1882. /*╔═══════════════════════════════════════════════════════════════════════╗
  1883.   ║                                                                       ║
  1884.   ║ Cujo_CheckSpawnPos                                                    ║
  1885.   ║                                                                       ║
  1886.   ║ Returns true if Cujo's bounding box area around SpawnPos is free of   ║
  1887.   ║ solid objects                                                         ║
  1888.   ║                                                                       ║
  1889.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  1890.  
  1891. float (vector SpawnPos) CUJO_CheckSpawnPos =
  1892. {
  1893.   local  vector  XVector, YVector, ZPosVec, ZNegVec;
  1894.   local  vector  HalfYVec, HalfXVec, HalfZPosVec, HalfZNegVec;
  1895.   local  float   pc;
  1896.  
  1897.   // These vector offsets match Cujo's current size, but do not match
  1898.   // the exact size of the Dog model (which was too big to follow the
  1899.   // player some places), so the dog may at times look like his head
  1900.   // is in a wall, but he shouldn't get stuck (we hope).
  1901.  
  1902.   XVector = '16 0 0';
  1903.   HalfXVec = '8 0 0';
  1904.  
  1905.   YVector = '0 16 0';
  1906.   HalfYVec = '0 16 0';
  1907.  
  1908.   ZPosVec = '0 0 16';
  1909.   HalfZPosVec = '0 0 8';
  1910.   ZNegVec = '0 0 -24';
  1911.   HalfZNegVec = '0 0 -12';
  1912.  
  1913.   // check the very center of the box, just in case something is floating
  1914.   // there (for example, the moving "key cubes" at the end of e1m2)
  1915.  
  1916.   pc = pointcontents (SpawnPos);
  1917.   if (pc != CONTENT_EMPTY && pc != CONTENT_WATER) return FALSE;
  1918.  
  1919.   // check half the distance to each face of the box.  Again, this is
  1920.   // for floating or thin objects which might fit in between the face
  1921.   // edge and the center of the face
  1922.  
  1923.   pc = pointcontents (SpawnPos + HalfXVec);
  1924.   if (pc != CONTENT_EMPTY && pc != CONTENT_WATER) return FALSE;
  1925.  
  1926.   pc = pointcontents (SpawnPos - HalfXVec);
  1927.   if (pc != CONTENT_EMPTY && pc != CONTENT_WATER) return FALSE;
  1928.  
  1929.   pc = pointcontents (SpawnPos + HalfYVec);
  1930.   if (pc != CONTENT_EMPTY && pc != CONTENT_WATER) return FALSE;
  1931.  
  1932.   pc = pointcontents (SpawnPos - HalfYVec);
  1933.   if (pc != CONTENT_EMPTY && pc != CONTENT_WATER) return FALSE;
  1934.  
  1935.   pc = pointcontents (SpawnPos + HalfZPosVec);
  1936.   if (pc != CONTENT_EMPTY && pc != CONTENT_WATER) return FALSE;
  1937.  
  1938.   pc = pointcontents (SpawnPos + HalfZNegVec);
  1939.   if (pc != CONTENT_EMPTY && pc != CONTENT_WATER) return FALSE;
  1940.  
  1941.  
  1942.   // this checks the six faces of each side of Cujo's bounding box
  1943.  
  1944.   pc = pointcontents (SpawnPos + XVector);
  1945.   if (pc != CONTENT_EMPTY && pc != CONTENT_WATER) return FALSE;
  1946.  
  1947.   pc = pointcontents (SpawnPos - XVector);
  1948.   if (pc != CONTENT_EMPTY && pc != CONTENT_WATER) return FALSE;
  1949.  
  1950.   pc = pointcontents (SpawnPos + YVector);
  1951.   if (pc != CONTENT_EMPTY && pc != CONTENT_WATER) return FALSE;
  1952.  
  1953.   pc = pointcontents (SpawnPos - YVector);
  1954.   if (pc != CONTENT_EMPTY && pc != CONTENT_WATER) return FALSE;
  1955.  
  1956.   pc = pointcontents (SpawnPos + ZPosVec);
  1957.   if (pc != CONTENT_EMPTY && pc != CONTENT_WATER) return FALSE;
  1958.  
  1959.   pc = pointcontents (SpawnPos + ZNegVec);
  1960.   if (pc != CONTENT_EMPTY && pc != CONTENT_WATER) return FALSE;
  1961.  
  1962.  
  1963.   // Check the eight vertices of Cujo's bounding box
  1964.  
  1965.   pc = pointcontents (SpawnPos + XVector + YVector + ZPosVec);
  1966.   if (pc != CONTENT_EMPTY && pc != CONTENT_WATER) return FALSE;
  1967.  
  1968.   pc = pointcontents (SpawnPos + XVector + YVector + ZNegVec);
  1969.   if (pc != CONTENT_EMPTY && pc != CONTENT_WATER) return FALSE;
  1970.  
  1971.   pc = pointcontents (SpawnPos + XVector - YVector + ZPosVec);
  1972.   if (pc != CONTENT_EMPTY && pc != CONTENT_WATER) return FALSE;
  1973.  
  1974.   pc = pointcontents (SpawnPos + XVector - YVector + ZNegVec);
  1975.   if (pc != CONTENT_EMPTY && pc != CONTENT_WATER) return FALSE;
  1976.  
  1977.   pc = pointcontents (SpawnPos - XVector + YVector + ZPosVec);
  1978.   if (pc != CONTENT_EMPTY && pc != CONTENT_WATER) return FALSE;
  1979.  
  1980.   pc = pointcontents (SpawnPos - XVector + YVector + ZNegVec);
  1981.   if (pc != CONTENT_EMPTY && pc != CONTENT_WATER) return FALSE;
  1982.  
  1983.   pc = pointcontents (SpawnPos - XVector - YVector + ZPosVec);
  1984.   if (pc != CONTENT_EMPTY && pc != CONTENT_WATER) return FALSE;
  1985.  
  1986.   pc = pointcontents (SpawnPos - XVector - YVector + ZNegVec);
  1987.   if (pc != CONTENT_EMPTY && pc != CONTENT_WATER) return FALSE;
  1988.  
  1989.  
  1990.   // check the center of each edge of the box for content type
  1991.   // first check the center of the top 4 edges of the bounding box
  1992.  
  1993.   pc = pointcontents (SpawnPos + (YVector + ZPosVec));
  1994.   if (pc != CONTENT_EMPTY && pc != CONTENT_WATER) return FALSE;
  1995.  
  1996.   pc = pointcontents (SpawnPos - (YVector + ZPosVec));
  1997.   if (pc != CONTENT_EMPTY && pc != CONTENT_WATER) return FALSE;
  1998.  
  1999.   pc = pointcontents (SpawnPos + (XVector + ZPosVec));
  2000.   if (pc != CONTENT_EMPTY && pc != CONTENT_WATER) return FALSE;
  2001.  
  2002.   pc = pointcontents (SpawnPos - (XVector + ZPosVec));
  2003.   if (pc != CONTENT_EMPTY && pc != CONTENT_WATER) return FALSE;
  2004.  
  2005.   // now check the center of the middle 4 edges of the bounding box
  2006.  
  2007.   pc = pointcontents (SpawnPos - (XVector + YVector));
  2008.   if (pc != CONTENT_EMPTY && pc != CONTENT_WATER) return FALSE;
  2009.  
  2010.   pc = pointcontents (SpawnPos - (XVector - YVector));
  2011.   if (pc != CONTENT_EMPTY && pc != CONTENT_WATER) return FALSE;
  2012.  
  2013.   pc = pointcontents (SpawnPos + (XVector + YVector));
  2014.   if (pc != CONTENT_EMPTY && pc != CONTENT_WATER) return FALSE;
  2015.  
  2016.   pc = pointcontents (SpawnPos + (XVector - YVector));
  2017.   if (pc != CONTENT_EMPTY && pc != CONTENT_WATER) return FALSE;
  2018.  
  2019.   // now check the center of the bottom 4 edges of the bounding box
  2020.  
  2021.   pc = pointcontents (SpawnPos + (YVector + ZNegVec));
  2022.   if (pc != CONTENT_EMPTY && pc != CONTENT_WATER) return FALSE;
  2023.  
  2024.   pc = pointcontents (SpawnPos - (YVector + ZNegVec));
  2025.   if (pc != CONTENT_EMPTY && pc != CONTENT_WATER) return FALSE;
  2026.  
  2027.   pc = pointcontents (SpawnPos + (XVector + ZNegVec));
  2028.   if (pc != CONTENT_EMPTY && pc != CONTENT_WATER) return FALSE;
  2029.  
  2030.   pc = pointcontents (SpawnPos - (XVector + ZNegVec));
  2031.   if (pc != CONTENT_EMPTY && pc != CONTENT_WATER) return FALSE;
  2032.  
  2033.   // if that doesn't check enough spots, then that's just too bad.
  2034.  
  2035.   return TRUE;
  2036. };
  2037.  
  2038. /*╔═══════════════════════════════════════════════════════════════════════╗
  2039.   ║                                                                       ║
  2040.   ║ Cujo_SpawnPos                                                         ║
  2041.   ║                                                                       ║
  2042.   ║ Determines the best position for Cujo to spawn in                     ║
  2043.   ║                                                                       ║
  2044.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  2045.  
  2046. vector () CUJO_SpawnPos =
  2047. {
  2048.   local vector  org, temp, right, front;
  2049.  
  2050.   org = self.origin;
  2051.  
  2052.   makevectors (self.angles);
  2053.  
  2054.   // make front equal 50 units in front of the player
  2055.   front = 50 * normalize (v_forward);
  2056.   // and right equal fifty units to the right
  2057.   right = 50 * normalize (v_right);
  2058.  
  2059.  
  2060.   // right of player
  2061.  
  2062.   temp = org + right;
  2063.   if (CUJO_CheckSpawnPos (temp)) return temp;
  2064.  
  2065.   // left of player
  2066.  
  2067.   temp = org - right;
  2068.   if (CUJO_CheckSpawnPos (temp)) return temp;
  2069.  
  2070.   // in front of player
  2071.  
  2072.   temp = org + front;
  2073.   if (CUJO_CheckSpawnPos (temp)) return temp;
  2074.  
  2075.   // behind player
  2076.  
  2077.   temp = org - front;
  2078.   if (CUJO_CheckSpawnPos (temp)) return temp;
  2079.  
  2080. };
  2081.  
  2082.  
  2083. /*╔═══════════════════════════════════════════════════════════════════════╗
  2084.   ║                                                                       ║
  2085.   ║ Cujo_Activate                                                         ║
  2086.   ║                                                                       ║
  2087.   ║ Called by player, self = player                                       ║
  2088.   ║                                                                       ║
  2089.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  2090.  
  2091. void () CUJO_Activate =
  2092. {
  2093.   local entity  dogbot;
  2094.   local vector  org;
  2095.   local float   bit;
  2096.  
  2097.   // Check to see if there is room to spawn Cujo here, if not, say so and
  2098.   // exit.
  2099.  
  2100.   org = CUJO_SpawnPos ();
  2101.   if (org == '0 0 0')
  2102.   {
  2103.     centerprint (self, "There is no room for Cujo here.\n");
  2104.     return;
  2105.   }
  2106.  
  2107.   // Spawn Cujo
  2108.  
  2109.   dogbot = spawn();
  2110.   dogbot.solid = SOLID_BBOX;
  2111.   dogbot.movetype = MOVETYPE_STEP;
  2112.  
  2113.   dogbot.angles = self.angles;
  2114.   dogbot.classname = "cujo";
  2115.   dogbot.owner=self;
  2116.   self.Cujo=dogbot;
  2117.   self.Cujo_flag = TRUE;
  2118.   self.Cujo_auto = TRUE;
  2119.   self.Cujo_view = FALSE;
  2120.   self.Cujo.Cujo_attack = TRUE;
  2121.   self.Cujo.Cujo_stay = FALSE;
  2122.  
  2123.   dogbot.takedamage = DAMAGE_AIM;
  2124.   dogbot.goalentity = self;
  2125.   dogbot.movetarget = self;
  2126.   dogbot.pausetime = time + 5;
  2127.   dogbot.ideal_yaw = dogbot.angles * '0 1 0';
  2128.   dogbot.yaw_speed = 20; // was 30 for a normal dog, looks really odd from Cujo's view
  2129.  
  2130.   // lowered Cujo's view offset to 15 above the center of it's bounding box
  2131.   // (I have no idea if this is exactly right...)
  2132.  
  2133.   dogbot.view_ofs = '0 0 15';
  2134.  
  2135.   // this dog has some badass armor
  2136.  
  2137.   dogbot.health = 200;
  2138.   bit = IT_ARMOR3;
  2139.   dogbot.armortype = 0.8;
  2140.   dogbot.armorvalue = 200;
  2141.   dogbot.items = other.items - (other.items & (IT_ARMOR1 | IT_ARMOR2 | IT_ARMOR3)) + bit;
  2142.  
  2143.   // Set to automatic mode sequences at startup
  2144.  
  2145.   dogbot.th_stand = CUJO_stand1;
  2146.   dogbot.th_walk = CUJO_walk1;
  2147.   dogbot.th_run = CUJO_run1;
  2148.   dogbot.th_pain = CUJO_pain;
  2149.   dogbot.th_die = CUJO_die;
  2150.   dogbot.th_melee = CUJO_atta1;
  2151.   dogbot.th_missile = CUJO_leap1;
  2152.   dogbot.team = self.team;
  2153.   dogbot.deadflag = DEAD_NO;
  2154.  
  2155.   setmodel (dogbot, "progs/cujo.mdl");
  2156.  
  2157.   // Cujo's size was made the same as the player, (except for his height)
  2158.   // because he was having trouble getting though some narrow doorways
  2159.   // and halls.
  2160.  
  2161.   setsize (dogbot, '-16 -16 -24', '16 16 16');
  2162.   setorigin(dogbot, org);
  2163.  
  2164.   spawn_tfog (dogbot.origin);
  2165.  
  2166.   sound (self, CHAN_BODY, "dog/dattack1.wav", 1, ATTN_NORM);
  2167.   centerprint(self, "Cujo is here.\n");
  2168.  
  2169.   dogbot.nextthink = time + nextthinktime;
  2170.   dogbot.think = dogbot.th_stand;
  2171.  
  2172.   // remove powerup status from player
  2173.   self.Cujo_avail = 0;
  2174.  
  2175.   // used to determine if Cujo's think is a jumping frame
  2176. };
  2177.  
  2178. /*╔═══════════════════════════════════════════════════════════════════════╗
  2179.   ║                                                                       ║
  2180.   ║ Cujo_Deactivate                                                       ║
  2181.   ║                                                                       ║
  2182.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  2183.  
  2184. void () CUJO_Deactivate =
  2185. {
  2186.   spawn_tfog (self.Cujo.origin);
  2187.  
  2188.   if (self.Cujo_view)
  2189.     CUJO_SetPlayerView ();
  2190.  
  2191.   // prevents fire from continuing on the next cujo spawned if he
  2192.   // was burning but not dead when he was deactivated
  2193.   blaze_extinguishentity (self.Cujo);
  2194.  
  2195.   self.Cujo.nextthink = time + nextthinktime;
  2196.   self.Cujo.think = SUB_Remove;
  2197.   self.Cujo_flag = FALSE;
  2198.  
  2199.   centerprint (self, "Cujo went back to his doghouse.\n");
  2200. };
  2201.  
  2202. /*╔═══════════════════════════════════════════════════════════════════════╗
  2203.   ║                                                                       ║
  2204.   ║ Cujo_Toggle                                                           ║
  2205.   ║                                                                       ║
  2206.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  2207.  
  2208. // ver 1.2 -  Changed spawning rule for deathmatch
  2209. // ver 1.2 -  uhhh... fixed spawning rules for deathmatch :)
  2210.  
  2211. void () CUJO_Toggle =
  2212. {
  2213.   if ((self.Cujo_flag) && (self.Cujo.deadflag == DEAD_DYING)) return;
  2214.  
  2215.   if (deathmatch)
  2216.   {
  2217.     if (self.Cujo_flag)
  2218.     {
  2219.       if ((self.deadflag == DEAD_DYING) || (self.deadflag == DEAD_DEAD))
  2220.       {
  2221.         CUJO_Deactivate ();
  2222.       }
  2223.       else
  2224.         sprint (self, "Cujo can't be sent away in deathmatch.\n");
  2225.     }
  2226.     else if (self.Cujo_avail)
  2227.     {
  2228.       CUJO_Activate ();
  2229.     }
  2230.     else
  2231.       sprint (self, "Cujo is not available.\n");
  2232.   }
  2233.   else
  2234.   {
  2235.     if (!(self.Cujo_flag))
  2236.       CUJO_Activate();
  2237.     else
  2238.       CUJO_Deactivate();
  2239.   }
  2240. };
  2241.  
  2242. /*╔═══════════════════════════════════════════════════════════════════════╗
  2243.   ║                                                                       ║
  2244.   ║ Cujo_AttackToggle                                                     ║
  2245.   ║                                                                       ║
  2246.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  2247.  
  2248. void () CUJO_AttackToggle =
  2249. {
  2250.   if (!(self.Cujo_flag))
  2251.   {
  2252.     CUJO_PrintCujoStatus ();
  2253.  
  2254.     return;
  2255.   }
  2256.  
  2257.   if (self.Cujo.Cujo_attack == TRUE)
  2258.   {
  2259.     self.Cujo.Cujo_attack = FALSE;
  2260.     sprint (self, "Cujo will not attack.\n");
  2261.  
  2262.     // reset Cujo's enemy so he doesn't whine even when the enemy goes
  2263.     // out of view.
  2264.  
  2265.     self.Cujo.enemy = world;
  2266.     self.Cujo.oldenemy = world;
  2267.     self.Cujo.goalentity = self;
  2268.     self.Cujo.movetarget = self;
  2269.  
  2270.     self.Cujo.think = self.Cujo.th_stand;
  2271.     self.Cujo.nextthink = time + nextthinktime;
  2272.   }
  2273.   else
  2274.   {
  2275.     self.Cujo.Cujo_attack = TRUE;
  2276.     sprint (self, "Cujo wants blood!\n");
  2277.   }
  2278.  
  2279.   return;
  2280. };
  2281.  
  2282. /*╔═══════════════════════════════════════════════════════════════════════╗
  2283.   ║                                                                       ║
  2284.   ║ Cujo_Attack                                                           ║
  2285.   ║                                                                       ║
  2286.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  2287. /*
  2288. void () CUJO_Attack =
  2289. {
  2290.   local entity oldself;
  2291.   local vector org;
  2292.  
  2293.   if (!(self.Cujo_flag))
  2294.   {
  2295.     CUJO_PrintCujoStatus ();
  2296.  
  2297.     return;
  2298.   }
  2299.  
  2300.   oldself = self;
  2301.   self = self.Cujo;
  2302.  
  2303.   if (self.owner.Cujo_auto && self.enemy != world && self.enemy.health > 1
  2304.   && !(self.enemy.items & IT_INVISIBILITY) && visible(self.enemy))
  2305.   {
  2306.   }
  2307.   else
  2308.   {
  2309.   }
  2310.  
  2311.   self = oldself;
  2312.  
  2313.   return;
  2314. };
  2315. */
  2316. /*╔═══════════════════════════════════════════════════════════════════════╗
  2317.   ║                                                                       ║
  2318.   ║ Cujo_TeleportHome                                                     ║
  2319.   ║                                                                       ║
  2320.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  2321.  
  2322. void () CUJO_TeleportHome =
  2323. {
  2324.   local vector org;
  2325.  
  2326.   if (!(self.Cujo_flag))
  2327.   {
  2328.     CUJO_PrintCujoStatus ();
  2329.  
  2330.     return;
  2331.   }
  2332.   if (self.Cujo_flag)
  2333.     if (self.Cujo.deadflag == DEAD_DYING) return;
  2334.  
  2335.   spawn_tfog (self.Cujo.origin);
  2336.   self.Cujo.ideal_yaw = self.angles * '0 1 0';
  2337.   self.Cujo.pausetime = time + 2;
  2338.   self.Cujo.nextthink = time + nextthinktime;
  2339.   self.Cujo.think = self.Cujo.th_stand;
  2340.  
  2341.   org = self.origin + '0 0 0';
  2342.   setorigin(self.Cujo, org);
  2343.  
  2344.   spawn_tfog (self.Cujo.origin);
  2345.   self.Cujo.nextthink = time + nextthinktime;
  2346.   self.Cujo.think = self.Cujo.th_stand;
  2347.  
  2348.   return;
  2349. };
  2350.  
  2351. /*╔═══════════════════════════════════════════════════════════════════════╗
  2352.   ║                                                                       ║
  2353.   ║ Cujo_LightToggle                                                      ║
  2354.   ║                                                                       ║
  2355.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  2356.  
  2357.  
  2358. // This will always be called from the players code, so self is always
  2359. // expected to be the player
  2360.  
  2361. void () CUJO_LightToggle =
  2362. {
  2363.   local   float   effect;
  2364.   local   float   bitmask;
  2365.  
  2366.   if (self.Cujo_flag)
  2367.   {
  2368.     effect = EF_DIMLIGHT;
  2369.  
  2370.     bitmask = (effect) & self.Cujo.effects;
  2371.  
  2372.     if (bitmask == 0)
  2373.     {
  2374.       self.Cujo.effects = self.Cujo.effects | effect;
  2375.     }
  2376.     else
  2377.     {
  2378.       bitmask = !(bitmask);
  2379.       self.Cujo.effects = (self.Cujo.effects) & bitmask;
  2380.     }
  2381.   }
  2382.   else
  2383.     CUJO_PrintCujoStatus ();
  2384.  
  2385.   return;
  2386. };
  2387.  
  2388. /*╔═══════════════════════════════════════════════════════════════════════╗
  2389.   ║                                                                       ║
  2390.   ║ Cujo_SetDogView                                                       ║
  2391.   ║                                                                       ║
  2392.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  2393.  
  2394. // This will always be called from the players code, so self is always
  2395. // expected to be the player
  2396.  
  2397. void () CUJO_SetDogView =
  2398. {
  2399.   if (self.Cujo_flag)
  2400.   {
  2401.     // Set view point
  2402.     msg_entity = self;
  2403.     WriteByte (MSG_ONE, 5);                 // SVC, set the viewport
  2404.     WriteEntity (MSG_ONE, self.Cujo);
  2405.  
  2406.     // set the view angles to Cujo's view angles
  2407. //    WriteByte (MSG_ONE, 10);               // SVC, set view angles
  2408. //    WriteAngle(MSG_ONE, self.Cujo.angles_x);
  2409. //    WriteAngle(MSG_ONE, self.Cujo.angles_y);
  2410. //    WriteAngle(MSG_ONE, self.Cujo.angles_z);
  2411.  
  2412.     self.weaponmodel = "";
  2413.     self.weaponframe = 0;
  2414.  
  2415.     self.Cujo_view = TRUE;
  2416.  
  2417.     old_player_angles = self.angles;
  2418.  
  2419.     // turn to Cujo's angles...  I guess
  2420.     self.angles = self.Cujo.angles;
  2421.     self.fixangle = TRUE;        // turn this way immediately
  2422.  
  2423.   }
  2424.   else
  2425.     CUJO_PrintCujoStatus ();
  2426.  
  2427.   return;
  2428.  
  2429. };
  2430.  
  2431. /*╔═══════════════════════════════════════════════════════════════════════╗
  2432.   ║                                                                       ║
  2433.   ║ Cujo_SetPlayerView                                                    ║
  2434.   ║                                                                       ║
  2435.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  2436.  
  2437.  
  2438. // This will always be called from the players code, so self is always
  2439. // expected to be the player
  2440.  
  2441. void () CUJO_SetPlayerView =
  2442. {
  2443.   if (self.Cujo_flag)
  2444.   {
  2445.     // Set view point
  2446.     msg_entity = self;
  2447.     WriteByte (MSG_ONE, 5);                 // service, set the viewport
  2448.     WriteEntity (MSG_ONE, self);
  2449.  
  2450. //    WriteByte (MSG_ONE, 10);               // SVC, set view angles
  2451. //    WriteAngle(MSG_ONE, self.angles_x);    // tilt
  2452. //    WriteAngle(MSG_ONE, self.angles_y);    // yaw
  2453. //    WriteAngle(MSG_ONE, self.angles_z);    // flip
  2454.  
  2455.     // reset the players weapon model
  2456.     self.Cujo_view = FALSE;
  2457.     W_SetCurrentAmmo ();
  2458.  
  2459.     self.angles = old_player_angles;
  2460.     self.fixangle = TRUE;        // turn this way immediately
  2461.   }
  2462.   else
  2463.     CUJO_PrintCujoStatus ();
  2464.  
  2465.   return;
  2466. };
  2467.  
  2468. /*╔═══════════════════════════════════════════════════════════════════════╗
  2469.   ║                                                                       ║
  2470.   ║ Cujo_Stay                                                             ║
  2471.   ║                                                                       ║
  2472.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  2473.  
  2474. // always called from player code.  Toggles self.Cujo.Cujo_stay which
  2475. // is used in follow routines to determine if Cujo should stay or follow
  2476.  
  2477. void () CUJO_Stay =
  2478. {
  2479.   if (self.Cujo_flag)
  2480.   {
  2481.     // toggle Cujo's stay flag
  2482.     if (self.Cujo.Cujo_stay == TRUE)
  2483.     {
  2484.       self.Cujo.Cujo_stay = FALSE;
  2485.       sprint (self, "Cujo is following.\n");
  2486.     }
  2487.     else
  2488.     {
  2489.       self.Cujo.Cujo_stay = TRUE;
  2490.       sprint (self, "Cujo is staying.\n");
  2491.     }
  2492.  
  2493.   }
  2494.   else
  2495.     CUJO_PrintCujoStatus ();
  2496.  
  2497.   return;
  2498. };
  2499.  
  2500. /*╔═══════════════════════════════════════════════════════════════════════╗
  2501.   ║                                                                       ║
  2502.   ║ Cujo_GiveStatus                                                       ║
  2503.   ║                                                                       ║
  2504.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  2505.  
  2506. // called from player code, ie. self = player
  2507.  
  2508. void () CUJO_GiveStatus =
  2509. {
  2510.   local   string  str_temp;
  2511.   local   float   ftemp;
  2512.  
  2513.   if (self.Cujo_flag)
  2514.   {
  2515.     if (visible (self.Cujo))
  2516.     {
  2517.       sprint (self, "Cujo's health is ");
  2518.  
  2519.       if (!deathmatch)
  2520.         ftemp = self.Cujo.health / 2;
  2521.       else
  2522.         ftemp = self.Cujo.health;
  2523.  
  2524.       str_temp = ftos (ftemp);
  2525.       sprint (self, str_temp);
  2526.       sprint (self, "%.\n");
  2527.  
  2528.       sprint (self, "His armor is ");
  2529.  
  2530.       if (!deathmatch)
  2531.         ftemp = self.Cujo.armorvalue / 2;
  2532.       else
  2533.         ftemp = self.Cujo.armorvalue / 1.5;
  2534.  
  2535.       str_temp = ftos (ftemp);
  2536.       sprint (self, str_temp);
  2537.       sprint (self, "%.\n");
  2538.     }
  2539.     else
  2540.     {
  2541.       sprint (self, "You must be able to see Cujo to determine his status.\n");
  2542.     }
  2543.   }
  2544.   else
  2545.     CUJO_PrintCujoStatus ();
  2546.  
  2547.   return;
  2548. };
  2549.  
  2550. /*╔═══════════════════════════════════════════════════════════════════════╗
  2551.   ║                                                                       ║
  2552.   ║ Cujo_KillAllMonsters                                                  ║
  2553.   ║                                                                       ║
  2554.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  2555.  
  2556. // to make debugging maps easier -- no having to fight stuff
  2557.  
  2558. void () CUJO_KillAllMonsters =
  2559. {
  2560.   local   entity  head;
  2561.  
  2562.   head = nextent (world);
  2563.  
  2564.   while (head)
  2565.   {
  2566.     if ((head.classname == "monster_army")
  2567.     || (head.classname == "monster_demon1")
  2568.     || (head.classname == "monster_dog")
  2569.     || (head.classname == "monster_dragon")
  2570.     || (head.classname == "monster_enforcer")
  2571.     || (head.classname == "monster_fish")
  2572.     || (head.classname == "monster_hell_knight")
  2573.     || (head.classname == "monster_knight")
  2574.     || (head.classname == "monster_ogre")
  2575.     || (head.classname == "monster_oldone")
  2576.     || (head.classname == "monster_shalrath")
  2577.     || (head.classname == "monster_shambler")
  2578.     || (head.classname == "monster_tarbaby")
  2579.     || (head.classname == "monster_vomit")
  2580.     || (head.classname == "monster_wizard"))
  2581.     {
  2582.       if (random () < 0.2)
  2583.         T_Damage (head, self, self, head.health + 100);
  2584.       else
  2585.         T_Damage (head, self, self, head.health + 1);
  2586.       bprint ("kill ");
  2587.     }
  2588.     else if (head.classname == "monster_zombie")
  2589.       T_Damage (head, self, self, head.health + 50);
  2590.  
  2591.     head = nextent (head);
  2592.    }
  2593.    bprint ("Everything is dead.\n");
  2594. };
  2595.