]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/bot/havocbot/role_onslaught.qc
Merge branch 'sev/hud_nades' into 'master'
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / bot / havocbot / role_onslaught.qc
1 #include "../../_all.qh"
2
3 #include "havocbot.qh"
4
5 #include "../bot.qh"
6 #include "../navigation.qh"
7 #include "../waypoints.qh"
8
9 #include "../../mutators/mutators_include.qh"
10
11 #include "../../../common/teams.qh"
12
13 const int HAVOCBOT_ONS_ROLE_NONE                = 0;
14 const int HAVOCBOT_ONS_ROLE_DEFENSE     = 2;
15 const int HAVOCBOT_ONS_ROLE_ASSISTANT   = 4;
16 const int HAVOCBOT_ONS_ROLE_OFFENSE     = 8;
17
18 .int havocbot_role_flags;
19 .float havocbot_attack_time;
20
21 .void() havocbot_role;
22 .void() havocbot_previous_role;
23
24 void() havocbot_role_ons_defense;
25 void() havocbot_role_ons_offense;
26 void() havocbot_role_ons_assistant;
27
28 void(entity bot) havocbot_ons_reset_role;
29 void(float ratingscale, vector org, float sradius) havocbot_goalrating_items;
30 void(float ratingscale, vector org, float sradius) havocbot_goalrating_enemyplayers;
31
32 .float isshielded;
33 .float iscaptured;
34 .float islinked;
35 .float isgenneighbor_blue, iscpneighbor_blue;
36 .float isgenneighbor_red, iscpneighbor_red;
37
38 .entity havocbot_ons_target;
39
40 void havocbot_goalrating_ons_offenseitems(float ratingscale, vector org, float sradius)
41 {
42         entity head;
43         float t, i, c, needarmor = false, needweapons = false;
44
45         // Needs armor/health?
46         if(self.health<100)
47                 needarmor = true;
48
49         // Needs weapons?
50         c = 0;
51         for(i = WEP_FIRST; i <= WEP_LAST ; ++i)
52         {
53                 // Find weapon
54                 if(self.weapons & WepSet_FromWeapon(i))
55                 if(++c>=4)
56                         break;
57         }
58
59         if(c<4)
60                 needweapons = true;
61
62         if(!needweapons && !needarmor)
63                 return;
64
65 //      dprint(self.netname, " needs weapons ", ftos(needweapons) , "\n");
66 //      dprint(self.netname, " needs armor ", ftos(needarmor) , "\n");
67
68         // See what is around
69         head = findchainfloat(bot_pickup, true);
70         while (head)
71         {
72                 // gather health and armor only
73                 if (head.solid)
74                 if ( ((head.health || head.armorvalue) && needarmor) || (head.weapons && needweapons ) )
75                 if (vlen(head.origin - org) < sradius)
76                 {
77                         t = head.bot_pickupevalfunc(self, head);
78                         if (t > 0)
79                                 navigation_routerating(head, t * ratingscale, 500);
80                 }
81                 head = head.chain;
82         }
83 }
84
85 void havocbot_role_ons_setrole(entity bot, float role)
86 {
87         dprint(strcat(bot.netname," switched to "));
88         switch(role)
89         {
90                 case HAVOCBOT_ONS_ROLE_DEFENSE:
91                         dprint("defense");
92                         bot.havocbot_role = havocbot_role_ons_defense;
93                         bot.havocbot_role_flags = HAVOCBOT_ONS_ROLE_DEFENSE;
94                         bot.havocbot_role_timeout = 0;
95                         break;
96                 case HAVOCBOT_ONS_ROLE_ASSISTANT:
97                         dprint("assistant");
98                         bot.havocbot_role = havocbot_role_ons_assistant;
99                         bot.havocbot_role_flags = HAVOCBOT_ONS_ROLE_ASSISTANT;
100                         bot.havocbot_role_timeout = 0;
101                         break;
102                 case HAVOCBOT_ONS_ROLE_OFFENSE:
103                         dprint("offense");
104                         bot.havocbot_role = havocbot_role_ons_offense;
105                         bot.havocbot_role_flags = HAVOCBOT_ONS_ROLE_OFFENSE;
106                         bot.havocbot_role_timeout = 0;
107                         break;
108         }
109         dprint("\n");
110 }
111
112 float havocbot_ons_teamcount(entity bot, int role)
113 {
114         float c = 0;
115         entity head;
116
117         FOR_EACH_PLAYER(head)
118         if(head.team==self.team)
119         if(head.havocbot_role_flags & role)
120                 ++c;
121
122         return c;
123 }
124
125 void havocbot_goalrating_ons_controlpoints_attack(float ratingscale)
126 {
127         entity cp, cp1, cp2, best, pl, wp;
128         float radius, found, bestvalue, c;
129
130         cp1 = cp2 = findchain(classname, "onslaught_controlpoint");
131
132         // Filter control points
133         for (; cp2; cp2 = cp2.chain)
134         {
135                 cp2.wpcost = c = 0;
136                 cp2.wpconsidered = false;
137
138                 if(cp2.isshielded)
139                         continue;
140
141                 // Ignore owned controlpoints
142                 if(self.team == NUM_TEAM_1)
143                 {
144                         if( (cp2.isgenneighbor_blue || cp2.iscpneighbor_blue) && !(cp2.isgenneighbor_red || cp2.iscpneighbor_red) )
145                                 continue;
146                 }
147                 else if(self.team == NUM_TEAM_2)
148                 {
149                         if( (cp2.isgenneighbor_red || cp2.iscpneighbor_red) && !(cp2.isgenneighbor_blue || cp2.iscpneighbor_blue) )
150                                 continue;
151                 }
152
153                 // Count team mates interested in this control point
154                 // (easier and cleaner than keeping counters per cp and teams)
155                 FOR_EACH_PLAYER(pl)
156                 if(pl.team==self.team)
157                 if(pl.havocbot_role_flags & HAVOCBOT_ONS_ROLE_OFFENSE)
158                 if(pl.havocbot_ons_target==cp2)
159                         ++c;
160
161                 // NOTE: probably decrease the cost of attackable control points
162                 cp2.wpcost = c;
163                 cp2.wpconsidered = true;
164         }
165
166         // We'll consider only the best case
167         bestvalue = 99999999999;
168         cp = world;
169         for (; cp1; cp1 = cp1.chain)
170         {
171                 if (!cp1.wpconsidered)
172                         continue;
173
174                 if(cp1.wpcost<bestvalue)
175                 {
176                         bestvalue = cp1.wpcost;
177                         cp = cp1;
178                         self.havocbot_ons_target = cp1;
179                 }
180         }
181
182         if (!cp)
183                 return;
184
185 //      dprint(self.netname, " chose cp ranked ", ftos(bestvalue), "\n");
186
187         if(cp.goalentity)
188         {
189                 // Should be attacked
190                 // Rate waypoints near it
191                 found = false;
192                 best = world;
193                 bestvalue = 99999999999;
194                 for(radius=0; radius<1000 && !found; radius+=500)
195                 {
196                         for(wp=findradius(cp.origin,radius); wp; wp=wp.chain)
197                         {
198                                 if(!(wp.wpflags & WAYPOINTFLAG_GENERATED))
199                                 if(wp.classname=="waypoint")
200                                 if(checkpvs(wp.origin,cp))
201                                 {
202                                         found = true;
203                                         if(wp.cnt<bestvalue)
204                                         {
205                                                 best = wp;
206                                                 bestvalue = wp.cnt;
207                                         }
208                                 }
209                         }
210                 }
211
212                 if(best)
213                 {
214                         navigation_routerating(best, ratingscale, 10000);
215                         best.cnt += 1;
216
217                         self.havocbot_attack_time = 0;
218                         if(checkpvs(self.view_ofs,cp))
219                         if(checkpvs(self.view_ofs,best))
220                                 self.havocbot_attack_time = time + 2;
221                 }
222                 else
223                 {
224                         navigation_routerating(cp, ratingscale, 10000);
225                 }
226         //      dprint(self.netname, " found an attackable controlpoint at ", vtos(cp.origin) ,"\n");
227         }
228         else
229         {
230                 // Should be touched
231                 // dprint(self.netname, " found a touchable controlpoint at ", vtos(cp.origin) ,"\n");
232                 found = false;
233
234                 // Look for auto generated waypoint
235                 if (!bot_waypoints_for_items)
236                 for (wp = findradius(cp.origin,100); wp; wp = wp.chain)
237                 {
238                         if(wp.classname=="waypoint")
239                         {
240                                 navigation_routerating(wp, ratingscale, 10000);
241                                 found = true;
242                         }
243                 }
244
245                 // Nothing found, rate the controlpoint itself
246                 if (!found)
247                         navigation_routerating(cp, ratingscale, 10000);
248         }
249 }
250
251 float havocbot_goalrating_ons_generator_attack(float ratingscale)
252 {
253         entity g, wp, bestwp;
254         float found, best;
255
256         for (g = findchain(classname, "onslaught_generator"); g; g = g.chain)
257         {
258                 if(g.team == self.team || g.isshielded)
259                         continue;
260
261                 // Should be attacked
262                 // Rate waypoints near it
263                 found = false;
264                 bestwp = world;
265                 best = 99999999999;
266
267                 for(wp=findradius(g.origin,400); wp; wp=wp.chain)
268                 {
269                         if(wp.classname=="waypoint")
270                         if(checkpvs(wp.origin,g))
271                         {
272                                 found = true;
273                                 if(wp.cnt<best)
274                                 {
275                                         bestwp = wp;
276                                         best = wp.cnt;
277                                 }
278                         }
279                 }
280
281                 if(bestwp)
282                 {
283                 //      dprint("waypoints found around generator\n");
284                         navigation_routerating(bestwp, ratingscale, 10000);
285                         bestwp.cnt += 1;
286
287                         self.havocbot_attack_time = 0;
288                         if(checkpvs(self.view_ofs,g))
289                         if(checkpvs(self.view_ofs,bestwp))
290                                 self.havocbot_attack_time = time + 5;
291
292                         return true;
293                 }
294                 else
295                 {
296                 //      dprint("generator found without waypoints around\n");
297                         // if there aren't waypoints near the generator go straight to it
298                         navigation_routerating(g, ratingscale, 10000);
299                         self.havocbot_attack_time = 0;
300                         return true;
301                 }
302         }
303         return false;
304 }
305
306 void havocbot_role_ons_offense()
307 {
308         if(self.deadflag != DEAD_NO)
309         {
310                 self.havocbot_attack_time = 0;
311                 havocbot_ons_reset_role(self);
312                 return;
313         }
314
315         // Set the role timeout if necessary
316         if (!self.havocbot_role_timeout)
317                 self.havocbot_role_timeout = time + 120;
318
319         if (time > self.havocbot_role_timeout)
320         {
321                 havocbot_ons_reset_role(self);
322                 return;
323         }
324
325         if(self.havocbot_attack_time>time)
326                 return;
327
328         if (self.bot_strategytime < time)
329         {
330                 navigation_goalrating_start();
331                 havocbot_goalrating_enemyplayers(20000, self.origin, 650);
332                 if(!havocbot_goalrating_ons_generator_attack(20000))
333                         havocbot_goalrating_ons_controlpoints_attack(20000);
334                 havocbot_goalrating_ons_offenseitems(10000, self.origin, 10000);
335                 navigation_goalrating_end();
336
337                 self.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
338         }
339 }
340
341 void havocbot_role_ons_assistant()
342 {
343         havocbot_ons_reset_role(self);
344 }
345
346 void havocbot_role_ons_defense()
347 {
348         havocbot_ons_reset_role(self);
349 }
350
351 void havocbot_ons_reset_role(entity bot)
352 {
353         entity head;
354         float c;
355
356         if(self.deadflag != DEAD_NO)
357                 return;
358
359         bot.havocbot_ons_target = world;
360
361         // TODO: Defend control points or generator if necessary
362
363         // if there is only me on the team switch to offense
364         c = 0;
365         FOR_EACH_PLAYER(head)
366         if(head.team==self.team)
367                 ++c;
368
369         if(c==1)
370         {
371                 havocbot_role_ons_setrole(bot, HAVOCBOT_ONS_ROLE_OFFENSE);
372                 return;
373         }
374
375         havocbot_role_ons_setrole(bot, HAVOCBOT_ONS_ROLE_OFFENSE);
376 }
377
378 void havocbot_chooserole_ons()
379 {
380         havocbot_ons_reset_role(self);
381 }