]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/gamec/mauvebot.c
fixed some bugs with scoretransfer on teamchange
[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 = "1";
29                 return "Fricka";
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/lycanthrope.zym";
88                 self.playerskin = "0";
89                 return "Lycanthrope";
90         }
91         else if (r == 12)
92         {
93                 self.playermodel = "models/player/pyria.zym";
94                 self.playerskin = "0";
95                 return "Pyria";
96         }
97         else if (r == 13)
98         {
99                 self.playermodel = "models/player/shock.zym";
100                 self.playerskin = "0";
101                 return "Shock";
102         }
103         else if (r == 14)
104         {
105                 self.playermodel = "models/player/lurk.zym";
106                 self.playerskin = "1";
107                 return "Reptile";
108         }
109         else if (r == 15)
110         {
111                 self.playermodel = "models/player/crash.zym";
112                 self.playerskin = "0";
113                 return "Quark";
114         }
115         else if (r == 16)
116         {
117                 self.playermodel = "models/player/insurrectionist.zym";
118                 self.playerskin = "1";
119                 return "Anarchist";
120         }
121         else if (r == 17)
122         {
123                 self.playermodel = "models/player/carni.zym";
124                 self.playerskin = "1";
125                 return "Armored Carni";
126         }
127         else
128         {
129                 self.playermodel = "models/player/carni.zym";
130                 self.playerskin = "0";
131                 return "Carni";
132         }
133 };
134
135 string () PickARandomName =
136 {
137         local float y, test;
138         local string h;
139         local entity t;
140
141         t = find(world, classname, "player");
142         while(t)
143         {
144                 if (clienttype(self) == CLIENTTYPE_BOT)
145                         y = y + 1;
146                 t = find(t, classname, "player");
147         }
148
149         if (y > 17)
150         {
151                 self.playermodel = "models/player/marine.zym";
152                 self.playerskin = "0";
153                 return "EJP_Tank";
154         }
155
156         y = TRUE;
157         while(y)
158         {
159                 test = ceil(random() * 17);
160                 h = BotName(test);
161                 t = find(world, netname, h);
162                 if (t == world)
163                         y = FALSE;
164         }
165         return h;
166 };
167
168 // Adds a bot to the server
169 void() add_MauveBot =
170 {
171         local entity oldself;
172         local float flo;
173         local string str;
174
175         oldself = self;
176         self = spawnclient();
177         if (!self)
178         {
179                 bprint("Can not add bot, server full.\n");
180                 str = ftos(bot_number);
181                 cvar_set("bot_number", str);
182                 self = oldself;
183                 return;
184         }
185
186         if(teamplay)
187         {
188                 // keep teams balanced
189                 JoinBestTeam(self, 0);
190         }
191         else
192         {
193                 // MORE COLORS!!!!! :P
194                 flo = floor(random() * 15);
195                 self.clientcolors = flo + flo * 16;
196         }
197
198         self.netname = PickARandomName();
199  
200         ClientConnect();
201         self.classname = "player";
202         PutClientInServer();
203         self.autoswitch = 1; // bots currently need autoswitching
204         self.skill_level = cvar("skill");
205         if (self.skill_level > 10)
206                 self.skill_level = 10;
207         if (self.skill_level < 1)
208                 self.skill_level = 1;
209         self = oldself;
210
211         bot_number = bot_number + 1;
212 };
213
214 void() remove_MauveBot = 
215 {
216         local float flo, i;
217         local entity ent;
218
219         flo = floor(random() * bot_number);
220         
221         ent = find(world, classname, "player");
222         
223         while(ent)
224         {
225                 if (clienttype(ent) == CLIENTTYPE_BOT)
226                 {
227                         if (flo == i)
228                         {
229                                 dropclient(ent);
230
231                                 // bot was dead, so decrease dead count
232                                 if(cvar("g_lms") && self.frags < 1)
233                                         lms_dead_count -= 1;
234                                 if (bot_number > 0)
235                                         bot_number = bot_number - 1;
236                                 return;
237                         }
238                         i = i + 1;
239                 }
240                 ent = find(ent, classname, "player");
241         }
242 };
243
244 float (entity targ) visible =
245 {
246         local vector start, end, org;
247
248 //      if (self.skill_level < 3)
249 //              if (random() > self.skill_level / 10)
250 //                      return FALSE;
251
252         org = targ.origin;
253         if (org == '0 0 0')
254                 org = (targ.absmin + targ.absmax) * 0.5;
255
256         start = self.origin + self.view_ofs;
257         end = org;
258         traceline (start, end, TRUE, self);     
259         if (!(trace_inopen && trace_inwater))   
260                 if (trace_fraction == 1)
261                         return TRUE;
262         return FALSE;
263 };
264
265 void(entity ent) bot_set_angle =
266 {
267         local vector view;
268         local float flo;
269         if (ent != world)
270         {
271                 view = ent.origin;
272                 if (view == '0 0 0')
273                         view = (ent.absmin + ent.absmax) * 0.5;
274                 
275                 if (self.skill_level < 6)
276                         view = view - ent.velocity * (0.5 / self.skill_level);
277
278                 flo = vlen(ent.origin - self.origin) / (self.skill_level * 4);
279
280                 view_x = view_x + (random() * flo * 2) - flo;
281                 view_y = view_y + (random() * flo * 2) - flo;
282                 view_z = view_z + (random() * flo * 2) - flo;
283
284                 view = view - (self.origin + self.view_ofs);
285                 self.v_angle = vectoangles(view);
286                 self.v_angle_x = self.v_angle_x * -1;
287                 self.angles = self.v_angle;
288                 self.angles_x = self.angles_x * -1;
289                 if (self.angles_x > 180)
290                         self.angles_x = self.angles_x - 360;
291                 if (self.angles_x != 0)
292                         self.angles_x = self.angles_x / 6;
293         }
294         else
295                 self.angles_x = 0;// hack!
296 };
297
298 void() MauveToGoal =
299 {
300         local vector vec, en, diff, dir;
301
302         vec = self.origin;
303         movetogoal(20);
304         en = self.origin;
305         self.origin = vec;
306
307         diff = en - self.origin;
308         if (diff == '0 0 0')
309                 diff = self.goalentity.origin - self.origin;
310
311         if (self.search_time < time)
312         {
313                 self.goalentity.search_time = time + 20;
314                 self.goalentity = world;
315                 return;
316         }       
317
318         dir = normalize(diff);
319         dir = dir * cvar("sv_maxspeed");// - (self.velocity - dir * (self.velocity * dir));
320
321         makevectors(self.v_angle);
322         self.movement_x = dir * v_forward;
323         self.movement_y = dir * v_right;
324         self.movement_z = dir * v_up;
325 };
326
327 void() look_for_stuff =
328 {
329         local entity item, best, best2, best3;
330         local float dist, tdist, dist2, topthreat;
331         
332         item = findradius(self.origin, 1000);
333         
334         if (self.search_time < time)
335                 self.enemy = world;
336
337         if (self.enemy.health < 0)
338                 self.enemy = world;
339
340         if (self.enemy.deadflag)
341                 self.enemy = world;
342         
343         dist = dist2 = SEARCH_DIST;
344         topthreat = 0;
345
346         while(item)
347         {
348                 if (item.flags & FL_CLIENT)
349                 {
350                         if (item != self && !(cvar("teamplay") && (item.clientcolors == self.clientcolors)))
351                         {
352                                 if ((item.health > 0) && (!item.deadflag))
353                                 {
354                                         if (visible(item))
355                                         {
356                                                 tdist = vlen(item.origin - self.origin);
357                                                 if (tdist < dist)
358                                                 {
359                                                         best = item;
360                                                         dist = tdist;
361                                                 }
362                                         }
363                                 }
364                         }
365                 }
366                 else if (( (item.flags & FL_ITEM) && item.model != string_null) && (item.search_time < time))
367                 {
368                         if (visible(item))
369                         {
370                                 tdist = vlen(item.origin - self.origin);
371                                 if (tdist < dist2)
372                                 {
373                                         best2 = item;
374                                         dist2 = tdist;
375                                 }
376                         }
377                 }
378                 if (item.threat == THREAT_UNFLAGGED)
379                 {
380                         if (item.classname == "missile")
381                                 item.threat = 100;
382                         else if (item.classname == "laserbolt") 
383                                 item.threat = 10;
384                         else if (item.classname == "grenade") 
385                                 item.threat = 50;
386                         else if (item.classname == "plasma") 
387                                 item.threat = 60;
388                         else if (item.classname == "spike")
389                                 item.threat = 30;
390                         else
391                                 item.threat = THREAT_IGNORE;
392                 }
393                 if (item.threat > 0)
394                 {
395                         if ((item.owner != self) || (item.velocity == '0 0 0'))
396                         {
397                                 tdist = vlen(item.origin - self.origin);
398                                 if (tdist < DODGE_DIST)
399                                 {
400                                         if (item.threat / tdist > topthreat)
401                                         {
402                                                 topthreat = item.threat / tdist;
403                                                 best3 = item;
404                                         }
405                                 }
406                         }
407                 }
408                 item = item.chain;
409         }
410         if ((best != self.enemy) && (best != world))
411                 self.enemy = best;
412         if ((best2 != self.goalentity) && (best2 != world))
413         {
414                 self.goalentity = best2;
415                 self.search_time = time + 5;
416         }
417         self.dodgeent = best3;
418 };
419
420 void() DodgeProjectile =
421 {
422         local vector vec;
423
424         vec = vectoangles(self.dodgeent.origin - self.origin);
425         makevectors(vec);
426         
427         self.goalentity = spawn();
428
429         if (self.dodgeent.velocity != '0 0 0')
430                 setorigin(self.goalentity, self.origin + v_right * 50);
431         else
432                 setorigin(self.goalentity, self.origin - v_forward * 50);
433         
434         MauveToGoal();
435
436         remove(self.goalentity);
437         self.goalentity = world;
438 };
439
440 void() MauveBot_AI =
441 {
442         
443         if (clienttype(self) != CLIENTTYPE_BOT)
444         {
445                 local float flo;
446
447                 if (time >= 3)
448                 {
449                         float botnumbercvar;
450                         botnumbercvar = cvar("bot_number");
451                         flo = rint(botnumbercvar);
452                         if(flo != botnumbercvar)
453                                 cvar_set("bot_number", ftos(flo));
454         
455                         if (flo > bot_number)
456                                 add_MauveBot();
457                         else if (flo < bot_number)
458                                 remove_MauveBot();
459                 }
460                 else
461                         remove_MauveBot();
462                 return;
463         }
464
465         self.button0 = 0;
466         self.button2 = 0;
467         self.button3 = 0;
468
469         self.movement = '0 0 0';
470
471         if (self.deadflag == DEAD_RESPAWNABLE)
472         {
473                 self.button0 = 1;
474                 return;
475         }
476         else if (self.deadflag)
477                 return;
478
479         if (self.goalentity.model == string_null)
480                 self.goalentity = world;
481
482         if (time > self.ai_time)
483         {
484                 self.ai_time = time + (0.5 / self.skill_level) + (random() * (1 / self.skill_level));
485
486                 look_for_stuff();
487         
488                 if (self.enemy)
489                 {
490                         bot_set_angle(self.enemy);
491                         if (visible(self.enemy))
492                         {
493                                 self.button0 = 1;
494                                 self.search_time = 100000;
495                         }
496                         else    if (self.search_time == 100000)
497                                 self.search_time = time + 5;
498                         if (self.goalentity == world)
499                         {
500                                 if (self.enemy)
501                                 {
502                                         self.goalentity = self.enemy;
503                                         if (vlen(self.enemy.origin - self.origin) < 300)
504                                                 self.dodgeent = self.enemy;
505                                 }
506                         }
507                 }
508                 else if (self.goalentity)
509                         bot_set_angle(self.goalentity);
510
511                 if (random() < 0.03)
512                         self.button2 = 1;
513         }
514         else
515         {
516                 if (self.skill_level > 3)
517                 {
518                         if (visible(self.enemy))
519                         {
520                                 self.button0 = 1;
521                                 self.search_time = 100000;
522                         }
523                 }
524         }
525
526         if (self.dodgeent != world)
527                 DodgeProjectile();
528         else if (self.goalentity != world)
529                 MauveToGoal();
530 };