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