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