agrip/tutor.mqc

Go to the documentation of this file.
00001 // AGRIP Notes:
00002 //  * This tutor bot is basically the normal one plus some code from oldman
00003 //    that makes it use the ZQ bot support.
00004 //  * It has 3 skill levels:
00005 //      + 0 -- stays in the same place during combat
00006 //      + 1 -- chases player during combat (when sensible)
00007 //      + 2 -- does bot_run_slide() during combat
00008 
00009 // Extra defns...
00010 .float ai_time;
00011 void bot_attack();
00012 vector realorigin (entity ent)
00013 {
00014         return (ent.absmin + ent.absmax) * 0.5;
00015 }
00016 
00017 // KickABotClientCheck
00018 // Ensures clients can't kick bots below the padclients level...
00019 // Returns 0 if the user is not allowed to kick the bot, 1 if they are.
00020 float KickABotClientCheck()
00021 {
00022     local float pclients;
00023 
00024     pclients = stof(infokey(world, "bots_padclients"));
00025     
00026     if( infokey(world, "bots_clientcontrol") == "1" )
00027     {
00028         if( CountPlayers() > pclients )
00029             return 1;
00030         else
00031             sprint(self, PRINT_HIGH, "Can't remove bots that are there to pad player slots!\n");
00032     }
00033     else
00034         sprint(self, PRINT_HIGH, "Clients not allowed to add/kick bots on this server!\n");
00035     
00036     return 0;
00037 }
00038 
00039 /*
00040 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
00041 
00042 Kick A Bot (DM version)
00043 Adapted from FrikBot version to support bot padding
00044 
00045 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
00046 */
00047 
00048 void KickABotDM(float clientcalled)
00049 {
00050     if( clientcalled )
00051         if( !KickABotClientCheck )
00052             return 2;
00053 
00054         entity pkick;
00055         pkick = find(world, classname, "player");
00056         while( pkick != world )
00057         {
00058         if( infokey(pkick, "isbot") == "1" )
00059         {
00060                         stuffcmd(pkick, "disconnect\n");
00061                         pkick = world;
00062                 }
00063                 else
00064         {
00065                         pkick = find(pkick, classname, "player");
00066         }
00067         }
00068 
00069     // FIXME all bots kicked -> deny.wav?
00070 }
00071 
00072 /*
00073 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
00074 
00075 Kick A Bot (Team version)
00076 Adapted from FrikBot version to support bot padding
00077 
00078 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
00079 */
00080 
00081 float KickABotTeam(float bteam, float clientcalled)
00082 {
00083     // Returns:
00084     //       2 for you're not allowed to kick
00085     //       1 for didn't kick (not found)
00086     //       0 for did kick
00087    
00088     // FIXME do loop in here, check for no bots left
00089     
00090     local string isbot;
00091     local string found_ent_team;
00092     local string bot_team, s_bot_team;
00093     local float retval;
00094 
00095     retval = 1;  // assume we don't find smt to kick
00096 
00097     if( clientcalled )
00098         if( !KickABotClientCheck() )
00099             return 2;
00100 
00101     // Look up real team name for passed in team number..
00102     bot_team = snap_misc_nameforteamnum(bteam);
00103     s_bot_team = strzone(bot_team);
00104 
00105         entity pkick;
00106         pkick = find(world, classname, "player");
00107         while( pkick != world )
00108         {
00109         found_ent_team = infokey(pkick, "team");
00110         isbot = infokey(pkick, "isbot");
00111 
00112         if( isbot == "1" && found_ent_team == s_bot_team )
00113         {
00114                         stuffcmd(pkick, "disconnect\n");
00115                         pkick = world;
00116             retval = 0;
00117                 }
00118                 else
00119         {
00120                         pkick = find(pkick, classname, "player");
00121         }
00122         }
00123 
00124     strunzone(s_bot_team);
00125     return retval;
00126 
00127     // FIXME all bots kicked -> deny.wav?
00128 }
00129 
00130 
00131 /*
00132 ---------------------------------------------------------------------------
00133 
00134                 T U T O R   B O T
00135 
00136 
00137 
00138                 Author:         Darryl "Coffee" Atchison
00139                                         coffee@planetquake.com
00140 
00141                 Website:                http://www.planetquake.com/minion/tutorial/main.htm
00142                                         http://www.planetquake.com
00143 
00144                 Version:                1.0
00145                                         1.10.99
00146                                 
00147 
00148 
00149 
00150                 I n s t r u c t i o n s
00151 
00152                 Yes, you heard correctly, this one file acts as an almost-
00153         fully-functional bot. You can plug him into deathmatch patches and
00154         mods. Or you can read his tutorials and customize him.
00155                 Here's what to do.
00156 
00157 
00158         1. Open up the PROGS.SRC file, and insert the words "tutor.qc"
00159                 without quotes after the words "misc.qc" 
00160 
00161 
00162         2. Open up the file CLIENT.QC and scroll down to ClientObituary().
00163                 You'll see an early line looking like this:             
00164 
00165                 if (targ.classname == "player")
00166 
00167         Change it to this:
00168 
00169                 if (targ.classname == "player" || targ.classname == "bot")
00170         
00171         About 20 lines down, you see this line:
00172 
00173                 if (attacker.classname == "player")
00174 
00175         Change it to this:
00176 
00177                 if (attacker.classname == "player" || attacker.classname == "bot")
00178 
00179 
00180         3. Open up WEAPONS.QC and add this line to the top of the file:
00181 
00182 void create_bot(float teem);
00183 
00184         Then, down below, add these lines to ImpulseCommands():
00185 
00186                 if (self.impulse == 100)
00187                         create_bot(1);
00188                 if (self.impulse == 101)
00189                         create_bot(2);
00190 
00191 
00192         4. Compile as you normally would and enjoy.
00193 
00194 
00195 ---------------------------------------------------------------------------
00196 */
00197 
00198 
00199 
00200 /*
00201 ==========================================================================
00202 ==========================================================================
00203 ==========================================================================
00204 
00205         Section 1: AI
00206 
00207         Don't let this part worry you. Basically it's just walking and
00208         running thoughts. He'll check around himself for items, use
00209         movetogoal() to navigation, then grab his goal when he's close
00210         enough. He always looks for enemy bots and players. In addition,
00211         he must manually check for water, lava, and gaps in front of him.
00212         Remember, though, he's made to be simple, not smart.
00213 
00214 ==========================================================================
00215 ==========================================================================
00216 ==========================================================================
00217 */
00218 
00219 
00220 // declaring the routines before they are called
00221 // AGRIP - remove these 2:
00222 void bot_jump1();
00223 void respawn_bot();
00224 void bot_check_ammo();
00225 
00226 
00227 
00228 
00229 // ------------------------------------------------
00230 void bot_search_for_items()
00231 // ------------------------------------------------
00232 {
00233 local entity item;
00234 
00235 // he gives up on that item and marks it to avoid it for a while
00236         if (time > self.search_time && self.goalentity != world)
00237                 {
00238                 self.goalentity.search_time = time + 30;
00239                 self.goalentity = world;
00240                 }
00241 
00242         if (self.goalentity != world)
00243                 return;
00244 
00245 // checks a radius around him for items
00246         item = findradius(self.origin, 1500);
00247 
00248         while(item)
00249                 {
00250                 if ( (item.flags & FL_ITEM) && visible(item) && item.model != string_null && time > item.search_time)
00251                         {
00252                         self.search_time = time + 30;
00253                         self.goalentity = item;
00254                         }
00255                 item = item.chain;
00256                 }
00257 
00258 };
00259 
00260 
00261 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
00262 void bot_grab_items()
00263 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
00264 {
00265 
00266 // sees if he's close enough to pick that item up
00267 
00268         if (self.goalentity == world)
00269                 return;
00270 
00271         if (vlen(self.origin - self.goalentity.origin) <= 70)
00272                 {
00273                 self.goalentity.search_time = time + 60;
00274                 self.goalentity.solid = SOLID_NOT;
00275                 self.goalentity.model = string_null;
00276                 self.goalentity.nextthink = time + 20;
00277                 self.goalentity.think = SUB_regen;
00278                 if (self.goalentity.healamount)
00279                         sound (self, CHAN_ITEM, "items/health1.wav", 1, ATTN_NORM);
00280                 else if (self.goalentity.weapon)
00281                         sound (self, CHAN_ITEM, "weapons/pkup.wav", 1, ATTN_NORM);
00282                 else
00283                         sound(self, CHAN_ITEM, "items/armor1.wav", 1, ATTN_NORM);
00284                 self.goalentity = world;
00285                 }
00286 
00287 };
00288 
00289 
00290 
00291 
00292 
00293 
00294 
00295 
00296 // -----------------------------------------
00297 void jump_forward()
00298 // -----------------------------------------
00299 {
00300 // propels him into the air
00301 
00302         if (!(self.flags & FL_ONGROUND))
00303                 return;
00304 
00305         self.flags = self.flags - (self.flags & FL_ONGROUND);
00306         makevectors(self.angles);
00307         self.velocity = self.velocity + (v_forward * 250);
00308         self.velocity_z = self.velocity_z + 250;
00309 
00310 };
00311 
00312 
00313 // ------------------------------------------------
00314 void check_for_water()
00315 // ------------------------------------------------
00316 {
00317 local float p;
00318 
00319 // bots don't see water like players do, so we check for it
00320 
00321         makevectors(self.angles);
00322         p = pointcontents(self.origin + v_forward*16);
00323         if (p != CONTENT_WATER && p != CONTENT_SLIME && p != CONTENT_LAVA)
00324                 return;
00325 
00326         if (p == CONTENT_WATER && time > self.pain_finished)
00327                 {
00328                 T_Damage (self, world, world, 5);
00329                 self.pain_finished = time + 2;
00330                 sound (self, CHAN_VOICE, "player/gasp2.wav", 1, ATTN_NORM);
00331                 }
00332         if (p == CONTENT_SLIME && time > self.pain_finished)
00333                 {
00334                 T_Damage (self, world, world, 10);
00335                 self.pain_finished = time + 1;
00336                 sound (self, CHAN_VOICE, "player/lburn2.wav", 1, ATTN_NORM);
00337                 }
00338         if (p == CONTENT_LAVA && time > self.pain_finished)
00339                 {
00340                 T_Damage (self, world, world, 20);
00341                 self.pain_finished = time + 0.5;
00342                 sound (self, CHAN_VOICE, "player/lburn1.wav", 1, ATTN_NORM);
00343                 }
00344 
00345         self.flags = self.flags - (self.flags & FL_ONGROUND);
00346 
00347 // he'll try to swim upward here
00348         self.velocity = self.velocity + (v_forward * 200);
00349         self.velocity_z = self.velocity_z + 200;
00350         if (random() < 0.4)
00351                 self.velocity_x = self.velocity_x + 100;
00352         else if (random() > 0.8)
00353                 self.velocity_y = self.velocity_y + 100;
00354 
00355 };
00356 
00357 
00358 
00359 
00360 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
00361 void check_for_ledge()
00362 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
00363 {
00364 local vector spot;
00365 
00366 // movetogoal() will never move over a legde, so we have to 
00367 // check for a break in front of him and force him to jump
00368 
00369         if (random() < 0.80)
00370                 return;
00371 
00372         if (!(self.flags & FL_ONGROUND))
00373                 return;
00374 
00375     // ``old'' code a.a.t. new tutor bot in qc...
00376     self.flags = self.flags - (self.flags & FL_ONGROUND);
00377     makevectors(self.angles);
00378     self.velocity = self.velocity + (v_forward * 250);
00379     self.velocity_z = self.velocity_z + 250;
00380     
00381 /*      makevectors (self.angles);
00382         spot = self.origin + (v_forward * 60);
00383         spot = spot - '0 0 35';
00384         if (pointcontents(spot) == CONTENT_EMPTY)
00385                 bot_jump1();*/
00386 };
00387 
00388 
00389 
00390 
00391 
00392 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
00393 float bot_look_for_players()
00394 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
00395 {
00396         local entity    client;
00397         local float             r;
00398 
00399 // this is just like id's FindTarget(), he's looking for clients
00400 
00401         client = checkclient ();
00402         if (!client)
00403                 return false;
00404 
00405     if( coop )// && ( self.colormap == client.colormap || !client ) )
00406     {
00407         local entity favent;
00408 
00409         // Look for a monster instead...
00410         client = findradius(self.origin, 1000);
00411         while( client )
00412         {
00413             if(     client.flags & FL_MONSTER
00414                     &&  client.health > 0
00415                     &&  visible(client) )
00416                 favent = client;
00417 
00418             client = client.chain;
00419         }
00420 
00421         client = favent;
00422     }
00423     /* $AGRIP-END */
00424 
00425 
00426     // AGRIP matatk no:
00427         /*if (teamplay)
00428                 if (self.team == client.team)
00429                         return false;*/
00430     if (teamplay)
00431     {
00432         local string myteam, theirteam;
00433         myteam = infokey(self, "team");
00434         theirteam = infokey(client, "team");
00435         if (myteam == theirteam && myteam != "")
00436             return false;
00437     }
00438 
00439         if (client.netname == "observer")
00440                 return false;
00441 
00442         if (client == self.enemy)
00443                 return false;
00444 
00445         if (client.flags & FL_NOTARGET)
00446                 return false;
00447         if (client.items & IT_INVISIBILITY)
00448                 return false;
00449 
00450         r = range (client);
00451         if (r == RANGE_FAR)
00452                 return false;
00453                 
00454         if (!visible (client))
00455                 return false;
00456 
00457         if (r == RANGE_NEAR)
00458         {
00459                 if (client.show_hostile < time && !infront (client))
00460                         return false;
00461         }
00462         else if (r == RANGE_MID)
00463         {
00464                 if (!infront (client))
00465                         return false;
00466         }
00467         
00468         self.enemy = client;
00469         
00470     // AGRIP: no
00471     self.goalentity = client;
00472         //FoundTarget ();
00473 
00474         return true;
00475 };
00476 
00477 
00478 /*
00479 // ------------------------------------------------
00480 void bot_look_for_bots()
00481 // ------------------------------------------------
00482 {
00483 local entity found, foe;
00484 
00485 // bots aren't clients, so we have to check fo them manually
00486 // we just see if any of the bots in the entity list are visible
00487 
00488         if (self.enemy)
00489                 return;
00490 
00491         found = world;
00492         foe = find(world, classname, "bot");
00493 
00494         while(foe)
00495                 {
00496                 if (visible(foe) && foe != self && foe.health > 0) 
00497                         found = foe;
00498                 if (teamplay && found.team == self.team)
00499                         found = world;
00500                 foe = find(foe, classname, "bot");
00501                 }
00502 
00503         if (found != world)
00504                 {
00505                 self.enemy = found;
00506                 self.goalentity = found;
00507                 self.th_run();
00508                 }
00509 };*/
00510 
00511 /* AGRIP no:
00512 // ----------------------
00513 void bot_face()
00514 // ----------------------
00515 {
00516 // this turns him directly toward his enemy
00517 
00518         self.angles_y = vectoyaw(self.enemy.origin - self.origin);
00519 };
00520 */
00521 
00522 // ----------------------
00523 void bot_face()
00524 // ----------------------
00525 {
00526     if(self.enemy != world) {
00527         vector viewangles;
00528 
00529         // this turns him directly toward his enemy
00530         self.angles_y = vectoyaw(self.enemy.origin - self.origin);
00531         viewangles = vectoangles(self.enemy.origin - self.origin);
00532         self.v_angle_x = viewangles_x;
00533         self.v_angle_y = viewangles_y;
00534         
00535         if (self.v_angle_x > 180)
00536             self.v_angle_x = self.v_angle_x - 360; // corrects an ID mistake
00537         
00538         self.v_angle_x = self.v_angle_x * -1;  // corrects an ID mistake
00539         
00540         if (self.v_angle_x > 80)
00541             self.v_angle_x = 80;
00542         
00543         if (self.v_angle_x < -80)
00544             self.v_angle_x = -80;
00545     }
00546 }
00547     
00548 // ----------------------
00549 void bot_stand()
00550 // ----------------------
00551 {
00552 
00553 // his standing thoughts, pretty simple
00554 
00555     // AGRIP: no
00556         //bot_look_for_bots();
00557         bot_look_for_players();
00558         check_for_water();
00559 
00560         if (time > self.pausetime)
00561         {
00562                 self.th_walk();
00563                 return;
00564         }
00565         
00566 // do a cute little turn
00567         if (random() < 0.1)
00568                 self.angles_y = self.angles_y - 45;
00569         else if (random() > 0.9)
00570                 self.angles_y = self.angles_y + 15;
00571 };
00572 
00573 
00574 
00575 
00576 // ******************************
00577 void coffee_move()
00578 // ******************************
00579 {
00580 
00581 // this is the best subroutine i've ever written, and probably the
00582 // most powerful bot roaming function. i hope you credit me if you use
00583 // it. basically he strafes along a wall, then turns at a 45 or -45
00584 // degree angle at the wall's corner. i have seen my bots do laps
00585 // around entire levels with these three lines of code
00586 
00587         if (walkmove (self.angles_y, 20) == false)
00588                 if (walkmove (self.angles_y + self.button1, 20) == false)
00589                         self.angles_y = self.angles_y + (self.button1 / 2);
00590 
00591 // every so often, he'll change his wall-hugging direction
00592 
00593         if (random() <= 0.02)
00594                 if (self.button1 == 90)
00595                         self.button1 = -90;
00596                         else self.button1 = 90;
00597 
00598 };
00599 
00600 
00601 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
00602 void bot_walk()
00603 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
00604 {
00605         
00606 
00607 // this is his main AI routine, where he will look for items and enemies
00608 
00609         if (!(self.flags & FL_ONGROUND))
00610                 return;
00611 
00612         // AGRIP: no
00613     //bot_look_for_bots();
00614         bot_look_for_players();
00615         //bot_search_for_items();
00616         //bot_grab_items();
00617 
00618         check_for_ledge();
00619         check_for_water();
00620 
00621 // of course movetogoal() is id's C function, it moves randomly
00622 // toward what his self.goalentity is; don't let it worry you,
00623 // this function takes a long time to get where its going
00624 // the coffee_move is his cool running function
00625 
00626         if (self.goalentity != world)
00627                 movetogoal(20);
00628                 else coffee_move();
00629 
00630 
00631 
00632 };
00633 
00634 
00635 
00636 
00637 
00638 // --------------------------------
00639 void bot_run_slide()
00640 // --------------------------------
00641 {
00642         local float     ofs;
00643         
00644 // this is a cool strafing routine
00645 
00646         if (self.lefty)
00647                 ofs = 90;
00648         else
00649                 ofs = -90;
00650 
00651         if (walkmove (self.angles_y + ofs, 20))
00652                 return;
00653                 
00654         self.lefty = 1 - self.lefty;
00655         
00656         walkmove (self.angles_y - ofs, 20);
00657 };
00658 
00659 
00660 
00661 
00662 // ----------------------
00663 void bot_strafe()
00664 // ----------------------
00665 {
00666 // this routine is called every frame during combat, 
00667 // so he strafes and dodges even while shooting
00668 
00669         bot_check_ammo();
00670 
00671         if (!visible(self.enemy))
00672                 {
00673                 movetogoal(20);
00674                 return;
00675                 }
00676 
00677         bot_face();
00678 
00679 
00680 // stepping backwards for a long distance shot
00681         if (self.weapon == IT_ROCKET_LAUNCHER)
00682                 {
00683                 if (walkmove (self.angles_y - 180, 20) == false)
00684                         bot_run_slide();
00685                 }
00686 
00687 // chasing the player here
00688         else if (self.weapon == IT_SUPER_SHOTGUN)
00689                 movetogoal(20);
00690 
00691 // standing still while attacking
00692         else if (self.weapon == IT_LIGHTNING)
00693                 return;
00694 //      else bot_run_slide();   // AGRIP
00695     else
00696     {
00697         // AGRIP
00698         // Skill level 0 -- just stand there like an idiot.
00699         // Skill level 1 -- chase player
00700         // Skill level 2 -- bot_run_slide()
00701         local float skill;
00702         skill = stof(infokey(self, "bskill"));
00703         if (skill == 1)
00704         {
00705             if( self.weapon != IT_ROCKET_LAUNCHER )
00706             if( self.weapon != IT_GRENADE_LAUNCHER )
00707             {
00708                 movetogoal(20);
00709             }
00710         }
00711         else if (skill == 2)
00712         {
00713             bot_run_slide();
00714         }
00715         // END AGRIP skillz stuff
00716     }
00717 };
00718 
00719 
00720 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
00721 void bot_run()
00722 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
00723 {
00724 
00725 // his fighting thoughts. after a short while, he'll give up 
00726 // on his enemy, but if he can see him, he'll always attack
00727 
00728         if (!(self.flags & FL_ONGROUND))
00729                 return;
00730 
00731         check_for_water();
00732 
00733         if (visible(self.enemy))
00734                 self.search_time = time + 6;
00735 
00736         if (time > self.search_time || self.enemy.health <= 0)
00737                 {
00738                 self.goalentity = world;
00739                 self.enemy = world;
00740                 self.pausetime = time + 4;
00741         // AGRIP no:
00742                 //self.th_stand();
00743                 return;
00744                 }
00745 
00746         bot_strafe();
00747 
00748     // AGRIP:
00749     bot_face();
00750 
00751         if (visible(self.enemy) && time > self.attack_finished)
00752                 bot_attack();
00753                 //self.th_missile(); AGRIP
00754 
00755 };
00756 
00757 
00758 
00759 
00760 
00761 
00762 
00763 
00764 
00765 /*
00766 ==========================================================================
00767 ==========================================================================
00768 ==========================================================================
00769 
00770         Section 2: Weapons
00771 
00772         This section is the simplest, basically dull stuff. It checks for
00773         his best weapon and sets the relevant ammo. It gives him a free
00774         weapon. And it does the actual firing of the weapons. The key
00775         difference between a player weapon routine and a bot weapon 
00776         routine is the aiming. In player routines, you'll see a line
00777         like this:
00778 
00779                 dir = aim (self, 100000);
00780 
00781         If you want a bot to share that subroutine, basically all you need
00782         to do is change it to this:
00783 
00784                 if (self.classname == "player")
00785                         dir = aim (self, 100000);
00786                         else dir = normalize(self.enemy.origin - self.origin);
00787                         
00788         This allows the bot to aim directly at his enemy.
00789 
00790 
00791 ==========================================================================
00792 ==========================================================================
00793 ==========================================================================
00794 */
00795 
00796 // AGRIP no:
00797 void bot_run1();
00798 
00799 
00800 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
00801 void bot_drop_pack()
00802 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
00803 {
00804         if (random () < 0.5)
00805                 DropBackpack();
00806 };
00807 
00808 
00809 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
00810 void give_random_weapon(entity targ)
00811 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
00812 {
00813 local float it;
00814 
00815         it = floor(random() * 6);
00816 
00817         if (it == 0)
00818                 {
00819                 targ.items = targ.items | IT_SUPER_SHOTGUN;
00820                 targ.currentammo = targ.ammo_shells = targ.ammo_shells + 25;
00821                 targ.weapon = IT_SUPER_SHOTGUN;
00822                 }
00823         else if (it == 1)
00824                 {
00825                 targ.items = targ.items | IT_NAILGUN;
00826                 targ.currentammo = targ.ammo_nails = targ.ammo_nails + 50;
00827                 targ.weapon = IT_NAILGUN;
00828                 }
00829         else if (it == 2)
00830                 {
00831                 targ.items = targ.items | IT_LIGHTNING;
00832                 targ.currentammo = targ.ammo_cells = targ.ammo_cells + 50;
00833                 targ.weapon = IT_LIGHTNING;
00834                 }
00835         else if (it == 3)
00836                 {
00837                 targ.items = targ.items | IT_GRENADE_LAUNCHER;
00838                 targ.currentammo = targ.ammo_rockets = targ.ammo_rockets + 5;
00839                 targ.weapon = IT_GRENADE_LAUNCHER;
00840                 }
00841         else if (it == 4)
00842                 {
00843                 targ.items = targ.items | IT_ROCKET_LAUNCHER;
00844                 targ.currentammo = targ.ammo_rockets = targ.ammo_rockets + 5;
00845                 targ.weapon = IT_ROCKET_LAUNCHER;
00846                 }
00847         else
00848                 {
00849                 targ.items = targ.items | IT_SUPER_NAILGUN;
00850                 targ.currentammo = targ.ammo_nails = targ.ammo_nails + 50;
00851                 targ.weapon = IT_SUPER_NAILGUN;
00852                 }
00853 
00854 };
00855 
00856 
00857 
00858 
00859 
00860 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
00861 float bot_bestweapon()
00862 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
00863 {
00864         local   float   it;
00865         
00866         it = self.items;
00867 
00868         if (self.ammo_rockets >= 1 && (it & IT_ROCKET_LAUNCHER) )
00869                 return IT_ROCKET_LAUNCHER;
00870         else if (self.ammo_cells >= 2 && (it & IT_LIGHTNING) )
00871                 return IT_LIGHTNING;
00872         else if (self.ammo_nails >= 2 && (it & IT_SUPER_NAILGUN) )
00873                 return IT_SUPER_NAILGUN;
00874         else if (self.ammo_rockets >= 1 && (it & IT_GRENADE_LAUNCHER) )
00875                 return IT_GRENADE_LAUNCHER;
00876         else if (self.ammo_shells >= 2 && (it & IT_SUPER_SHOTGUN) )
00877                 return IT_SUPER_SHOTGUN;
00878         else if (self.ammo_nails >= 1 && (it & IT_NAILGUN) )
00879                 return IT_NAILGUN;
00880 
00881                 return IT_SHOTGUN;
00882 
00883 };
00884 
00885 
00886 
00887 // --------------------------------
00888 void bot_set_currentammo()
00889 // --------------------------------
00890 {
00891         self.items = self.items - ( self.items & (IT_SHELLS | IT_NAILS | IT_ROCKETS | IT_CELLS) );
00892         
00893       if (self.weapon == IT_SHOTGUN)
00894         {
00895                 self.currentammo = self.ammo_shells;
00896                 self.items = self.items | IT_SHELLS;
00897         }
00898         else if (self.weapon == IT_SUPER_SHOTGUN)
00899         {
00900                 self.currentammo = self.ammo_shells;
00901                 self.items = self.items | IT_SHELLS;
00902         }
00903         else if (self.weapon == IT_NAILGUN)
00904         {
00905                 self.currentammo = self.ammo_nails;
00906                 self.items = self.items | IT_NAILS;
00907         }
00908         else if (self.weapon == IT_SUPER_NAILGUN)
00909         {
00910                 self.currentammo = self.ammo_nails;
00911                 self.items = self.items | IT_NAILS;
00912         }
00913         else if (self.weapon == IT_ROCKET_LAUNCHER)
00914         {
00915                 self.currentammo = self.ammo_rockets;
00916                 self.items = self.items | IT_ROCKETS;
00917         }
00918         else if (self.weapon == IT_LIGHTNING)
00919         {
00920                 self.currentammo = self.ammo_cells;
00921                 self.items = self.items | IT_CELLS;
00922         }
00923         else
00924         {
00925                 self.currentammo = 0;
00926                 self.weaponmodel = "";
00927                 self.weaponframe = 0;
00928         }
00929 };
00930 
00931 
00932 // -------------------------
00933 void bot_check_ammo()
00934 // -------------------------
00935 {
00936 local float chance;
00937 
00938         if (self.currentammo > 0)
00939                 return;
00940 
00941         if (self.weapon == IT_SHOTGUN)
00942                 return;
00943 
00944         self.weapon = bot_bestweapon();
00945 
00946         bot_set_currentammo();
00947         
00948     // AGRIP no:
00949         //bot_run1();
00950 };
00951 
00952 
00953 
00954 // -------------------------------------
00955 vector() bot_aim_at_enemy =
00956 // -------------------------------------
00957 {
00958 
00959         return normalize(self.enemy.origin - self.origin);
00960 
00961 };
00962 
00963 
00964 // -------------------------------------
00965 void bot_fire_supershotgun()
00966 // -------------------------------------
00967 {
00968 local vector dir;
00969 
00970         self.currentammo = self.ammo_shells = self.ammo_shells - 2;
00971         bot_face();
00972         sound (self ,CHAN_WEAPON, "weapons/shotgn2.wav", 1, ATTN_NORM); 
00973         //self.effects = self.effects | EF_MUZZLEFLASH;
00974         dir = bot_aim_at_enemy();
00975         FireBullets (18, dir, '0.14 0.1 0');
00976 };
00977 
00978 
00979 
00980 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
00981 void bot_fire_shotgun()
00982 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
00983 {
00984 local vector dir;
00985         
00986         if (self.weapon == IT_SUPER_SHOTGUN)
00987                 {
00988                 bot_fire_supershotgun();
00989                 return;
00990                 }
00991 
00992         bot_face();
00993 
00994         sound (self, CHAN_WEAPON, "weapons/guncock.wav", 1, ATTN_NORM); 
00995         //self.effects = self.effects | EF_MUZZLEFLASH;
00996 
00997         dir = bot_aim_at_enemy();
00998         FireBullets (6, dir, '0.04 0.04 0');
00999 
01000 };
01001 
01002 
01003 
01004 
01005 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
01006 void bot_fire_supernailgun()
01007 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
01008 {
01009 local vector dir;
01010 
01011         self.currentammo = self.ammo_nails = self.ammo_nails - 1;
01012         sound (self, CHAN_WEAPON, "weapons/spike2.wav", 1, ATTN_NORM);
01013 
01014         dir = bot_aim_at_enemy();
01015         launch_spike (self.origin + '0 6 16', dir);
01016         newmis.touch = superspike_touch;
01017         setmodel (newmis, "progs/s_spike.mdl");
01018         setsize (newmis, VEC_ORIGIN, VEC_ORIGIN);
01019 };
01020 
01021 
01022 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
01023 void bot_fire_nailgun()
01024 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
01025 {
01026 local vector dir;
01027 
01028         if (self.weapon == IT_SUPER_NAILGUN)
01029                 {
01030                 bot_fire_supernailgun();
01031                 return;
01032                 }
01033 
01034         self.currentammo = self.ammo_nails = self.ammo_nails - 1;
01035         makevectors (self.v_angle);
01036         sound (self, CHAN_WEAPON, "weapons/rocket1i.wav", 1, ATTN_NORM);
01037 
01038         dir = bot_aim_at_enemy();
01039         launch_spike (self.origin + '0 6 16', dir);
01040 
01041 };
01042 
01043 
01044 // """"""""""""""""""""""""""""
01045 void bot_fire_grenade()
01046 // ----------------------------
01047 {
01048         local   entity missile;
01049         
01050         self.currentammo = self.ammo_rockets = self.ammo_rockets - 1;
01051         //self.effects = self.effects | EF_MUZZLEFLASH;
01052         sound (self, CHAN_WEAPON, "weapons/grenade.wav", 1, ATTN_NORM);
01053 
01054         missile = spawn ();
01055         missile.owner = self;
01056         missile.movetype = MOVETYPE_BOUNCE;
01057         missile.solid = SOLID_BBOX;
01058                 
01059 // set missile speed    
01060         makevectors (self.v_angle);
01061 
01062         missile.velocity = bot_aim_at_enemy();
01063         missile.velocity = missile.velocity * 500;
01064         missile.velocity_z = 300;
01065 
01066         missile.avelocity = '300 300 300';
01067 
01068         missile.angles = vectoangles(missile.velocity);
01069         
01070         missile.touch = GrenadeTouch;
01071         
01072 // set missile duration
01073         missile.nextthink = time + 2.5;
01074         missile.think = GrenadeExplode;
01075 
01076         setmodel (missile, "progs/grenade.mdl");
01077         setsize (missile, '0 0 0', '0 0 0');            
01078         setorigin (missile, self.origin);
01079 
01080 };
01081 
01082 
01083 
01084 // =============================
01085 void bot_fire_rocket()
01086 // -----------------------------
01087 {
01088 local entity missile;
01089 
01090         if (self.weapon == IT_GRENADE_LAUNCHER)
01091                 {
01092                 bot_fire_grenade();
01093                 return;
01094                 }
01095 
01096         self.currentammo = self.ammo_rockets = self.ammo_rockets - 1;
01097         sound (self, CHAN_WEAPON, "weapons/sgun1.wav", 1, ATTN_NORM);
01098 
01099         missile = spawn ();
01100         missile.owner = self;
01101         missile.movetype = MOVETYPE_FLYMISSILE;
01102         missile.solid = SOLID_BBOX;
01103         missile.classname = "missile";
01104 
01105         missile.velocity = bot_aim_at_enemy() * 1000;
01106 
01107         missile.angles = vectoangles(missile.velocity);
01108         missile.touch = T_MissileTouch;
01109         missile.nextthink = time + 5;
01110         missile.think = SUB_Remove;
01111 
01112         setmodel (missile, "progs/missile.mdl");
01113         setsize (missile, '-1 -1 -1', '1 1 1');
01114         setorigin (missile, self.origin + v_forward*8 + '0 0 16');
01115 };
01116 
01117 
01118 
01119 
01120 
01121 
01122 // ==============================
01123 void bot_lightning()
01124 // ------------------------------
01125 {
01126         local   vector  org, dir;
01127         
01128         //self.effects = self.effects | EF_MUZZLEFLASH;
01129 
01130         bot_face();
01131 
01132 
01133         makevectors(self.angles);
01134         org = self.origin + '0 0 8' + (v_forward * 8);
01135 
01136         dir = bot_aim_at_enemy();
01137 
01138         traceline (org, self.origin + dir*600, true, self);
01139 
01140         WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
01141         WriteByte (MSG_BROADCAST, TE_LIGHTNING1);
01142         WriteEntity (MSG_BROADCAST, self);
01143         WriteCoord (MSG_BROADCAST, org_x);
01144         WriteCoord (MSG_BROADCAST, org_y);
01145         WriteCoord (MSG_BROADCAST, org_z);
01146         WriteCoord (MSG_BROADCAST, trace_endpos_x);
01147         WriteCoord (MSG_BROADCAST, trace_endpos_y);
01148         WriteCoord (MSG_BROADCAST, trace_endpos_z);
01149 
01150         LightningDamage (org, trace_endpos, self, 10);
01151 
01152         sound (self, CHAN_WEAPON, "weapons/lhit.wav", 1, ATTN_NORM);
01153         self.currentammo = self.ammo_cells = self.ammo_cells - 1;
01154 };
01155 
01156 
01157 
01158 
01159 
01160 
01161 
01162 
01163 
01164 
01165 /*
01166 ==========================================================================
01167 ==========================================================================
01168 ==========================================================================
01169 
01170         Section 3: Animation
01171 
01172         This is a bunch of ugly stuff, and you don't really need to
01173         understand all of it. It merely defines his animation frames and
01174         sequences. After that you have his pain, death, and attack routines.
01175         Lastly, we have his spawn/respawn subroutine.
01176 
01177 ==========================================================================
01178 ==========================================================================
01179 ==========================================================================
01180 */
01181 
01182 
01183 /*
01184 // Frame macros
01185 
01186 $cd /raid/quake/id1/models/player_4
01187 $origin 0 -6 24
01188 $base base              
01189 $skin skin
01190 
01191 $frame axrun1 axrun2 axrun3 axrun4 axrun5 axrun6
01192 
01193 $frame rockrun1 rockrun2 rockrun3 rockrun4 rockrun5 rockrun6
01194 
01195 //
01196 // standing
01197 //
01198 $frame stand1 stand2 stand3 stand4 stand5
01199 
01200 $frame axstnd1 axstnd2 axstnd3 axstnd4 axstnd5 axstnd6
01201 $frame axstnd7 axstnd8 axstnd9 axstnd10 axstnd11 axstnd12
01202 
01203 
01204 //
01205 // pain
01206 //
01207 $frame axpain1 axpain2 axpain3 axpain4 axpain5 axpain6
01208 
01209 $frame pain1 pain2 pain3 pain4 pain5 pain6
01210 
01211 
01212 //
01213 // death
01214 //
01215 
01216 $frame axdeth1 axdeth2 axdeth3 axdeth4 axdeth5 axdeth6
01217 $frame axdeth7 axdeth8 axdeth9
01218 
01219 $frame deatha1 deatha2 deatha3 deatha4 deatha5 deatha6 deatha7 deatha8
01220 $frame deatha9 deatha10 deatha11
01221 
01222 $frame deathb1 deathb2 deathb3 deathb4 deathb5 deathb6 deathb7 deathb8
01223 $frame deathb9
01224 
01225 $frame deathc1 deathc2 deathc3 deathc4 deathc5 deathc6 deathc7 deathc8
01226 $frame deathc9 deathc10 deathc11 deathc12 deathc13 deathc14 deathc15
01227 
01228 $frame deathd1 deathd2 deathd3 deathd4 deathd5 deathd6 deathd7
01229 $frame deathd8 deathd9
01230 
01231 $frame deathe1 deathe2 deathe3 deathe4 deathe5 deathe6 deathe7
01232 $frame deathe8 deathe9
01233 
01234 //
01235 // attacks
01236 //
01237 $frame nailatt1 nailatt2
01238 
01239 $frame light1 light2
01240 
01241 $frame rockatt1 rockatt2 rockatt3 rockatt4 rockatt5 rockatt6
01242 
01243 $frame shotatt1 shotatt2 shotatt3 shotatt4 shotatt5 shotatt6
01244 
01245 $frame axatt1 axatt2 axatt3 axatt4 axatt5 axatt6
01246 
01247 $frame axattb1 axattb2 axattb3 axattb4 axattb5 axattb6
01248 
01249 $frame axattc1 axattc2 axattc3 axattc4 axattc5 axattc6
01250 
01251 $frame axattd1 axattd2 axattd3 axattd4 axattd5 axattd6
01252 
01253 
01254 // movement animation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
01255 
01256 void bot_stand1()
01257 void bot_stand2()
01258 void bot_stand3()
01259 void bot_stand4()
01260 void bot_stand5()
01261 
01262         
01263 void bot_walk1()
01264 void bot_walk2()
01265 void bot_walk3()
01266 void bot_walk4()
01267 void bot_walk5()
01268 void bot_walk6()
01269 
01270 void bot_run1()
01271 void bot_run2()
01272 void bot_run3()
01273 void bot_run4()
01274 void bot_run5()
01275 void bot_run6()
01276 
01277 
01278 void bot_jump1()
01279 void bot_jump2()
01280 void bot_jump3()
01281 void bot_jump4()
01282 void bot_jump5()
01283 
01284 
01285 // attack animation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
01286 
01287 void bot_rock1()
01288 void bot_rock2()
01289 void bot_rock3()
01290 void bot_rock4()
01291 void bot_rock5()
01292 void bot_rock6()
01293 
01294 void bot_shot1()
01295 void bot_shot2()
01296 void bot_shot3()
01297 void bot_shot4()
01298 void bot_shot5()
01299 void bot_shot6()
01300 
01301 void bot_nail1()
01302 void bot_nail2()
01303 void bot_nail3()
01304 void bot_nail4()
01305 void bot_nail5()
01306 void bot_nail6()
01307 
01308 void bot_light1()
01309 void bot_light2()
01310 void bot_light3()
01311 void bot_light4()
01312 void bot_light5()
01313 void bot_light6()
01314 
01315 
01316 
01317 
01318 
01319 void th_respawn()
01320 {
01321         self.think = respawn_bot;
01322         self.nextthink = time + 1;
01323 };
01324 
01325 void bot_pain1()
01326 void bot_pain2()
01327 void bot_pain3()
01328 void bot_pain4()
01329 void bot_pain5()
01330 void bot_pain6()
01331 
01332 void bot_die1()
01333 void bot_die2()
01334 void bot_die3()
01335 void bot_die4()
01336 void bot_die5()
01337 void bot_die6()
01338 void bot_die7()
01339 void bot_die8()
01340 void bot_die9()
01341 void bot_die10()
01342 void bot_die11()
01343 
01344 void bot_dieb1()
01345 void bot_dieb2()
01346 void bot_dieb3()
01347 void bot_dieb4()
01348 void bot_dieb5()
01349 void bot_dieb6()
01350 void bot_dieb7()
01351 void bot_dieb8()
01352 void bot_dieb9()
01353 
01354 void bot_diec1()
01355 void bot_diec2()
01356 void bot_diec3()
01357 void bot_diec4()
01358 void bot_diec5()
01359 void bot_diec6()
01360 void bot_diec7()
01361 void bot_diec8()
01362 void bot_diec9()
01363 void bot_diec10()
01364 void bot_diec11()
01365 void bot_diec12()
01366 void bot_diec13()
01367 void bot_diec14()
01368 void bot_diec15()
01369 
01370 void bot_died1()
01371 void bot_died2()
01372 void bot_died3()
01373 void bot_died4()
01374 void bot_died5()
01375 void bot_died6()
01376 void bot_died7()
01377 void bot_died8()
01378 void bot_died9()
01379 
01380 void bot_diee1()
01381 void bot_diee2()
01382 void bot_diee3()
01383 void bot_diee4()
01384 void bot_diee5()
01385 void bot_diee6()
01386 void bot_diee7()
01387 void bot_diee8()
01388 void bot_diee9()
01389 
01390 
01391 
01392 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
01393 void bot_pain(entity attacker, float damage)
01394 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
01395 {
01396 
01397 // get mad at attacker if he's just walking
01398         if (attacker != self.enemy && attacker != world && self.enemy.classname != "player")
01399                 {
01400                 self.enemy = attacker;
01401                 FoundTarget();
01402                 }
01403 
01404         if (self.pain_finished > time)
01405                 return;
01406 
01407         if (random() < 0.25)
01408                 return;
01409         
01410         PainSound();
01411 
01412         if (random() > 0.75)
01413                 return;
01414         
01415         self.pain_finished = time + 1;
01416         bot_pain1();
01417 
01418 };
01419 
01420 
01421 
01422 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
01423 void gib_bot()
01424 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
01425 {
01426         ThrowGib ("progs/h_player.mdl", self.health);
01427         ThrowGib ("progs/gib1.mdl", self.health);
01428         ThrowGib ("progs/gib2.mdl", self.health);
01429         setmodel (self, "progs/gib3.mdl");
01430         setsize (self, '0 0 0', '0 0 0');
01431 
01432         self.deadflag = DEAD_DEAD;
01433         self.think = th_respawn;
01434         self.nextthink = time + 1;
01435 
01436         if (damage_attacker.classname == "teledeath")
01437         {
01438                 sound (self, CHAN_VOICE, "player/teledth1.wav", 1, ATTN_NONE);
01439                 return;
01440         }
01441 
01442         if (damage_attacker.classname == "teledeath2")
01443         {
01444                 sound (self, CHAN_VOICE, "player/teledth1.wav", 1, ATTN_NONE);
01445                 return;
01446         }
01447                 
01448         if (random() < 0.5)
01449                 sound (self, CHAN_VOICE, "player/gib.wav", 1, ATTN_NONE);
01450         else
01451                 sound (self, CHAN_VOICE, "player/udeath.wav", 1, ATTN_NONE);
01452 
01453 };
01454 
01455 
01456 
01457 
01458 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
01459 void bot_die()
01460 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
01461 {
01462 local float i;
01463 
01464 // we're turning the bot off here and playing his death scene
01465 
01466         self.deadflag = DEAD_DYING;
01467         self.solid = SOLID_NOT;
01468         self.flags = self.flags - (self.flags & FL_ONGROUND);
01469         self.movetype = MOVETYPE_TOSS;
01470         if (self.velocity_z < 10)
01471                 self.velocity_z = self.velocity_z + random()*300;
01472 
01473         if (self.health < -40)
01474                 {
01475                 gib_bot();
01476                 return;
01477                 }
01478         
01479         DeathSound();
01480 
01481         i = floor(random() * 5);
01482 
01483         if (i == 0)
01484                 bot_die1();
01485         else if (i == 1)
01486                 bot_dieb1();
01487         else if (i == 2)
01488                 bot_diec1();
01489         else if (i == 3)
01490                 bot_died1();
01491         else
01492                 bot_diee1();
01493         
01494 };
01495 
01496 
01497 
01498 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
01499 void bot_attack()
01500 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
01501 {
01502 // this routine decides which animation sequence to play
01503 
01504         if (time < self.attack_finished)
01505                 return;
01506 
01507         bot_check_ammo();
01508 
01509         if (self.weapon == IT_SHOTGUN)
01510                 {
01511                 self.attack_finished = time + 0.5;
01512                 bot_shot1();
01513                 }
01514         else if (self.weapon == IT_SUPER_SHOTGUN)
01515                 {
01516                 self.attack_finished = time + 0.7;
01517                 bot_shot1();
01518                 }
01519         else if (self.weapon == IT_NAILGUN)
01520                 {
01521                 self.attack_finished = time + 0.2;
01522                 bot_nail1();
01523                 }
01524         else if (self.weapon == IT_SUPER_NAILGUN)
01525                 {
01526                 self.attack_finished = time + 0.2;
01527                 bot_nail1();
01528                 }
01529         else if (self.weapon == IT_GRENADE_LAUNCHER)
01530                 {
01531                 self.attack_finished = time + 0.8;
01532                 bot_rock1();
01533                 }
01534         else if (self.weapon == IT_ROCKET_LAUNCHER)
01535                 {
01536                 self.attack_finished = time + 1;
01537                 bot_rock1();
01538                 }
01539         else if (self.weapon == IT_LIGHTNING)
01540                 {
01541                 self.attack_finished = time + 0.1;
01542                 bot_light1();
01543                 }
01544 
01545 
01546 };
01547 */
01548 
01549 
01550 /*
01551 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
01552 weapon_range
01553 
01554 _x "sweet spot range" - try to maintain this range if possible
01555 _y minimum range bot can be to be effective (rl/gl) (move away)
01556 _z maximum range bot can be to be effective (lg/axe) (move in)
01557  -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
01558 */
01559 vector weapon_range (float wep)
01560 {
01561     if (wep == IT_AXE)
01562         return '48 0 64';
01563     else if (wep == IT_SHOTGUN)
01564         return '128 0 99999';
01565     else if (wep == IT_SUPER_SHOTGUN)
01566         return '128 0 99999';
01567     else if (wep == IT_NAILGUN)
01568         return '180 0 3000';
01569     else if (wep == IT_SUPER_NAILGUN)
01570         return '180 0 3000';
01571     else if (wep == IT_GRENADE_LAUNCHER)
01572         return '180 48 3000';
01573     else if (wep == IT_ROCKET_LAUNCHER)
01574         return '180 48 3000';
01575     else if (wep == IT_LIGHTNING)
01576         return '350 0 512';
01577 }
01578 
01579 
01580 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
01581 void bot_attack()
01582 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
01583 {
01584     vector dir, org, range;
01585     float foedist;
01586 
01587     bot_check_ammo();
01588     /* dir = vectoangles(bot_aim_at_enemy()); */
01589     /* self.v_angle_x = dir_x; */
01590     /* self.v_angle_y = dir_y; */
01591     org = realorigin(self.enemy);
01592     makevectors(self.v_angle);
01593     foedist = vlen(org - self.origin);
01594     range = weapon_range(self.weapon);
01595     traceline (self.origin + self.view_ofs, self.origin + self.view_ofs + v_forward * range_z, false, self);
01596 
01597     if (vlen(trace_endpos - (self.origin + self.view_ofs)) >= range_y)
01598         self.button0 = true;
01599 }
01600 
01601 
01602 // ------------------------------------------------
01603 string() bot_name =
01604 // ------------------------------------------------
01605 {
01606     local float n;
01607         n = floor(random() * 16);
01608         if (n == 0)     return "Obliterator";
01609         else if (n == 1) return "Obelisk";
01610         else if (n == 2) return "Obscure";
01611         else if (n == 3) return "Obtuse";
01612         else if (n == 4) return "Obe";
01613         else if (n == 5) return "Obsidian";
01614         else if (n == 6) return "Obstructor";
01615         else if (n == 7) return "Obi";
01616         else if (n == 8) return "Obstinate";
01617         else if (n == 9) return "Objector";
01618     else if (n ==10) return "Oberon";
01619     else if (n ==11) return "Obveela";
01620     else if (n ==12) return "Obate";
01621     else if (n ==13) return "Obruge";
01622     else if (n ==14) return "Obete";
01623         else return "Oblivion";
01624         /*if (n == 0)   return "[B] Frog";
01625         else if (n == 1) return "[B] Reaper";
01626         else if (n == 2) return "[B] Omicron";
01627         else if (n == 3) return "[B] Estep";
01628         else if (n == 4) return "[B] Zeus";
01629         else if (n == 5) return "[B] Cujo";
01630         else if (n == 6) return "[B] Gyro";
01631         else if (n == 7) return "[B] Oak";
01632         else if (n == 8) return "[B] Eliminator";
01633         else if (n == 9) return "[B] Eraser";
01634         else return "[B] BG";*/
01635 };
01636 
01637 
01638 
01639 /*
01640 // ------------------------------------------------
01641 void respawn_bot()
01642 // ------------------------------------------------
01643 {
01644 
01645 local entity spot;
01646 
01647 // putting him back in game
01648         spot = SelectSpawnPoint();
01649         self.origin = spot.origin + '0 0 1';
01650         self.angles = spot.angles;
01651         self.fixangle = true;   
01652         spawn_tfog (self.origin);
01653         spawn_tdeath (self.origin, self);
01654         self.solid = SOLID_SLIDEBOX;
01655         self.movetype = MOVETYPE_STEP;
01656         self.flags = self.flags - (self.flags & FL_ONGROUND);
01657         makevectors(self.angles);
01658         self.velocity = self.velocity + v_forward*20;
01659 
01660 // making him normal again
01661         setmodel(self, "progs/player.mdl");
01662         setsize (self, VEC_HULL_MIN, VEC_HULL_MAX);
01663         self.deadflag = DEAD_NO;
01664         self.takedamage = DAMAGE_AIM;
01665         self.ideal_yaw = self.angles * '0 1 0';
01666         self.yaw_speed = 120;
01667         self.view_ofs = '0 0 22';
01668         self.frame = $stand1;
01669         self.button1 = 90;
01670 
01671 // finishing his characteristics
01672         self.items = IT_SHOTGUN;
01673         self.currentammo = self.ammo_shells = self.ammo_shells + 25;
01674         self.weapon = IT_SHOTGUN;
01675         give_random_weapon(self);
01676         self.health = 100;
01677         self.classname = "bot";
01678         self.enemy = world;
01679         self.goalentity = world;
01680         self.search_time = 0;
01681 
01682 // making him stand for a bit
01683         self.pausetime = time + 5;
01684         self.nextthink = time + 0.1 + random();
01685         self.think = self.th_stand;
01686 };
01687 */
01688 
01689 /*
01690 // ------------------------------------------------
01691 void create_bot(float teem)
01692 // ------------------------------------------------
01693 {
01694 local entity bot, spot, plr;
01695 
01696 // initializing the entity
01697         bot = spawn();
01698         spot = SelectSpawnPoint();
01699         bot.origin = spot.origin + '0 0 1';
01700         bot.angles = spot.angles;
01701         bot.fixangle = true;    
01702         spawn_tfog (bot.origin);
01703         spawn_tdeath (bot.origin, bot);
01704         bot.solid = SOLID_SLIDEBOX;
01705         bot.movetype = MOVETYPE_STEP;
01706 
01707 // defining his animation
01708         setmodel(bot, "progs/player.mdl");
01709         bot.frame = $stand1;
01710         bot.th_stand = bot_stand1;
01711         bot.th_walk = bot_walk1;
01712         bot.th_run = bot_run1;
01713         bot.th_pain = bot_pain;
01714         bot.th_die = bot_die;
01715         bot.th_missile = bot_attack;
01716 
01717 // arming and naming him
01718         bot.items = bot.items | IT_SHOTGUN;
01719         bot.currentammo = bot.ammo_shells = bot.ammo_shells + 25;
01720         bot.weapon = IT_SHOTGUN;
01721         give_random_weapon(bot);
01722         bot.health = 100;
01723         bot.classname = "bot";
01724 
01725         if (teamplay)
01726                 {
01727                 plr = find(world, classname, "player");
01728                 plr.team = 1;
01729                 bot.team = teem;
01730                 if (teem == plr.team)
01731                         bot.colormap = plr.colormap;
01732                 }
01733 
01734         bot.netname = bot_name();
01735         bprint(bot.netname);
01736         bprint(" enters the game\n");
01737 
01738 // polishing him up
01739         setsize (bot, VEC_HULL_MIN, VEC_HULL_MAX);
01740         bot.ideal_yaw = bot.angles * '0 1 0';
01741         bot.yaw_speed = 120;
01742         bot.view_ofs = '0 0 22';
01743         bot.takedamage = DAMAGE_AIM;
01744         bot.attack_state = 0;
01745         bot.button1 = 90;
01746         bot.nextthink = time + 0.1 + random();
01747         bot.think = bot.th_walk;
01748 };
01749 */
01750 
01751 // ------------------------------------------------
01752 void create_bot(float bottom, float top, string bteam, float clientcalled)
01753 // ------------------------------------------------
01754 {
01755     entity bot;
01756     local string s_bteam;
01757     local string skill, tmp;
01758 
01759     s_bteam = strzone(bteam);
01760     
01761     /*dprint("CREATE_BOT! bot: ");
01762     dprint(ftos(bottom));dprint("  top: ");
01763     dprint(ftos(top));dprint("  s_bteam: ");
01764     dprint(s_bteam);dprint("  cc: ");
01765     dprint(ftos(clientcalled));dprint("\n");*/
01766 
01767     // AGRIP - don't spawn a bot if it's not allowed...
01768     if( clientcalled )  // i.e. only check if we're a client
01769     if (!stof(infokey(world, "bots_clientcontrol")))
01770     {
01771         sprint(self, PRINT_HIGH, "Clients not allowed to add/kick bots on this server!\n");
01772         strunzone(s_bteam);
01773         // FIXME deny.wav!
01774         return;
01775     }
01776 
01777     // AGRIP - check bot skill setting...
01778     skill = infokey(world, "bots_skill");
01779     if( !skill || stof(skill) < 0 || stof(skill) > 2 )
01780     {
01781         sprint(self, PRINT_HIGH, "Can't add a bot - server admin has not set bots_skill to 0, 1 or 2!\n");
01782         strunzone(s_bteam);
01783         return;
01784     }
01785 
01786     // initializing the entity
01787     bot = testbot("tutorbot"); // use ZQuake server bot support
01788     if (!bot)
01789     {
01790         sprint(self, PRINT_HIGH, "Can't connect any more bots - server is full!\n");
01791         strunzone(s_bteam);
01792         return;
01793     }
01794 
01795     // Team name...
01796     setinfo (bot, "team", s_bteam);
01797     // NOTE: this is always set to text passed to testbot() whatever we do
01798     //       and doesn't work at all if it's not done right here... :-S
01799     strunzone(s_bteam);
01800 
01801     // Colours...
01802     tmp = ftos(bottom);
01803     setinfo (bot, "bottomcolor", tmp);
01804     tmp = ftos(top);
01805     setinfo (bot, "topcolor", tmp);
01806     
01807     // Ugly hack...
01808     //setinfo (bot, "fortytwo", "42");
01809 
01810     /* bot.speed = cvar("sv_maxspeed"); */
01811     bot.speed = 320;
01812 
01813     // Name...
01814     bot.netname = bot_name();
01815     setinfo (bot, "name", bot.netname);
01816 
01817     // initialise button1 to 90 degrees
01818     //self.button1 = 90;
01819     //self.failedmove = 0;
01820     bot.button1 = 90;
01821     //bot.failedmove = 0;
01822 
01823     // AGRIP skillz stuff...
01824     setinfo(bot, "isbot", "1");
01825     if( clientcalled )
01826         setinfo(bot, "cab", "1");
01827     setinfo(bot, "bskill", skill);  // variable by server admin
01828 }
01829 
01830 // called by the engine instead of ClientConnect for bots
01831 void BotConnect ()
01832 {
01833         ClientConnect ();
01834 }
01835 
01836 // called by the engine instead of ClientDisconnect for bots
01837 void BotDisconnect ()
01838 {
01839         ClientDisconnect ();
01840 }
01841 
01842 // called by the engine instead of PlayerPreThink for bots
01843 void BotPreThink ()
01844 {
01845         // am I dead? fire randomly until I respawn
01846         if (self.health < 1)
01847         {
01848                 self.button0 = false;
01849                 self.button2 = floor(random() * 2);
01850         self.enemy = self.goalentity = world;
01851         if (self.deadflag == DEAD_DEAD)
01852         {
01853             self.deadflag = DEAD_RESPAWNABLE;
01854             respawn();
01855         }
01856         return;
01857     }
01858 
01859     if (self.ai_time > time) {
01860         // do nothing
01861     } else {
01862         self.ai_time = time + 0.1;
01863         if(self.enemy == world || self.enemy == self) {
01864             self.button0 = false;
01865             /* if (!(self.flags & FL_ONGROUND)) */
01866                 /* self.button2 = false; */
01867             if (time > self.pausetime) {
01868                 bot_walk();
01869             } else {
01870                 bot_stand();
01871             }
01872         } else if (self.health > 0) {
01873             bot_run();
01874         }
01875     }
01876 
01877     PlayerPreThink ();
01878 }
01879 
01880 // called by the engine instead of PlayerPostThink for bots
01881 void BotPostThink ()
01882 {
01883         PlayerPostThink ();             // just call the default function
01884 }
01885 
01886 // vi:foldenable

Generated on Tue Jan 1 17:55:54 2008 for AudioQuake QuakeC by  doxygen 1.5.4