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