]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/gamec/mauvebot.c
new gamemode: "Last Man Standing" (g_lms)
[divverent/nexuiz.git] / data / qcsrc / gamec / mauvebot.c
1 /*
2 MauveBot v1.0 for Nexuiz
3 */
4
5 float intermission_running;
6
7 .float skill_level;
8
9 .float ai_time;
10 .float threat;
11 .entity dodgeent;
12
13 float THREAT_UNFLAGGED = 0;
14 float THREAT_IGNORE = -1;
15
16 float DODGE_DIST = 500;
17 float SEARCH_DIST = 1000;
18
19 .float search_time;
20
21 float bot_number;
22
23 string(float r) BotName =
24 {
25         if (r == 1)
26         {
27                 self.playermodel = "models/player/visitant.zym";
28                 self.playerskin = "0";
29                 return "Visitant";
30         }
31         else if (r == 2)
32         {
33                 self.playermodel = "models/player/marine.zym";
34                 self.playerskin = "0";
35                 return "Marine";
36         }
37         else if (r == 3)
38         {
39                 self.playermodel = "models/player/specop.zym";
40                 self.playerskin = "0";
41                 return "Specop";
42         }
43         else if (r == 4)
44         {
45                 self.playermodel = "models/player/nexus.zym";
46                 self.playerskin = "0";
47                 return "Nexus";
48         }
49         else if (r == 5)
50         {
51                 self.playermodel = "models/player/skadi.zym";
52                 self.playerskin = "0";
53                 return "Skadi";
54         }
55         else if (r == 6)
56         {
57                 self.playermodel = "models/player/lurk.zym";
58                 self.playerskin = "0";
59                 return "Lurk";
60         }
61         else if (r == 7)
62         {
63                 self.playermodel = "models/player/headhunter.zym";
64                 self.playerskin = "0";
65                 return "HeadHunter";
66         }
67         else if (r == 8)
68         {
69                 self.playermodel = "models/player/carni.zym";
70                 self.playerskin = "0";
71                 return "Carni";
72         }
73         else if (r == 9)
74         {
75                 self.playermodel = "models/player/grunt.zym";
76                 self.playerskin = "0";
77                 return "Grunt";
78         }
79         else if (r == 10)
80         {
81                 self.playermodel = "models/player/insurrectionist.zym";
82                 self.playerskin = "0";
83                 return "Insurrectionist";
84         }
85         else if (r == 11)
86         {
87                 self.playermodel = "models/player/jeandarc.zym";
88                 self.playerskin = "0";
89                 return "Jeandarc";
90         }
91         else if (r == 12)
92         {
93                 self.playermodel = "models/player/lycanthrope.zym";
94                 self.playerskin = "0";
95                 return "Lycanthrope";
96         }
97         else if (r == 13)
98         {
99                 self.playermodel = "models/player/pyria.zym";
100                 self.playerskin = "0";
101                 return "Pyria";
102         }
103         else if (r == 14)
104         {
105                 self.playermodel = "models/player/shock.zym";
106                 self.playerskin = "0";
107                 return "Shock";
108         }
109         else if (r == 15)
110         {
111                 self.playermodel = "models/player/marine.zym";
112                 self.playerskin = "1";
113                 return "Private";
114         }
115         else if (r == 16)
116         {
117                 self.playermodel = "models/player/nexus.zym";
118                 self.playerskin = "1";
119                 return "Mulder";
120         }
121         else if (r == 17)
122         {
123                 self.playermodel = "models/player/skadi.zym";
124                 self.playerskin = "1";
125                 return "Elite";
126         }
127         else if (r == 18)
128         {
129                 self.playermodel = "models/player/lurk.zym";
130                 self.playerskin = "1";
131                 return "Reptile";
132         }
133         else if (r == 19)
134         {
135                 self.playermodel = "models/player/crash.zym";
136                 self.playerskin = "2";
137                 return "mechanical";
138         }
139         else if (r == 20)
140         {
141                 self.playermodel = "models/player/crash.zym";
142                 self.playerskin = "1";
143                 return "Quark";
144         }
145         else if (r == 21)
146         {
147                 self.playermodel = "models/player/insurrectionist.zym";
148                 self.playerskin = "1";
149                 return "Anarchist";
150         }
151         else if (r == 22)
152         {
153                 self.playermodel = "models/player/jeandarc.zym";
154                 self.playerskin = "1";
155                 return "Heroine";
156         }
157         else if (r == 23)
158         {
159                 self.playermodel = "models/player/lycanthrope.zym";
160                 self.playerskin = "1";
161                 return "Wolf";
162         }
163         else if (r == 24)
164         {
165                 self.playermodel = "models/player/pyria.zym";
166                 self.playerskin = "1";
167                 return "Soldier";
168         }
169         else if (r == 25)
170         {
171                 self.playermodel = "models/player/shock.zym";
172                 self.playerskin = "1";
173                 return "Rebel";
174         }
175         else
176         {
177                 self.playermodel = "models/player/crash.zym";
178                 self.playerskin = "0";
179                 return "Crash";
180         }
181
182 };
183
184 string () PickARandomName =
185 {
186         local float y, test;
187         local string h;
188         local entity t;
189
190         t = find(world, classname, "player");
191         while(t)
192         {
193                 if (clienttype(self) == CLIENTTYPE_BOT)
194                         y = y + 1;
195                 t = find(t, classname, "player");
196         }
197
198         if (y > 25)
199         {
200                 self.playermodel = "models/player/marine.zym";
201                 self.playerskin = "0";
202                 return "EJP_Tank";
203         }
204
205         y = TRUE;
206         while(y)
207         {
208                 test = ceil(random() * 25);
209                 h = BotName(test);
210                 t = find(world, netname, h);
211                 if (t == world)
212                         y = FALSE;
213         }
214         return h;
215 };
216
217 // Adds a bot to the server
218 void() add_MauveBot =
219 {
220         local entity oldself;
221         local float flo;
222         local string str;
223
224         oldself = self;
225         self = spawnclient();
226         if (!self)
227         {
228                 bprint("Can not add bot, server full.\n");
229                 str = ftos(bot_number);
230                 cvar_set("bot_number", str);
231                 self = oldself;
232                 return;
233         }
234         
235         flo = floor(random() * 5);
236         if (flo == 0)
237                 self.clientcolors = 0;
238         else if (flo == 1)
239                 self.clientcolors = 3;
240         else if (flo == 2)
241                 self.clientcolors = 4;
242         else if (flo == 3)
243                 self.clientcolors = 12;
244         else if (flo == 4)
245                 self.clientcolors = 13;
246         self.clientcolors = self.clientcolors + self.clientcolors * 16;
247
248         self.netname = PickARandomName();
249
250         ClientConnect();
251         self.classname = "player";
252         PutClientInServer();
253         self.autoswitch = 1; // bots currently need autoswitching
254         self.skill_level = cvar("skill");
255         if (self.skill_level > 10)
256                 self.skill_level = 10;
257         if (self.skill_level < 1)
258                 self.skill_level = 1;
259         self = oldself;
260
261         bot_number = bot_number + 1;
262 };
263
264 void() remove_MauveBot = 
265 {
266         local float flo, i;
267         local entity ent;
268
269         flo = floor(random() * bot_number);
270         
271         ent = find(world, classname, "player");
272         
273         while(ent)
274         {
275                 if (clienttype(ent) == CLIENTTYPE_BOT)
276                 {
277                         if (flo == i)
278                         {
279                                 dropclient(ent);
280                                 // decrease player count for lms
281                                 player_count -= 1;
282                                 // bot was dead, so decrease dead count too
283                                 if(cvar("g_lms") && self.frags < 1)
284                                         lms_dead_count -= 1;
285                                 if (bot_number > 0)
286                                         bot_number = bot_number - 1;
287                                 return;
288                         }
289                         i = i + 1;
290                 }
291                 ent = find(ent, classname, "player");
292         }
293 };
294
295 float (entity targ) visible =
296 {
297         local vector start, end, org;
298
299 //      if (self.skill_level < 3)
300 //              if (random() > self.skill_level / 10)
301 //                      return FALSE;
302
303         org = targ.origin;
304         if (org == '0 0 0')
305                 org = (targ.absmin + targ.absmax) * 0.5;
306
307         start = self.origin + self.view_ofs;
308         end = org;
309         traceline (start, end, TRUE, self);     
310         if (!(trace_inopen && trace_inwater))   
311                 if (trace_fraction == 1)
312                         return TRUE;
313         return FALSE;
314 };
315
316 void(entity ent) bot_set_angle =
317 {
318         local vector view;
319         local float flo;
320         if (ent != world)
321         {
322                 view = ent.origin;
323                 if (view == '0 0 0')
324                         view = (ent.absmin + ent.absmax) * 0.5;
325                 
326                 if (self.skill_level < 6)
327                         view = view - ent.velocity * (0.5 / self.skill_level);
328
329                 flo = vlen(ent.origin - self.origin) / (self.skill_level * 4);
330
331                 view_x = view_x + (random() * flo * 2) - flo;
332                 view_y = view_y + (random() * flo * 2) - flo;
333                 view_z = view_z + (random() * flo * 2) - flo;
334
335                 view = view - (self.origin + self.view_ofs);
336                 self.v_angle = vectoangles(view);
337                 self.v_angle_x = self.v_angle_x * -1;
338                 self.angles = self.v_angle;
339                 self.angles_x = self.angles_x * -1;
340                 if (self.angles_x > 180)
341                         self.angles_x = self.angles_x - 360;
342                 if (self.angles_x != 0)
343                         self.angles_x = self.angles_x / 6;
344         }
345         else
346                 self.angles_x = 0;// hack!
347 };
348
349 void() MauveToGoal =
350 {
351         local vector vec, en, diff, dir;
352
353         vec = self.origin;
354         movetogoal(20);
355         en = self.origin;
356         self.origin = vec;
357
358         diff = en - self.origin;
359         if (diff == '0 0 0')
360                 diff = self.goalentity.origin - self.origin;
361
362         if (self.search_time < time)
363         {
364                 self.goalentity.search_time = time + 20;
365                 self.goalentity = world;
366                 return;
367         }       
368
369         dir = normalize(diff);
370         dir = dir * cvar("sv_maxspeed");// - (self.velocity - dir * (self.velocity * dir));
371
372         makevectors(self.v_angle);
373         self.movement_x = dir * v_forward;
374         self.movement_y = dir * v_right;
375         self.movement_z = dir * v_up;
376 };
377
378 void() look_for_stuff =
379 {
380         local entity item, best, best2, best3;
381         local float dist, tdist, dist2, topthreat;
382         
383         item = findradius(self.origin, 1000);
384         
385         if (self.search_time < time)
386                 self.enemy = world;
387
388         if (self.enemy.health < 0)
389                 self.enemy = world;
390
391         if (self.enemy.deadflag)
392                 self.enemy = world;
393         
394         dist = dist2 = SEARCH_DIST;
395         topthreat = 0;
396
397         while(item)
398         {
399                 if (item.flags & FL_CLIENT)
400                 {
401                         if (item != self && !(cvar("teamplay") && (item.clientcolors == self.clientcolors)))
402                         {
403                                 if ((item.health > 0) && (!item.deadflag))
404                                 {
405                                         if (visible(item))
406                                         {
407                                                 tdist = vlen(item.origin - self.origin);
408                                                 if (tdist < dist)
409                                                 {
410                                                         best = item;
411                                                         dist = tdist;
412                                                 }
413                                         }
414                                 }
415                         }
416                 }
417                 else if (( (item.flags & FL_ITEM) && item.model != string_null) && (item.search_time < time))
418                 {
419                         if (visible(item))
420                         {
421                                 tdist = vlen(item.origin - self.origin);
422                                 if (tdist < dist2)
423                                 {
424                                         best2 = item;
425                                         dist2 = tdist;
426                                 }
427                         }
428                 }
429                 if (item.threat == THREAT_UNFLAGGED)
430                 {
431                         if (item.classname == "missile")
432                                 item.threat = 100;
433                         else if (item.classname == "laserbolt") 
434                                 item.threat = 10;
435                         else if (item.classname == "grenade") 
436                                 item.threat = 50;
437                         else if (item.classname == "plasma") 
438                                 item.threat = 60;
439                         else if (item.classname == "spike")
440                                 item.threat = 30;
441                         else
442                                 item.threat = THREAT_IGNORE;
443                 }
444                 if (item.threat > 0)
445                 {
446                         if ((item.owner != self) || (item.velocity == '0 0 0'))
447                         {
448                                 tdist = vlen(item.origin - self.origin);
449                                 if (tdist < DODGE_DIST)
450                                 {
451                                         if (item.threat / tdist > topthreat)
452                                         {
453                                                 topthreat = item.threat / tdist;
454                                                 best3 = item;
455                                         }
456                                 }
457                         }
458                 }
459                 item = item.chain;
460         }
461         if ((best != self.enemy) && (best != world))
462                 self.enemy = best;
463         if ((best2 != self.goalentity) && (best2 != world))
464         {
465                 self.goalentity = best2;
466                 self.search_time = time + 5;
467         }
468         self.dodgeent = best3;
469 };
470
471 void() DodgeProjectile =
472 {
473         local vector vec;
474
475         vec = vectoangles(self.dodgeent.origin - self.origin);
476         makevectors(vec);
477         
478         self.goalentity = spawn();
479
480         if (self.dodgeent.velocity != '0 0 0')
481                 setorigin(self.goalentity, self.origin + v_right * 50);
482         else
483                 setorigin(self.goalentity, self.origin - v_forward * 50);
484         
485         MauveToGoal();
486
487         remove(self.goalentity);
488         self.goalentity = world;
489 };
490
491 void() MauveBot_AI =
492 {
493         
494         if (clienttype(self) != CLIENTTYPE_BOT)
495         {
496                 local float flo;
497
498                 if (time >= 3)
499                 {
500                         float botnumbercvar;
501                         botnumbercvar = cvar("bot_number");
502                         flo = rint(botnumbercvar);
503                         if(flo != botnumbercvar)
504                                 cvar_set("bot_number", ftos(flo));
505         
506                         if (flo > bot_number)
507                                 add_MauveBot();
508                         else if (flo < bot_number)
509                                 remove_MauveBot();
510                 }
511                 else
512                         remove_MauveBot();
513                 return;
514         }
515
516         self.button0 = 0;
517         self.button2 = 0;
518         self.button3 = 0;
519
520         self.movement = '0 0 0';
521
522         if (self.deadflag == DEAD_RESPAWNABLE)
523         {
524                 self.button0 = 1;
525                 return;
526         }
527         else if (self.deadflag)
528                 return;
529
530         if (self.goalentity.model == string_null)
531                 self.goalentity = world;
532
533         if (time > self.ai_time)
534         {
535                 self.ai_time = time + (0.5 / self.skill_level) + (random() * (1 / self.skill_level));
536
537                 look_for_stuff();
538         
539                 if (self.enemy)
540                 {
541                         bot_set_angle(self.enemy);
542                         if (visible(self.enemy))
543                         {
544                                 self.button0 = 1;
545                                 self.search_time = 100000;
546                         }
547                         else    if (self.search_time == 100000)
548                                 self.search_time = time + 5;
549                         if (self.goalentity == world)
550                         {
551                                 if (self.enemy)
552                                 {
553                                         self.goalentity = self.enemy;
554                                         if (vlen(self.enemy.origin - self.origin) < 300)
555                                                 self.dodgeent = self.enemy;
556                                 }
557                         }
558                 }
559                 else if (self.goalentity)
560                         bot_set_angle(self.goalentity);
561
562                 if (random() < 0.03)
563                         self.button2 = 1;
564         }
565         else
566         {
567                 if (self.skill_level > 3)
568                 {
569                         if (visible(self.enemy))
570                         {
571                                 self.button0 = 1;
572                                 self.search_time = 100000;
573                         }
574                 }
575         }
576
577         if (self.dodgeent != world)
578                 DodgeProjectile();
579         else if (self.goalentity != world)
580                 MauveToGoal();
581 };