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