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