]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/assault.qc
Merge branch 'mand1nga/bot-fix-aiming-origins' into mand1nga/bot-assault
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / assault.qc
1 void spawnfunc_func_breakable();
2 void target_objective_decrease_activate();
3 .entity assault_decreaser;
4 .entity assault_sprite;
5
6 void spawnfunc_info_player_attacker() {
7         if(!g_assault)
8         {
9                 remove(self);
10                 return;
11         }
12         self.team = COLOR_TEAM1; // red, gets swapped every round
13         spawnfunc_info_player_deathmatch();
14 }
15
16 void spawnfunc_info_player_defender() {
17         if(!g_assault)
18         {
19                 remove(self);
20                 return;
21         }
22         self.team = COLOR_TEAM2; // blue, gets swapped every round
23         spawnfunc_info_player_deathmatch();
24 }
25
26 // reset this objective. Used when spawning an objective
27 // and when a new round starts
28 void assault_objective_reset() {
29         self.health = ASSAULT_VALUE_INACTIVE;
30 }
31
32 void assault_objective_use() {
33         // activate objective
34         self.health = 100;
35         //print("^2Activated objective ", self.targetname, "=", etos(self), "\n");
36         //print("Activator is ", activator.classname, "\n");
37
38         entity oldself;
39         oldself = self;
40
41         for(self = world; (self = find(self, target, oldself.targetname)); )
42         {
43                 if(self.classname == "target_objective_decrease")
44                         target_objective_decrease_activate();
45         }
46
47         self = oldself;
48 }
49
50 void spawnfunc_target_objective() {
51         if(!g_assault)
52         {
53                 remove(self);
54                 return;
55         }
56         self.classname = "target_objective";
57         self.use = assault_objective_use;
58         assault_objective_reset();
59         self.reset = assault_objective_reset;
60 }
61
62
63 // decrease the health of targeted objectives
64 void assault_objective_decrease_use() {
65         if(activator.team != assault_attacker_team) {
66                 // wrong team triggered decrease
67                 return;
68         }
69
70         if(other.assault_sprite)
71         {
72                 WaypointSprite_Disown(other.assault_sprite, waypointsprite_deadlifetime);
73                 if(other.classname == "func_assault_destructible")
74                         other.sprite = world;
75         }
76         else
77                 return; // already activated! cannot activate again!
78
79         if(self.enemy.health < ASSAULT_VALUE_INACTIVE)
80         {
81                 if(self.enemy.health - self.dmg > 0.5)
82                 {
83                         PlayerTeamScore_Add(activator, SP_SCORE, ST_SCORE, self.dmg);
84                         self.enemy.health = self.enemy.health - self.dmg;
85                 }
86                 else
87                 {
88                         PlayerTeamScore_Add(activator, SP_SCORE, ST_SCORE, self.enemy.health);
89                         PlayerTeamScore_Add(activator, SP_ASSAULT_OBJECTIVES, ST_ASSAULT_OBJECTIVES, 1);
90                         self.enemy.health = -1;
91
92                         entity oldself, oldactivator;
93
94                         oldself = self;
95                         self = oldself.enemy;
96                                 if(self.message)
97                                 {
98                                         entity player;
99                                         string s;
100                                         FOR_EACH_PLAYER(player)
101                                         {
102                                                 s = strcat(self.message, "\n");
103                                                 centerprint(player, s);
104                                         }
105                                 }
106
107                                 oldactivator = activator;
108                                 activator = oldself;
109                                         SUB_UseTargets();
110                                 activator = oldactivator;
111                         self = oldself;
112                 }
113         }
114 }
115
116 void assault_setenemytoobjective()
117 {
118         local entity objective;
119         for(objective = world; (objective = find(objective, targetname, self.target)); ) {
120                 if(objective.classname == "target_objective") {
121                         if(self.enemy == world)
122                                 self.enemy = objective;
123                         else
124                                 objerror("more than one objective as target - fix the map!");
125                         break;
126                 }
127         }
128
129         if(self.enemy == world)
130                 objerror("no objective as target - fix the map!");
131 }
132
133 float assault_decreaser_sprite_visible(entity e)
134 {
135         entity decreaser;
136
137         decreaser = self.assault_decreaser;
138
139         if(decreaser.enemy.health >= ASSAULT_VALUE_INACTIVE)
140                 return FALSE;
141
142         return TRUE;
143 }
144
145 void target_objective_decrease_activate()
146 {
147         entity ent, spr;
148         self.owner = world;
149         for(ent = world; (ent = find(ent, target, self.targetname)); )
150         {
151                 if(ent.assault_sprite != world)
152                 {
153                         WaypointSprite_Disown(ent.assault_sprite, waypointsprite_deadlifetime);
154                         if(ent.classname == "func_assault_destructible")
155                                 ent.sprite = world;
156                 }
157
158                 spr = WaypointSprite_SpawnFixed("<placeholder>", 0.5 * (ent.absmin + ent.absmax), ent, assault_sprite, RADARICON_OBJECTIVE, '1 0.5 0');
159                 spr.assault_decreaser = self;
160                 spr.waypointsprite_visible_for_player = assault_decreaser_sprite_visible;
161                 spr.classname = "sprite_waypoint";
162                 WaypointSprite_UpdateRule(spr, assault_attacker_team, SPRITERULE_TEAMPLAY);
163                 if(ent.classname == "func_assault_destructible")
164                 {
165                         WaypointSprite_UpdateSprites(spr, "as-defend", "as-destroy", "as-destroy");
166                         WaypointSprite_UpdateMaxHealth(spr, ent.max_health);
167                         WaypointSprite_UpdateHealth(spr, ent.health);
168                         ent.sprite = spr;
169                 }
170                 else
171                         WaypointSprite_UpdateSprites(spr, "as-defend", "as-push", "as-push");
172         }
173 }
174
175 void target_objective_decrease_findtarget()
176 {
177         assault_setenemytoobjective();
178 }
179
180 //=============================================================================
181
182 void spawnfunc_target_objective_decrease() {
183         if(!g_assault)
184         {
185                 remove(self);
186                 return;
187         }
188
189         self.classname = "target_objective_decrease";
190
191         if(!self.dmg) {
192                 self.dmg = 101;
193         }
194         self.use = assault_objective_decrease_use;
195         self.health = ASSAULT_VALUE_INACTIVE;
196         self.max_health = ASSAULT_VALUE_INACTIVE;
197         self.enemy = world;
198
199         InitializeEntity(self, target_objective_decrease_findtarget, INITPRIO_FINDTARGET);
200 }
201
202 // destructible walls that can be used to trigger target_objective_decrease
203 void spawnfunc_func_assault_destructible() {
204         if(!g_assault)
205         {
206                 remove(self);
207                 return;
208         }
209         self.spawnflags = 3;
210         self.classname = "func_assault_destructible";
211         self.takedamage = TRUE;
212         self.bot_attack = TRUE;
213         if(assault_attacker_team == COLOR_TEAM1) {
214                 self.team = COLOR_TEAM2;
215         } else {
216                 self.team = COLOR_TEAM1;
217         }
218         spawnfunc_func_breakable();
219 }
220
221 void assault_wall_think() {
222         if(self.enemy.health < 0) {
223                 self.model = "";
224                 self.solid = SOLID_NOT;
225         } else {
226                 self.model = self.mdl;
227                 self.solid = SOLID_BSP;
228         }
229
230         self.nextthink = time + 0.2;
231 }
232
233 void spawnfunc_func_assault_wall() {
234         if(!g_assault)
235         {
236                 remove(self);
237                 return;
238         }
239         self.classname = "func_assault_wall";
240         self.mdl = self.model;
241         setmodel(self, self.mdl);
242         self.solid = SOLID_BSP;
243         self.think = assault_wall_think;
244         self.nextthink = time;
245         InitializeEntity(self, assault_setenemytoobjective, INITPRIO_FINDTARGET);
246 }
247
248
249 void target_assault_roundend_reset() {
250         //print("round end reset\n");
251         self.cnt = self.cnt + 1; // up round counter
252         self.winning = 0; // up round
253 }
254
255 void target_assault_roundend_use() {
256         self.winning = 1; // round has been won by attackers
257 }
258
259 void spawnfunc_target_assault_roundend() {
260         if(!g_assault)
261         {
262                 remove(self);
263                 return;
264         }
265         self.winning = 0; // round not yet won by attackers
266         self.classname = "target_assault_roundend";
267         self.use = target_assault_roundend_use;
268         self.cnt = 0; // first round
269         self.reset = target_assault_roundend_reset;
270 }
271
272 void assault_roundstart_use() {
273
274         activator = self;
275         SUB_UseTargets();
276
277
278 #ifdef TTURRETS_ENABLED
279         entity ent, oldself;
280
281         //(Re)spawn all turrets
282         oldself = self;
283         ent = find(world, classname, "turret_main");
284         while(ent) {
285                 // Swap turret teams
286                 if(ent.team == COLOR_TEAM1)
287                         ent.team = COLOR_TEAM2;
288                 else
289                         ent.team = COLOR_TEAM1;
290
291                 self = ent;
292
293                 // Dubbles as teamchange
294                 turret_stdproc_respawn();
295
296                 ent = find(ent, classname, "turret_main");
297         }
298         self = oldself;
299 #endif
300
301
302 }
303
304 void spawnfunc_target_assault_roundstart() {
305         if(!g_assault)
306         {
307                 remove(self);
308                 return;
309         }
310         assault_attacker_team = COLOR_TEAM1;
311         self.classname = "target_assault_roundstart";
312         self.use = assault_roundstart_use;
313         self.reset2 = assault_roundstart_use;
314         InitializeEntity(self, assault_roundstart_use, INITPRIO_FINDTARGET);
315 }
316
317 // trigger new round
318 // reset objectives, toggle spawnpoints, reset triggers, ...
319 void vehicles_clearrturn();
320 void vehicles_spawn();
321 void assault_new_round()
322 {
323     entity oldself;
324         //bprint("ASSAULT: new round\n");
325
326         oldself = self;
327         // Eject players from vehicles
328     FOR_EACH_PLAYER(self)
329     {
330         if(self.vehicle)
331             vehicles_exit(VHEF_RELESE);
332     }
333
334     self = findchainflags(vehicle_flags, VHF_ISVEHICLE);
335     while(self)
336     {
337         vehicles_clearrturn();
338         vehicles_spawn();
339         self = self.chain;
340     }
341
342     self = oldself;
343
344         // up round counter
345         self.winning = self.winning + 1;
346
347         // swap attacker/defender roles
348         if(assault_attacker_team == COLOR_TEAM1) {
349                 assault_attacker_team = COLOR_TEAM2;
350         } else {
351                 assault_attacker_team = COLOR_TEAM1;
352         }
353
354
355         local entity ent;
356         for(ent = world; (ent = nextent(ent)); )
357         {
358                 if(clienttype(ent) == CLIENTTYPE_NOTACLIENT)
359                 {
360                         if(ent.team_saved == COLOR_TEAM1)
361                                 ent.team_saved = COLOR_TEAM2;
362                         else if(ent.team_saved == COLOR_TEAM2)
363                                 ent.team_saved = COLOR_TEAM1;
364                 }
365         }
366
367         // reset the level with a countdown
368         cvar_set("timelimit", ftos(ceil(time - game_starttime) / 60));
369         ReadyRestartForce(); // sets game_starttime
370 }