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