]> icculus.org git repositories - divverent/nexuiz.git/blob - qcsrc/gamec/bot.c
My stupidity is amazing
[divverent/nexuiz.git] / qcsrc / gamec / bot.c
1
2 /*
3 ======================================
4 FrikBot X (Version 0.10.1)
5 ======================================
6
7 This program is in the Public Domain. My crack legal
8 team would like to add:
9
10 RYAN "FRIKAC" SMITH IS PROVIDING THIS SOFTWARE "AS IS"
11 AND MAKES NO WARRANTY, EXPRESS OR IMPLIED, AS TO THE
12 ACCURACY, CAPABILITY, EFFICIENCY, MERCHANTABILITY, OR
13 FUNCTIONING OF THIS SOFTWARE AND/OR DOCUMENTATION. IN
14 NO EVENT WILL RYAN "FRIKAC" SMITH BE LIABLE FOR ANY
15 GENERAL, CONSEQUENTIAL, INDIRECT, INCIDENTAL,
16 EXEMPLARY, OR SPECIAL DAMAGES, EVEN IF RYAN "FRIKAC"
17 SMITH HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
18 DAMAGES, IRRESPECTIVE OF THE CAUSE OF SUCH DAMAGES.
19
20 You accept this software on the condition that you
21 indemnify and hold harmless Ryan "FrikaC" Smith from
22 any and all liability or damages to third parties,
23 including attorney fees, court costs, and other
24 related costs and expenses, arising out of your use
25 of this software irrespective of the cause of said
26 liability.
27
28 The export from the United States or the subsequent
29 reexport of this software is subject to compliance
30 with United States export control and munitions
31 control restrictions. You agree that in the event you
32 seek to export this software, you assume full
33 responsibility for obtaining all necessary export
34 licenses and approvals and for assuring compliance
35 with applicable reexport restrictions.
36
37 Any reproduction of this software must contain
38 this notice in its entirety.
39
40 ======================================
41 These installation instructions only apply to Normal Quake (as does this
42 entire file). For QuakeWorld, please refer to bot_qw.qc
43
44 --------------------------------------
45 To install on a new mod, do all this:
46 --------------------------------------
47 Place all included bot*.qc files in the subdirectory "frikbot"
48 in your source folder, then...
49
50 * Add the following lines to progs.src right after the defs.qc line
51 frikbot/bot.qc
52 frikbot/bot_way.qc
53 frikbot/bot_fight.qc
54 frikbot/bot_ai.qc
55 frikbot/bot_misc.qc
56 frikbot/bot_phys.qc
57 frikbot/bot_move.qc
58 frikbot/bot_ed.qc
59
60 --------------------------------------
61 * Comment out the following functions in defs.qc
62 sound, stuffcmd, sprint, aim, centerprint, setspawnparms
63 WriteByte, WriteChar, WriteShort, WriteLong, WriteCoord
64 WriteAngle, WriteString, WriteEntity
65 --------------------------------------
66 * Add this to worldspawn() in world.qc, right at the very top, before InitBodyQue();
67 BotInit();  // FrikBot
68 --------------------------------------
69 * add this line to StartFrame() in world.qc, at the very top
70 BotFrame(); // FrikBot
71 --------------------------------------
72 * Add these two lines to PlayerPreThink in client.qc at the very top
73 if (BotPreFrame()) // FrikBot
74         return;
75 --------------------------------------
76 * Add this line to PlayerPostThink in client.qc at the very top
77 if (BotPostFrame()) // FrikBot
78         return;
79 --------------------------------------
80 * Add the following line to the very top of Client Connect in client.qc
81 ClientInRankings(); // FrikBot
82 --------------------------------------
83 * Add these lines to the very top of ClientDisconnect in client.qc
84 ClientDisconnected(); // FrikBot
85 --------------------------------------
86 */
87
88 void() bot_map_load =
89 {
90         // place your qc loaded waypoints here
91         //if (mapname == "mattrye1_nex")
92         //      map_mattrye1_nex();
93 };
94
95 /*
96 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
97
98 Variables and shtuff
99
100 bot.qc has become pretty much a header file
101 for all variable in the bot...
102
103 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
104 */
105
106 // ----- entity fields ---
107 .float  wallhug, keys, oldkeys, ishuman;
108 .float  b_frags, b_clientno, b_shirt, b_pants;
109 .float  ai_time, b_sound, missile_speed;
110 .float  portal_time, b_skill, switch_wallhug;
111 .float  b_aiflags, b_num, b_chattime;
112 .float  b_menu, b_menu_time, b_menu_value;
113 .float route_failed, dyn_flags, dyn_time;
114 .float dyn_plat;
115 .entity temp_way, last_way, phys_obj;
116 .entity target1, target2, target3, target4;
117 .entity _next, _last;
118 .entity current_way;
119 .vector b_angle, mouse_emu, obs_dir;
120 .vector movement, b_dir;
121 .vector dyn_dest;
122
123 .float search_time;
124 .vector dest1, dest2;
125
126 // --------defines-----
127 //float SVC_UPDATENAME  = 13;
128 //float SVC_UPDATEFRAGS = 14;
129 //float SVC_UPDATECOLORS        = 17;
130
131 // used for the physics & movement AI
132 float KEY_MOVEUP                = 1;
133 float KEY_MOVEDOWN      = 2;
134 float KEY_MOVELEFT      = 4;
135 float KEY_MOVERIGHT     = 8;
136 float KEY_MOVEFORWARD   = 16;
137 float KEY_MOVEBACK      = 32;
138 float KEY_LOOKUP                = 64;
139 float KEY_LOOKDOWN      = 128;
140 float KEY_LOOKLEFT      = 256;
141 float KEY_LOOKRIGHT     = 512;
142
143 // these are aiflags for waypoints
144 // some overlap to the bot
145 float AI_TELELINK_1     = 1; // link type
146 float AI_TELELINK_2     = 2; // link type
147 float AI_TELELINK_3     = 4; // link type
148 float AI_TELELINK_4     = 8; // link type
149 float AI_DOORFLAG               = 16; // read ahead
150 float AI_PRECISION      = 32; // read ahead + point
151 float AI_SURFACE                = 64; // point
152 float AI_BLIND          = 128; // read ahead + point
153 float AI_JUMP           = 256; // point + ignore
154 float AI_DIRECTIONAL    = 512; // read ahead + ignore
155 float AI_PLAT_BOTTOM    = 1024; // read ahead
156 float AI_RIDE_TRAIN     = 2048; // read ahead
157 float AI_SUPER_JUMP     = 4096; // point + ignore + route test
158 float AI_SNIPER         = 8192; // point type
159 float AI_AMBUSH         = 16384; // point type
160 float AI_DOOR_NO_OPEN   = 32768; // read ahead
161 float AI_DIFFICULT      = 65536; // route test
162 float AI_TRACE_TEST     = 131072; // route test
163 float AI_CARELESS       = 262144; // Electro - jumppads, bot doesn't have to touch this entity, only get close
164
165 // these are flags for bots/players (dynamic/editor flags)
166 float AI_OBSTRUCTED     = 1;
167 float AI_HOLD_SELECT    = 2;
168 float AI_ROUTE_FAILED   = 2;
169 float AI_WAIT           = 4;
170 float AI_DANGER         = 8;
171
172
173 // addition masks
174 float AI_POINT_TYPES    = 29152;
175 float AI_READAHEAD_TYPES        = 36528;
176 float AI_IGNORE_TYPES   = 4864;
177
178 float WM_UNINIT         = 0;
179 float WM_DYNAMIC                = 1;
180 float WM_LOADING                = 2;
181 float WM_LOADED         = 3;
182 float WM_EDITOR         = 4;
183 float WM_EDITOR_DYNAMIC = 5;
184 float WM_EDITOR_DYNLINK = 6;
185
186
187 float OPT_SAVEBOTS      = 1;
188 float OPT_NOCHAT        = 2;
189
190 // -------globals-----
191 float   active_clients;
192 float           max_clients, real_frametime;
193 float           bot_count, b_options;
194 float           waypoint_mode, dump_mode;
195 float           waypoints, direct_route;
196 float           sv_friction, sv_gravity;
197 float           sv_accelerate, sv_maxairspeed, sv_maxspeed, sv_stopspeed;
198 entity  fixer;
199 entity  route_table;
200 entity  b_temp1, b_temp2, b_temp3;
201 entity  player_head, phys_head, way_head;
202 float           busy_waypoints;
203 float           saved_bots, saved_skills1, saved_skills2, current_bots;
204
205 // -------ProtoTypes------
206 // external
207 void()                          ClientConnect;
208 void()                          ClientDisconnect;
209 void()                          SetNewParms;
210
211 // rankings
212 float(float clientno)           ClientBitFlag;
213 float()                         ClientNextAvailable;
214 void(float whichteam, float whatbot, float whatskill) BotConnect;
215 void(entity bot)                        BotDisconnect;
216 void(float clientno)            BotInvalidClientNo;
217 void(entity who)                        UpdateClient;
218
219 // waypointing
220 void()                          DynamicWaypoint;
221 entity(vector org)              make_waypoint;
222 void()                          ClearAllWays;
223 void()                          FixWaypoints;
224 float()                         begin_route;
225 void(entity this, float direct)                 bot_get_path;
226 void()                          WaypointThink;
227 entity(entity start)                            FindWayPoint;
228
229 // physics & movement
230 float(entity e)                 bot_can_rj;
231 void()                          bot_jump;
232 void()                          frik_bot_roam;
233 float(vector weird)             frik_walkmove;
234 void()                          frik_movetogoal;
235 void()                          frik_obstacles;
236 float(float flag)                       frik_recognize_plat;
237 float(vector sdir)              frik_KeysForDir;
238 void(vector whichway, float danger) frik_obstructed;
239 void()                          SV_Physics_Client;
240 void()                          SV_ClientThink;
241 void()                          CL_KeyMove;
242
243 // ai & misc
244 string()                                PickARandomName;
245 float(entity targ)              fov;
246 float(float y1, float y2)       angcomp;
247 float(entity targ1, entity targ2)               wisible;
248 float(entity targ)              sisible;
249 float(entity targ)              fisible;
250 vector(entity ent)              realorigin;
251 void(entity ent)                        target_drop;
252 void(entity ent)                        target_add;
253 void()                          KickABot;
254 void()                          BotImpulses;
255 void(entity targ, float success) bot_lost;
256 string(float r)                 BotName;
257 float(float v)                  frik_anglemod;
258 //void() bot_chat;
259 void(float tpic) bot_start_topic;
260
261
262 // editor stuffs
263
264 void()                          bot_way_edit;
265 void()                          bot_menu_display;
266
267
268 // ----------Commands---------
269 void(entity e, float chan, string samp, float vol, float atten) frik_sound = #8;
270 void(entity client, string s)   frik_stuffcmd = #21;
271 void(entity client, string s)   frik_sprint = #24;
272 vector(entity e, float sped)    frik_aim = #44;
273 void(entity client, string s)   frik_centerprint = #73;
274 void(entity e)                  frik_setspawnparms = #78;
275 void(float to, float f)         frik_WriteByte = #52;
276 void(float to, float f)         frik_WriteChar = #53;
277 void(float to, float f)         frik_WriteShort = #54;
278 void(float to, float f)         frik_WriteLong = #55;
279 void(float to, float f)         frik_WriteCoord = #56;
280 void(float to, float f)         frik_WriteAngle = #57;
281 void(float to, string s)        frik_WriteString        = #58;
282 void(float to, entity s)        frik_WriteEntity        = #59;
283
284 void(entity client, string s1, string s2, string s3, string s4, string s5, string s6, string s7)
285 frik_big_centerprint = #73;
286
287 //----------------------------------------------------------------------------
288
289 /*
290 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
291
292 Function redclarations. These allow function
293 designed to work for clients (sprint, so forth)
294 to mainly not complain when working with a bot
295
296 Although these shouldn't be needed anymore,
297 as the bots truly are clients now, if you don't
298 stop the SZ_ buffer from filling up by disabling
299 direct messages to the bots, it crashes quake :-(
300
301 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
302 */
303 void(entity     client, string s) stuffcmd =
304 {
305         if (client.ishuman == 1)
306                 frik_stuffcmd(client, s);
307         b_temp1 = player_head;
308
309         while(b_temp1)
310         {
311                 if (b_temp1.classname == "botcam")
312                 {
313                         if ((b_temp1.enemy == client) && b_temp1.ishuman)
314                                 frik_stuffcmd(b_temp1, s);
315                 }
316                 b_temp1 = b_temp1._next;
317         }
318 };
319
320 void(entity     e) setspawnparms =
321 {
322         if (e.ishuman == 1)
323                 frik_setspawnparms(e);
324         else
325         {
326                 b_temp1 = player_head;
327                 while(b_temp1)
328                 {
329                         if (b_temp1.ishuman)
330                         {
331                                 frik_setspawnparms(b_temp1);
332                                 return;
333                         }
334                         b_temp1 = b_temp1._next;
335                 }
336                 SetNewParms();
337         }
338 };
339 void(entity     client, string s) sprint =
340 {
341         if (client.ishuman == 1)
342                 frik_sprint(client, s);
343         b_temp1 = player_head;
344
345         while(b_temp1)
346         {
347                 if (b_temp1.classname == "botcam")
348                 {
349                         if ((b_temp1.enemy == client) && b_temp1.ishuman)
350                                 frik_sprint(b_temp1, s);
351                 }
352                 b_temp1 = b_temp1._next;
353         }
354
355 };
356 void(entity     client, string s) centerprint =
357 {
358         if (client.ishuman == 1)
359                 frik_centerprint(client, s);
360         b_temp1 = player_head;
361
362         while(b_temp1)
363         {
364                 if (b_temp1.classname == "botcam")
365                 {
366                         if ((b_temp1.enemy == client) && b_temp1.ishuman)
367                                 frik_centerprint(b_temp1, s);
368                 }
369                 b_temp1 = b_temp1._next;
370         }
371 };
372
373 vector(entity e, float sped) aim =
374 {
375         e.missile_speed = sped;
376         return frik_aim(e, sped);
377 };
378
379 void(entity e, float chan, string samp, float vol, float atten) sound =
380 {
381
382         frik_sound(e, chan, samp, vol, atten);
383         if (samp == "items/inv3.wav")
384                 return;
385         else if (e.classname == "player")
386                 e.b_sound = time + 1;
387         else if (other.classname == "player")
388                 other.b_sound = time + 1;
389
390 };
391 void(float to, float f) WriteByte =
392 {
393         if ((to == MSG_ONE) && (msg_entity.ishuman != TRUE))
394                 return;
395         frik_WriteByte(to, f);
396 };
397 void(float to, float f) WriteChar =
398 {
399         if ((to == MSG_ONE) && (msg_entity.ishuman != TRUE))
400                 return;
401         frik_WriteChar(to, f);
402 };
403 void(float to, float f) WriteShort =
404 {
405         if ((to == MSG_ONE) && (msg_entity.ishuman != TRUE))
406                 return;
407         frik_WriteShort(to, f);
408 };
409 void(float to, float f) WriteLong =
410 {
411         if ((to == MSG_ONE) && (msg_entity.ishuman != TRUE))
412                 return;
413         frik_WriteLong(to, f);
414 };
415 void(float to, float f) WriteCoord =
416 {
417         if ((to == MSG_ONE) && (msg_entity.ishuman != TRUE))
418                 return;
419         frik_WriteCoord(to, f);
420 };
421 void(float to, float f) WriteAngle =
422 {
423         if ((to == MSG_ONE) && (msg_entity.ishuman != TRUE))
424                 return;
425         frik_WriteAngle(to, f);
426 };
427 void(float to, string s) WriteString =
428 {
429         if ((to == MSG_ONE) && (msg_entity.ishuman != TRUE))
430                 return;
431         frik_WriteString(to, s);
432 };
433 void(float to, entity s) WriteEntity =
434 {
435         if ((to == MSG_ONE) && (msg_entity.ishuman != TRUE))
436                 return;
437         frik_WriteEntity(to, s);
438 };
439 /*
440 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
441
442 Bot Cam, see what the bot sees (or any other player)
443
444 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
445 */
446
447 float() botcam =
448 {
449         if (self.classname != "botcam")
450                 return FALSE;
451         setorigin(self, self.enemy.origin);
452         self.items = self.enemy.items;
453         self.weapon = self.enemy.weapon;
454         self.weaponmodel = self.enemy.weaponmodel;
455         self.currentammo = self.enemy.currentammo;
456         self.weaponframe = self.enemy.weaponframe;
457         self.ammo_shells = self.enemy.ammo_shells;
458         self.ammo_nails = self.enemy.ammo_nails;
459         self.ammo_rockets= self.enemy.ammo_rockets;
460         self.ammo_cells = self.enemy.ammo_cells;
461         self.view_ofs = self.enemy.view_ofs;
462         self.health = self.enemy.health;
463         self.armorvalue = self.enemy.armorvalue;
464         self.dmg_take = self.enemy.dmg_take;
465         self.dmg_save = self.enemy.dmg_save;
466         self.dmg_inflictor = self.enemy.dmg_inflictor;
467         self.punchangle = self.enemy.punchangle;
468         self.deadflag = self.enemy.deadflag;
469         msg_entity = self;
470         WriteByte (MSG_ONE,5);
471         WriteEntity (MSG_ONE, self.enemy);
472         WriteByte (MSG_ONE, 10);
473         WriteAngle (MSG_ONE,self.enemy.v_angle_x);
474         WriteAngle (MSG_ONE,self.enemy.v_angle_y);
475         WriteAngle (MSG_ONE,self.enemy.v_angle_z);
476         self.modelindex = 0;
477
478         self.impulse = 0;
479         return TRUE;
480
481 };
482
483 void() botcam_u =
484 {
485
486         // sloppy cycling code
487         if (self.classname != "botcam")
488         {
489                 self.enemy = player_head;
490         }
491         else
492         {
493                 do
494                         self.enemy = self.enemy._next;
495                 while (self.enemy.classname == "botcam");
496         }
497         if (self.enemy == self)
498         {
499                 do
500                         self.enemy = self.enemy._next;
501                 while (self.enemy.classname == "botcam");
502         }
503
504         self.classname = "botcam";
505         self.solid = SOLID_NOT;
506         self.movetype = MOVETYPE_NONE;
507         self.takedamage = DAMAGE_NO;
508
509
510         if (!self.enemy)
511         {
512                 sprint(self, "No one left to track!\n");
513                 msg_entity = self;
514                 WriteByte (MSG_ONE,5);
515                 WriteEntity (MSG_ONE, self);
516                 PutClientInServer();
517                 return;
518         }
519         if (!self.enemy.ishuman)
520         {
521                 self.enemy.dmg_take = 0;
522                 self.enemy.dmg_save = 0;
523         }
524         sprint(self, "Now tracking ");
525         sprint(self, self.enemy.netname);
526         sprint(self, "\n");
527 };
528
529
530
531 /*
532 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
533
534 Stuff mentioned up top
535 it just links the bot into the mod
536
537 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
538 */
539
540 void() ClientFixRankings =
541 {
542         local float cno;
543         if (self.switch_wallhug > time)
544                 return;
545         self.switch_wallhug = 0;
546
547         b_temp2 = nextent(world);
548         cno = 0;
549
550         while (cno < max_clients)
551         {
552                 if ((!b_temp2.ishuman) && (active_clients & ClientBitFlag(cno)))
553                         UpdateClient(b_temp2);
554                 cno = cno + 1;
555                 b_temp2 = nextent(b_temp2);
556         }
557 };
558
559 void() ClientInRankings =
560 {
561         local float cno;
562         if (player_head)
563                 player_head._last = self;
564
565         self._next = player_head;
566         self._last = world;
567         player_head = self;
568
569         if (!self.phys_obj)
570         {
571                 b_temp2 = phys_head;
572                 while (b_temp2 != world && b_temp2.owner != self)
573                         b_temp2 = b_temp2._next;
574                 self.phys_obj = b_temp2;
575         }
576
577         if (self.ishuman == 2)
578         {
579                 self.ishuman = FALSE;
580                 return;
581         }
582         cno = self.colormap - 1;
583         BotInvalidClientNo (cno);
584         active_clients = active_clients | ClientBitFlag(cno);
585
586         self.b_clientno = cno;
587         self.ishuman = TRUE;
588         self.switch_wallhug = time + 1;
589 };
590
591
592 void() ClientDisconnected =
593 {
594         if (player_head == self)
595                 player_head = self._next;
596         if (self._next)
597                 self._next._last = self._last;
598         if (self._last)
599                 self._last._next = self._next;
600
601         active_clients = active_clients - active_clients & ClientBitFlag(self.b_clientno);
602 };
603 /*
604 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
605
606 BotPreFrame & BotPostFrame, used to make the
607 bot easier to install
608
609 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
610 */
611 float () BotPreFrame =
612 {
613         if (self.b_clientno == -1)
614                 return TRUE;
615         if (self.ishuman)
616         {
617                 if (self.switch_wallhug)
618                         ClientFixRankings();
619                 if (self.classname == "botcam")
620                         return TRUE;
621         }
622         if (self.b_frags != self.frags)
623         {
624
625                 if (self.b_frags > self.frags)
626                 {
627                         if (pointcontents(self.origin) == CONTENT_LAVA)
628                                 bot_start_topic(10);
629                         else
630                                 bot_start_topic(9);
631                 }
632                 else
633                         bot_start_topic(2);
634                 self.b_frags = self.frags;
635         }
636         DynamicWaypoint();
637         return FALSE;
638 };
639 float () BotPostFrame =
640 {
641         if (self.b_clientno == -1)
642                 return TRUE;
643         if (self.ishuman)
644         {
645
646                 if (waypoint_mode > WM_LOADED)
647                         bot_menu_display();
648
649                 BotImpulses();
650
651                 if (botcam())
652                         return TRUE;
653         }
654         return FALSE;
655 };
656
657 /*
658 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
659
660 Bot Chat code
661
662 The rest of this code is in bot_misc.qc
663
664 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
665 */
666 void(string h) BotSay = // simulate talking by composing a 'chat' message
667 {
668         WriteByte(MSG_ALL, 8);
669         WriteByte(MSG_ALL, 1);
670         WriteString(MSG_ALL, self.netname);
671         WriteByte(MSG_ALL, 8);
672         WriteByte(MSG_ALL, 2);
673         WriteString(MSG_ALL, h);
674 };
675 void() BotSayInit =
676 {
677         WriteByte(MSG_ALL, 8);
678         WriteByte(MSG_ALL, 1);
679         WriteString(MSG_ALL, self.netname);
680 };
681 void(string h) BotSay2 =
682 {
683         WriteByte(MSG_ALL, 8);
684         WriteByte(MSG_ALL, 2);
685         WriteString(MSG_ALL, h);
686 };
687 void(string h) BotSayTeam =
688 {
689         local entity t;
690         if (!teamplay)
691                 return;
692         t = player_head;
693         while(t)
694         {
695                 if (t.team == self.team)
696                 {
697                         msg_entity = t;
698                         WriteByte(MSG_ONE, 8);
699                         WriteByte(MSG_ONE, 1);
700                         WriteByte(MSG_ONE, 40);
701                         WriteString(MSG_ONE, self.netname);
702                         WriteByte(MSG_ONE, 8);
703                         WriteByte(MSG_ONE, 2);
704                         WriteByte(MSG_ONE, 41);
705                         WriteString(MSG_ONE, h);
706                 }
707                 t = t._next;
708         }
709 };
710 /*
711 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
712
713 BotInit
714
715 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
716 */
717
718
719 void() BotInit =
720 {
721         local entity ent, fisent;
722         local float numents;
723
724         // spawn entities for the physics
725         ent = nextent(world);
726         max_clients = 0;
727
728         while(ent != world)
729         {
730                 max_clients = max_clients + 1;
731                 ent = nextent(ent);
732         }
733         if (max_clients > 16)
734                 max_clients = 16;
735
736         ent = nextent(world);
737         fisent = world;
738         while (numents < max_clients)
739         {
740
741                 phys_head = spawn();
742                 if (fisent)
743                         fisent._next = phys_head;
744                 phys_head._last = fisent;
745                 fisent = phys_head;
746                 ent.phys_obj = phys_head;
747                 phys_head.classname = "phys_obj";
748                 phys_head.owner = ent;
749                 numents = numents + 1;
750                 ent = nextent(ent);
751         }
752         //precache_model("progs/s_light.spr"); // file missing from nexuiz
753         //precache_model("progs/s_bubble.spr"); // file missing from nexuiz
754         // the bots return!
755         b_options = cvar("saved1");
756         if (coop || (b_options & OPT_SAVEBOTS))
757         {
758                 saved_bots = cvar("scratch1");
759                 saved_skills1 = cvar("scratch2");
760                 saved_skills2 = cvar("scratch3");
761         }
762         cvar_set ("saved4", "0");
763         if (max_clients > 1)
764         {
765                 localcmd("exec maps/");
766                 localcmd(mapname);
767                 localcmd(".way\n");
768                 waypoint_mode = WM_DYNAMIC;
769                 bot_map_load();
770         }
771         else
772                 waypoint_mode = WM_LOADED;
773
774 };
775
776 /*
777 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
778
779 Rankings 'utilities'. Written by Alan Kivlin,
780 this code just fools clients by sending precisely
781 the same network messages as when a real player
782 signs on to the server.
783
784 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
785 */
786
787
788 void(entity who) UpdateClient =
789 {
790         WriteByte (MSG_ALL, SVC_UPDATENAME);
791         WriteByte (MSG_ALL, who.b_clientno);
792         WriteString (MSG_ALL, who.netname);
793         WriteByte (MSG_ALL, SVC_UPDATECOLORS);
794         WriteByte (MSG_ALL, who.b_clientno);
795         WriteByte (MSG_ALL, who.b_shirt * 16 + who.b_pants);
796         WriteByte (MSG_ALL, SVC_UPDATEFRAGS);
797         WriteByte (MSG_ALL, who.b_clientno);
798         WriteShort (MSG_ALL, who.frags);
799 };
800
801 float(float clientno) ClientBitFlag =
802 {
803         // bigger, but faster
804         if (clientno == 0)
805                 return 1;
806         else if (clientno == 1)
807                 return 2;
808         else if (clientno == 2)
809                 return 4;
810         else if (clientno == 3)
811                 return 8;
812         else if (clientno == 4)
813                 return 16;
814         else if (clientno == 5)
815                 return 32;
816         else if (clientno == 6)
817                 return 64;
818         else if (clientno == 7)
819                 return 128;
820         else if (clientno == 8)
821                 return 256;
822         else if (clientno == 9)
823                 return 512;
824         else if (clientno == 10)
825                 return 1024;
826         else if (clientno == 11)
827                 return 2048;
828         else if (clientno == 12)
829                 return 4096;
830         else if (clientno == 13)
831                 return 8192;
832         else if (clientno == 14)
833                 return 16384;
834         else if (clientno == 15)
835                 return 32768;
836         return 0;
837 };
838
839 float() ClientNextAvailable =
840 {
841         local float clientno;
842
843         clientno = max_clients;
844         while(clientno > 0)
845         {
846                 clientno = clientno - 1;
847
848                 if(!(active_clients & ClientBitFlag(clientno)))
849                         return clientno;
850         }
851
852         return -1;
853 };
854
855
856 void(entity e1, entity e2, float flag) DeveloperLightning =
857 {
858         // used to show waypoint links for debugging
859         WriteByte (MSG_BROADCAST, 23);
860         if (flag)
861                 WriteByte (MSG_BROADCAST, 6);
862         else
863                 WriteByte (MSG_BROADCAST, 13);
864         WriteEntity (MSG_BROADCAST, e2);
865         WriteCoord (MSG_BROADCAST, e1.origin_x);
866         WriteCoord (MSG_BROADCAST, e1.origin_y);
867         WriteCoord (MSG_BROADCAST, e1.origin_z);
868         WriteCoord (MSG_BROADCAST, e2.origin_x);
869         WriteCoord (MSG_BROADCAST, e2.origin_y);
870         WriteCoord (MSG_BROADCAST, e2.origin_z);
871 };
872
873 /*
874 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
875
876 Find Another Color
877
878 Team finding code
879
880 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
881 */
882
883 float(float tcolor) FindAnotherColor =
884 {
885         local float bestbet, scolor, pcount, bestp;
886         bestbet = -1;
887         bestp = 16;
888         while(scolor < 14)
889         {
890                 if (scolor != tcolor)
891                 {
892                         b_temp2 = player_head;
893                         pcount = 0;
894                         while(b_temp2 != world)
895                         {
896                                 if (b_temp2.team == scolor + 1)
897                                         pcount = pcount + 1;
898                                 b_temp2 = b_temp2._next;
899                         }
900                         if ((pcount < bestp) && pcount)
901                         {
902                                 bestbet = scolor;
903                                 bestp = pcount;
904                         }
905                 }
906                 scolor = scolor + 1;
907         }
908         if (bestbet < 0)
909         {
910                 bestbet = tcolor;
911                 while (bestbet == tcolor)
912                 {
913                         bestbet = floor(random() * 13);
914                 }
915         }
916         return bestbet;
917 };
918
919 /*
920 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
921
922 BotConnect and related functions.
923
924 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
925 */
926 entity(float num) GetClientEntity =
927 {
928         local entity upsy;
929         upsy = world;
930         num = num + 1;
931         while (num > 0)
932         {
933                 num = num - 1;
934                 upsy = nextent(upsy);
935         }
936         return upsy;
937 };
938
939 void(float whichteam, float whatbot, float whatskill) BotConnect =
940 {
941         local float f;
942         local string h;
943         local entity uself;
944
945         f = ClientNextAvailable();
946         uself = self;
947         if(f == -1)
948         {
949                 bprint("Unable to connect a bot, server is full.\n");
950                 return;
951         }
952
953         // chat thing
954
955         active_clients = active_clients | ClientBitFlag(f);
956         bot_count = bot_count + 1;
957         self = GetClientEntity(f);
958         if (!saved_bots)
959                 bot_start_topic(1);
960         self.b_clientno = f;
961         self.colormap = f + 1;
962         if (whatbot)
963                 self.netname = BotName(whatbot);
964         else
965                 self.netname = PickARandomName();
966
967
968         // players can set skill all weird, so leave these checks in
969         whatskill = rint(whatskill);
970         if (whatskill > 3)
971                 whatskill = 3;
972         else if (whatskill < 0)
973                 whatskill = 0;
974         self.b_skill = whatskill;
975
976         if (teamplay && !coop)
977         {
978                 if (whichteam)
979                         self.b_pants = FindAnotherColor(uself.team - 1);
980                 else
981                         self.b_pants = uself.team - 1;
982                 self.b_shirt = self.b_pants;
983         }
984
985         self.team = self.b_pants + 1;
986         UpdateClient(self);
987         SetNewParms();
988         self.ishuman = 2;
989         ClientConnect();
990         PutClientInServer();
991
992         // this is risky... could corrupt .way files if done wrong
993         // If you're not the gambling type, comment this out
994
995         f = ClientBitFlag(self.b_num - 1);
996         current_bots = current_bots | f;
997
998         if (self.b_num <= 8)
999                 saved_skills1 = (saved_skills1 & (65536 - (3 * f)) | (self.b_skill * f));
1000         else
1001         {
1002                 f = ClientBitFlag(self.b_num - 9);
1003                 saved_skills2 = (saved_skills2 & (65536 - (3 * f)) | (self.b_skill * f));
1004         }
1005
1006         h = ftos(current_bots);
1007         cvar_set("scratch1", h);
1008         h = ftos(saved_skills1);
1009         cvar_set("scratch2", h);
1010         h = ftos(saved_skills2);
1011         cvar_set("scratch3", h);
1012         self = uself;
1013
1014 };
1015
1016 void(entity bot) BotDisconnect =
1017 {
1018         local string h;
1019         local entity uself;
1020         uself = self;
1021         self = bot;
1022
1023         bot_count = bot_count - 1;
1024         current_bots = current_bots - (current_bots & ClientBitFlag(self.b_num - 1));
1025         h = ftos(current_bots);
1026         cvar_set("scratch1", h);
1027
1028
1029         ClientDisconnect();
1030
1031         if (self.b_clientno != -1)
1032         {
1033         // the bot's client number is not in use by a real player so we
1034                 // must remove it's entry in the rankings
1035                 // Quake engine sets all fields to 0, can only do the most important here
1036                 self.b_frags = self.frags = 0;
1037                 self.netname = "";
1038                 self.classname = "";
1039                 self.health = 0;
1040                 self.items = 0;
1041                 self.armorvalue = 0;
1042                 self.weaponmodel = "";
1043                 self.b_pants = 0;
1044                 self.b_shirt = 0;
1045                 self.ammo_shells = self.ammo_nails = self.ammo_rockets = self.ammo_cells = 0;
1046                 UpdateClient(self);
1047                 active_clients = active_clients - (active_clients & ClientBitFlag(self.b_clientno));
1048               self.b_clientno = -1;
1049         }
1050         self = uself;
1051 };
1052 /*
1053 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
1054
1055 BotInvalidClientNo
1056 kicks a bot if a player connects and takes the bot's space
1057
1058 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
1059 */
1060
1061 void(float clientno) BotInvalidClientNo =
1062 {
1063         local entity bot;
1064
1065         bot = GetClientEntity(clientno);
1066         if(bot.b_clientno > 0)
1067         {
1068                 if (!bot.ishuman)
1069                 {
1070                         bot.b_clientno = -1;
1071                         BotDisconnect(bot);
1072                         active_clients = active_clients | ClientBitFlag(self.b_clientno);
1073                         BotConnect(0, bot.b_num, bot.b_skill);
1074                         return;
1075                 }
1076         }
1077 };
1078
1079 /*
1080 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
1081
1082 Waypoint Loading from file
1083
1084 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
1085 */
1086 void() LoadWaypoint =
1087 {
1088         local vector org;
1089         local entity tep;
1090         local float r;
1091         org_x = cvar("saved1");
1092         org_y = cvar("saved2");
1093         org_z = cvar("saved3");
1094
1095         tep = make_waypoint(org);
1096
1097         r = cvar("saved4");
1098
1099         tep.b_aiflags = floor(r / 4);
1100         tep.b_pants = cvar("scratch1");
1101         tep.b_skill = cvar("scratch2");
1102         tep.b_shirt = cvar("scratch3");
1103         tep.b_frags = cvar("scratch4");
1104 };
1105
1106 void() bot_return =
1107 {
1108         if (time > 2)
1109         {
1110                 if ((waypoint_mode == WM_DYNAMIC) || (waypoint_mode == WM_LOADED))
1111                 {
1112                         // minor precaution
1113
1114                         if (saved_bots & 1) BotConnect(0, 1, saved_skills1 & 3);
1115                         if (saved_bots & 2) BotConnect(0, 2, (saved_skills1 & 12) / 4);
1116                         if (saved_bots & 4) BotConnect(0, 3, (saved_skills1 & 48) / 16);
1117                         if (saved_bots & 8) BotConnect(0, 4, (saved_skills1 & 192) / 64);
1118                         if (saved_bots & 16) BotConnect(0, 5, (saved_skills1 & 768) / 256);
1119                         if (saved_bots & 32) BotConnect(0, 6, (saved_skills1 & 3072) / 1024);
1120                         if (saved_bots & 64) BotConnect(0, 7, (saved_skills1 & 12288) / 4096);
1121                         if (saved_bots & 128) BotConnect(0, 8, (saved_skills1 & 49152) / 16384);
1122                         if (saved_bots & 256) BotConnect(0, 9, saved_skills2 & 3);
1123                         if (saved_bots & 512) BotConnect(0, 10, (saved_skills2 & 12) / 4);
1124                         if (saved_bots & 1024) BotConnect(0, 11, (saved_skills2& 48) / 16);
1125                         if (saved_bots & 2048) BotConnect(0, 12, (saved_skills2 & 192) / 64);
1126                         if (saved_bots & 4096) BotConnect(0, 13, (saved_skills2 & 768) / 256);
1127                         if (saved_bots & 8192) BotConnect(0, 14, (saved_skills2 & 3072) / 1024);
1128                         if (saved_bots & 16384) BotConnect(0, 15, (saved_skills2 & 12288) / 4096);
1129                         if (saved_bots & 32768) BotConnect(0, 16, (saved_skills2 & 49152) / 16384);
1130                         saved_bots = 0;
1131                 }
1132         }
1133 };
1134
1135
1136 void() WaypointWatch =
1137 {
1138         // Waypoint Baywatch
1139         local float bigboobs;
1140         local string h;
1141
1142         if (max_clients < 2)
1143                 return;
1144         if (waypoint_mode != WM_UNINIT)
1145         {
1146                 bigboobs = cvar("saved4");
1147                 if (bigboobs != 0)
1148                 {
1149                         if ((bigboobs & 3) == 1)
1150                                 ClearAllWays();
1151                         else if ((bigboobs & 3) == 3)
1152                         {
1153                                 FixWaypoints();
1154                                 h = ftos(b_options);
1155                                 cvar_set("saved1", h);
1156                                 cvar_set("saved4", "0");
1157                                 cvar_set("scratch1", "0");
1158                                 waypoint_mode = WM_LOADED;
1159                                 return;
1160                         }
1161                         LoadWaypoint();
1162                         waypoint_mode = WM_LOADING;
1163                         cvar_set("saved4", "0");
1164                 }
1165         }
1166 };
1167 void() BotFrame =
1168 {
1169         local float num;
1170
1171         // for the sake of speed
1172         sv_maxairspeed = cvar("sv_maxairspeed");
1173         sv_maxspeed = cvar("sv_maxspeed");
1174         sv_gravity = cvar("sv_gravity");
1175         sv_friction = cvar("sv_friction");
1176         sv_accelerate = cvar("sv_accelerate");
1177         sv_stopspeed = cvar("sv_stopspeed");
1178         real_frametime = frametime; // in NQ this is alright
1179
1180         self = nextent(world);
1181         num = 0;
1182         while (num < max_clients)
1183         {
1184                 if (self.ishuman == FALSE)
1185                 {
1186                         if (active_clients & ClientBitFlag(num))
1187                         {
1188                                 frik_obstacles();
1189                                 CL_KeyMove();
1190                                 SV_ClientThink();
1191                                 SV_Physics_Client();
1192                         }
1193                 }
1194                 self = nextent(self);
1195                 num = num + 1;
1196         }
1197         WaypointWatch();
1198
1199         if (saved_bots)
1200                 bot_return();
1201 };
1202
1203 /*
1204 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
1205
1206 Bot Impulses. Allows the player to perform bot
1207 related functions.
1208
1209 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
1210 */
1211
1212 void() BotImpulses =
1213 {
1214 /*      local float f;
1215         if (self.impulse == 100)
1216         {
1217                 f = cvar("skill");
1218                 BotConnect(0, 0, f);
1219         }
1220         if (self.impulse == 101)
1221         {
1222                 f = cvar("skill");
1223                 BotConnect(1, 0, f);
1224         }
1225         else if (self.impulse == 102)
1226                 KickABot();
1227         else if (self.impulse == 103)
1228                 botcam_u();
1229         else if (self.impulse == 104)
1230                 bot_way_edit();
1231         else
1232                 return;
1233
1234         self.impulse = 0;
1235 */
1236  };
1237
1238
1239