Increase the maximum number of mutators
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / bot / default / havocbot / roles.qc
1 #include "roles.qh"
2
3 .float max_armorvalue;
4 .float havocbot_role_timeout;
5
6 .void() havocbot_previous_role;
7 .void() havocbot_role;
8
9 void havocbot_goalrating_items(float ratingscale, vector org, float sradius)
10 {SELFPARAM();
11         entity head;
12         entity player;
13         float rating, d, discard, distance, friend_distance, enemy_distance;
14         vector o;
15         ratingscale = ratingscale * 0.0001; // items are rated around 10000 already
16         head = findchainfloat(bot_pickup, true);
17
18         while (head)
19         {
20                 o = (head.absmin + head.absmax) * 0.5;
21                 distance = vlen(o - org);
22                 friend_distance = 10000; enemy_distance = 10000;
23                 rating = 0;
24
25                 if(!head.solid || distance > sradius || (head == self.ignoregoal && time < self.ignoregoaltime) )
26                 {
27                         head = head.chain;
28                         continue;
29                 }
30
31                 // Check if the item can be picked up safely
32                 if(head.classname == "droppedweapon")
33                 {
34                         traceline(o, o + '0 0 -1500', true, world);
35
36                         d = pointcontents(trace_endpos + '0 0 1');
37                         if(d & CONTENT_WATER || d & CONTENT_SLIME || d & CONTENT_LAVA)
38                         {
39                                 head = head.chain;
40                                 continue;
41                         }
42                         if(tracebox_hits_trigger_hurt(head.origin, head.mins, head.maxs, trace_endpos))
43                         {
44                                 head = head.chain;
45                                 continue;
46                         }
47                 }
48                 else
49                 {
50                         // Ignore items under water
51                         traceline(head.origin + head.maxs, head.origin + head.maxs, MOVE_NORMAL, head);
52                         if(trace_dpstartcontents & DPCONTENTS_LIQUIDSMASK)
53                         {
54                                 head = head.chain;
55                                 continue;
56                         }
57                 }
58
59                 if(teamplay)
60                 {
61                         discard = false;
62
63                         FOR_EACH_PLAYER(player)
64                         {
65
66                                 if ( self == player || player.deadflag )
67                                         continue;
68
69                                 d = vlen(player.origin - o); // distance between player and item
70
71                                 if ( player.team == self.team )
72                                 {
73                                         if ( !IS_REAL_CLIENT(player) || discard )
74                                                 continue;
75
76                                         if( d > friend_distance)
77                                                 continue;
78
79                                         friend_distance = d;
80
81                                         discard = true;
82
83                                         if( head.health && player.health > self.health )
84                                                 continue;
85
86                                         if( head.armorvalue && player.armorvalue > self.armorvalue)
87                                                 continue;
88
89                                         if( head.weapons )
90                                         if( head.weapons & ~player.weapons )
91                                                 continue;
92
93                                         if (head.ammo_shells && player.ammo_shells > self.ammo_shells)
94                                                 continue;
95
96                                         if (head.ammo_nails && player.ammo_nails > self.ammo_nails)
97                                                 continue;
98
99                                         if (head.ammo_rockets && player.ammo_rockets > self.ammo_rockets)
100                                                 continue;
101
102                                         if (head.ammo_cells && player.ammo_cells > self.ammo_cells)
103                                                 continue;
104
105                                         if (head.ammo_plasma && player.ammo_plasma > self.ammo_plasma)
106                                                 continue;
107
108                                         discard = false;
109                                 }
110                                 else
111                                 {
112                                         // If enemy only track distances
113                                         // TODO: track only if visible ?
114                                         if( d < enemy_distance )
115                                                 enemy_distance = d;
116                                 }
117                         }
118
119                         // Rate the item only if no one needs it, or if an enemy is closer to it
120                         if ( (enemy_distance < friend_distance && distance < enemy_distance) ||
121                                 (friend_distance > autocvar_bot_ai_friends_aware_pickup_radius ) || !discard )
122                                 rating = head.bot_pickupevalfunc(self, head);
123
124                 }
125                 else
126                         rating = head.bot_pickupevalfunc(self, head);
127
128                 if(rating > 0)
129                         navigation_routerating(head, rating * ratingscale, 2000);
130                 head = head.chain;
131         }
132 }
133
134 void havocbot_goalrating_controlpoints(float ratingscale, vector org, float sradius)
135 {SELFPARAM();
136         entity head;
137         head = findchain(classname, "dom_controlpoint");
138         while (head)
139         {
140                 if (vlen(( ( head.absmin + head.absmax ) * 0.5 ) - org) < sradius)
141                 {
142                         if(head.cnt > -1) // this is just being fought for
143                                 navigation_routerating(head, ratingscale, 5000);
144                         else if(head.goalentity.cnt == 0) // unclaimed point
145                                 navigation_routerating(head, ratingscale * 0.5, 5000);
146                         else if(head.goalentity.team != self.team) // other team's point
147                                 navigation_routerating(head, ratingscale * 0.2, 5000);
148                 }
149                 head = head.chain;
150         }
151 }
152
153 void havocbot_goalrating_enemyplayers(float ratingscale, vector org, float sradius)
154 {SELFPARAM();
155         entity head;
156         int t;
157         float distance;
158         noref bool noteam = ((self.team == 0) || !teamplay);
159
160         if (autocvar_bot_nofire)
161                 return;
162
163         // don't chase players if we're under water
164         if(self.waterlevel>WATERLEVEL_WETFEET)
165                 return;
166
167         FOR_EACH_PLAYER(head)
168         {
169                 // TODO: Merge this logic with the bot_shouldattack function
170                 if(bot_shouldattack(head))
171                 {
172                         distance = vlen(head.origin - org);
173                         if (distance < 100 || distance > sradius)
174                                 continue;
175
176                         // rate only visible enemies
177                         /*
178                         traceline(self.origin + self.view_ofs, head.origin, MOVE_NOMONSTERS, self);
179                         if (trace_fraction < 1 || trace_ent != head)
180                                 continue;
181                         */
182
183                         if((head.flags & FL_INWATER) || (head.flags & FL_PARTIALGROUND))
184                                 continue;
185
186                         // not falling
187                         if((head.flags & FL_ONGROUND) == 0)
188                         {
189                                 traceline(head.origin, head.origin + '0 0 -1500', true, world);
190                                 t = pointcontents(trace_endpos + '0 0 1');
191                                 if( t != CONTENT_SOLID )
192                                 if(t & CONTENT_WATER || t & CONTENT_SLIME || t & CONTENT_LAVA)
193                                         continue;
194                                 if(tracebox_hits_trigger_hurt(head.origin, head.mins, head.maxs, trace_endpos))
195                                         continue;
196                         }
197
198                         // TODO: rate waypoints near the targetted player at that moment, instead of the player itself
199                         //               adding a player as a goal seems to be quite dangerous, especially on space maps
200                         //               remove hack in navigation_poptouchedgoals() after performing this change
201
202                         t = (self.health + self.armorvalue ) / (head.health + head.armorvalue );
203                         navigation_routerating(head, t * ratingscale, 2000);
204                 }
205         }
206 }
207
208 // legacy bot role for standard gamemodes
209 // go to best items
210 void havocbot_role_generic()
211 {SELFPARAM();
212         if(self.deadflag != DEAD_NO)
213                 return;
214
215         if (self.bot_strategytime < time)
216         {
217                 self.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
218                 navigation_goalrating_start();
219                 havocbot_goalrating_items(10000, self.origin, 10000);
220                 havocbot_goalrating_enemyplayers(20000, self.origin, 10000);
221                 //havocbot_goalrating_waypoints(1, self.origin, 1000);
222                 navigation_goalrating_end();
223         }
224 }
225
226 void havocbot_chooserole_generic()
227 {SELFPARAM();
228         self.havocbot_role = havocbot_role_generic;
229 }
230
231 void havocbot_chooserole()
232 {SELFPARAM();
233         LOG_TRACE("choosing a role...\n");
234         self.bot_strategytime = 0;
235         if (MUTATOR_CALLHOOK(HavocBot_ChooseRole, self))
236                 return;
237         else if (g_keyhunt)
238                 havocbot_chooserole_kh();
239         else
240                 havocbot_chooserole_generic();
241 }