]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/server/assault.qc
bugfix target_spawn, and change docs to match it
[divverent/nexuiz.git] / data / qcsrc / server / assault.qc
1 //=============================================================================
2
3 /*QUAKED spawnfunc_info_player_attacker (1 0 0) (-16 -16 -24) (16 16 45) INITIAL
4 Normal attacker spawning location for Nexuiz Asssault
5 -------- KEYS --------
6 angle : direction in which player will look when spawning in the game. Does not apply to bots.
7 target : this should point to a spawnfunc_target_objective to decide when this spawning point is active.
8 nobots : when set to 1, bots will never use this spawn point to respawn in the game.
9 nohumans : when set to 1, human players will never use this spawn point to respawn in the game.
10 notfree : when set to 1, entity will not spawn in "Free for all" and "Tournament" modes.
11 notteam : when set to 1, entity will not spawn in "Teamplay" and "CTF" modes.
12 notsingle : when set to 1, entity will not spawn in Single Player mode (bot play mode).
13 -------- SPAWNFLAGS --------
14 INITIAL : makes the spawnpoint the initial place for the player to spawn at the beginning of the game.*/
15 void spawnfunc_info_player_attacker() {
16         self.team = COLOR_TEAM1; // red, gets swapped every round
17         spawnfunc_info_player_deathmatch();
18 }
19
20 //=============================================================================
21
22 /*QUAKED spawnfunc_info_player_defender (0 1 0) (-16 -16 -24) (16 16 45) INITIAL
23 Normal defender spawning location for Nexuiz Asssault
24 -------- KEYS --------
25 angle : direction in which player will look when spawning in the game. Does not apply to bots.
26 target : this should point to a spawnfunc_target_objective to decide when this spawning point is active.
27 nobots : when set to 1, bots will never use this spawn point to respawn in the game.
28 nohumans : when set to 1, human players will never use this spawn point to respawn in the game.
29 notfree : when set to 1, entity will not spawn in "Free for all" and "Tournament" modes.
30 notteam : when set to 1, entity will not spawn in "Teamplay" and "CTF" modes.
31 notsingle : when set to 1, entity will not spawn in Single Player mode (bot play mode).
32 -------- SPAWNFLAGS --------
33 INITIAL : makes the spawnpoint the initial place for the player to spawn at the beginning of the game.*/
34 void spawnfunc_info_player_defender() {
35         self.team = COLOR_TEAM2; // blue, gets swapped every round
36         spawnfunc_info_player_deathmatch();
37 }
38
39 // reset this objective. Used when spawning an objective
40 // and when a new round starts
41 void assault_objective_reset() {
42         self.health = ASSAULT_VALUE_INACTIVE;
43 }
44
45 void assault_objective_use() {
46         // activate objective
47         self.health = 100;
48         self.nextthink = time + 0.1;
49 }
50
51 void assault_objective_think() {
52         if(self.health < 0) {
53                 //self.effects = 0;
54                 activator = self;
55                 SUB_UseTargets();
56         } else {
57                 //self.effects = EF_STARDUST;
58                 self.nextthink = time + 0.1;
59         }
60
61 }
62 //=============================================================================
63
64 /*QUAKED spawnfunc_target_objective (0 .5 0) (-8 -8 -8) (8 8 8)
65 Objective controller for Nexuiz Assault. When active it has 100 health. If it falls below 0 then
66 it'll trigger the next targeted entity (usually the next objective or spawnfunc_target_assault_roundend etc.)
67 -------- KEYS --------
68 targetname : point to e.g. next objective*/
69 void spawnfunc_target_objective() {
70         self.classname = "target_objective";
71         self.think = assault_objective_think;
72         self.use = assault_objective_use;
73         assault_objective_reset();
74 }
75
76 float assault_objective_decrease_customizeforclient() {
77         if(!self.spawnflags)
78                 return FALSE;
79
80         if(self.cnt == 0) {
81                 if(other.team == assault_attacker_team)
82                         if(self.spawnflags == 1)
83                                 setmodel(self, "models/sprites/push.sp2");
84                         else
85                                 setmodel(self, "models/sprites/destroy.sp2");
86                 else
87                         setmodel(self, "models/sprites/defend.sp2");
88         } else {
89                 return FALSE;
90         }
91         return TRUE;
92 }
93
94
95 void assault_objective_decrease_think() {
96
97         local entity objective;
98         local float found;
99         found = 0;
100         objective = find(world, targetname, self.target);
101         while(objective && found == 0) {
102                 if(objective.classname == "target_objective") {
103                         found = 1;
104                         if(objective.health < ASSAULT_VALUE_INACTIVE) { // targeted objective is active
105                                 if(self.cnt == 1 && self.max_health >= ASSAULT_VALUE_INACTIVE) {
106                                         // decrease was fired already, but objective did recover (round reset)
107                                         self.cnt = 0;
108                                 }
109                         } else { // objective isn't active
110                                 self.cnt = 1;
111                         }
112                         self.max_health = objective.health; // save current objective status for next think
113                 }
114         }
115
116         if(!self.spawnflags) {
117                 local entity ent;
118                 ent = find(world, target, self.targetname);
119                 if(ent) {
120                         if(ent.classname == "func_assault_destructible")
121                                 self.spawnflags = 2;
122                         else
123                                 self.spawnflags = 1;
124                 }
125         }
126
127         self.nextthink = time + 0.2;
128 }
129
130
131 // decrease the health of targeted objectives
132 void assault_objective_decrease_use() {
133
134         if(self.cnt > 0) {
135                 // did already fire
136                 return;
137         }
138
139         if(activator.team != assault_attacker_team) {
140                 // wrong team triggered decrease
141                 return;
142         }
143
144         local entity ent;
145         ent = find(world, targetname, self.target);
146         while(ent) {
147                 if(ent.health > 0 && ent.health < ASSAULT_VALUE_INACTIVE)
148                         ent.health = ent.health - self.dmg;
149                 ent = find(ent, targetname, self.target);
150         }
151
152         self.cnt = 1;
153 }
154
155 //=============================================================================
156
157 /*QUAKED target_objective_decrease (0 .5 0) (-8 -8 -8) (8 8 8)
158 When triggered decreases health of the targeted spawnfunc_target_objective.
159 -------- KEYS --------
160 targetname : point to a spawnfunc_target_objective entity*/
161 void spawnfunc_target_objective_decrease() {
162
163         self.classname = "target_objective_decrease";
164
165         precache_model("models/sprites/defend.sp2");
166         precache_model("models/sprites/destroy.sp2");
167         precache_model("models/sprites/push.sp2");
168
169         if(!self.dmg) {
170                 self.dmg = 101;
171         }
172         self.cnt = 0; // not used yet
173         self.use = assault_objective_decrease_use;
174         self.mdl = "models/sprites/here.sp2";
175         self.effects = EF_NODEPTHTEST;
176         self.health = ASSAULT_VALUE_INACTIVE;
177         self.max_health = ASSAULT_VALUE_INACTIVE;
178         self.think = assault_objective_decrease_think;
179         self.customizeentityforclient = assault_objective_decrease_customizeforclient;
180         self.nextthink = time;
181 }
182
183
184 void assault_destructible_reset() {
185         self.health = self.max_health;
186         self.model = self.mdl;
187         self.solid = SOLID_BSP;
188         self.colormod = '1 1 1';
189         self.cnt = 0; // not active
190         if(self.spawnflags)
191         {
192                 activator = self;
193                 self.use();
194         }
195 }
196
197 void assault_destructible_use() {
198         self.cnt = 1; // mark active
199         self.takedamage = DAMAGE_YES;
200 }
201
202 void assault_destructible_destroy() {
203         self.model = "";
204         self.takedamage = DAMAGE_NO;
205         self.solid = SOLID_NOT;
206         SUB_UseTargets();
207 }
208
209 void assault_destructible_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force) {
210
211         if(self.cnt > 0 && assault_attacker_team == attacker.team) {
212                 self.health = self.health - damage;
213                 if(self.health / self.max_health < 0.25)
214                         self.colormod = '1 0 0';
215                 else if(self.health / self.max_health < 0.375)
216                         self.colormod = '1 0.25 0';
217                 else if(self.health / self.max_health < 0.50)
218                         self.colormod = '1 0.5 0';
219                 else if(self.health / self.max_health < 0.625)
220                         self.colormod = '1 0.75 0';
221                 else if(self.health / self.max_health < 0.75)
222                         self.colormod = '1 1 0';
223                 else
224                         self.colormod = '1 1 1';
225         }
226
227         if(self.health < 0) {
228                 activator = attacker;
229                 assault_destructible_destroy();
230         }
231 }
232
233 // destructible walls that can be used to trigger target_objective_decrease
234 void spawnfunc_func_assault_destructible() {
235         if(!self.health)
236                 self.health = 100;
237
238         self.max_health = self.health;
239
240         self.cnt = 0; // not yet activated
241
242         self.classname = "func_assault_destructible";
243         self.mdl = self.model;
244         setmodel(self, self.mdl);
245
246         self.solid = SOLID_BSP;
247         self.use = assault_destructible_use;
248         self.event_damage = assault_destructible_damage;
249
250 }
251
252 void assault_wall_think() {
253         local entity ent;
254         local float notvisible;
255         notvisible = 0;
256         ent = find(world, targetname, self.target);
257         while(ent) {
258                 if(ent.classname == "target_objective" && ent.health < 0)
259                         notvisible = 1;
260                 ent = find(ent, targetname, self.target);
261         }
262
263         if(notvisible) {
264                 self.model = "";
265                 self.solid = SOLID_NOT;
266         } else {
267                 self.model = self.mdl;
268                 self.solid = SOLID_BSP;
269         }
270
271         self.nextthink = time + 0.2;
272 }
273
274 void spawnfunc_func_assault_wall() {
275         self.classname = "func_assault_wall";
276         self.mdl = self.model;
277         setmodel(self, self.mdl);
278         self.solid = SOLID_BSP;
279         self.think = assault_wall_think;
280         self.nextthink = time;
281 }
282
283
284 void target_assault_roundend_reset() {
285         self.cnt = self.cnt + 1; // up round counter
286         self.winning = 0; // up round
287 }
288
289 void target_assault_roundend_use() {
290         self.winning = 1; // round has been won by attackers
291 }
292
293 void spawnfunc_target_assault_roundend() {
294         if(!self.health)
295                 self.health = 300; // 5 minutes
296
297         cvar_set("timelimit", ftos(self.health/60));
298         self.winning = 0; // round not yet won by attackers
299         self.classname = "target_assault_roundend";
300         self.use = target_assault_roundend_use;
301         self.cnt = 0; // first round
302 }
303
304 void assault_roundstart_use() {
305
306         activator = self;
307         SUB_UseTargets();
308
309 #ifdef TTURRETS_ENABLED
310     entity ent,oldself;
311
312         //(Re)spawn all turrets
313         oldself = self;
314         ent = find(world, classname, "turret_main");
315         while(ent) {
316             // Swap turret teams
317         if(ent.team == COLOR_TEAM1)
318             ent.team = COLOR_TEAM2;
319         else
320             ent.team = COLOR_TEAM1;
321
322         self = ent;
323
324         // Dubbles as teamchange
325         turret_stdproc_respawn();
326         //ent.turret_spawnfunc();
327
328                 ent = find(ent, classname, "turret_main");
329         }
330         self = oldself;
331 #endif
332
333 }
334
335 void spawnfunc_target_assault_roundstart() {
336         assault_attacker_team = COLOR_TEAM1;
337         self.classname = "target_assault_roundstart";
338         self.use = assault_roundstart_use;
339         self.think = assault_roundstart_use;
340         self.nextthink = time + 0.1;
341 }
342
343 // trigger new round
344 // reset objectives, toggle spawnpoints, reset triggers, ...
345 void assault_new_round() {
346
347         // up round counter
348         self.winning = self.winning + 1;
349         // set end time for next round
350         self.cnt = time + self.health;
351
352         // swap spawn point teams
353         local entity ent;
354         local entity oldself;
355
356         // reward attackers for winning the round
357         ent = find(world, classname, "player");
358         while(ent) {
359                 if(ent.team == assault_attacker_team) {
360                         UpdateFrags(ent, 10);
361                 }
362                 ent = find(ent, classname, "player");
363         }
364
365         // swap attacker/defender roles
366         if(assault_attacker_team == COLOR_TEAM1) {
367                 assault_attacker_team = COLOR_TEAM2;
368         } else {
369                 assault_attacker_team = COLOR_TEAM1;
370         }
371
372         ent = find(world, classname, "info_player_deathmatch");
373         while (ent)
374         {
375                 oldself = self;
376                 self = ent;
377                 if(self.team == COLOR_TEAM1) {
378                         self.team = COLOR_TEAM2;
379                 } else {
380                         self.team = COLOR_TEAM1;
381                 }
382                 self = oldself;
383
384                 ent = find(ent, classname, "info_player_deathmatch");
385         }
386
387         // reset all objectives
388         ent = find(world, classname, "target_objective");
389         while (ent)
390         {
391                 oldself = self;
392                 self = ent;
393                 assault_objective_reset();
394                 self = oldself;
395
396                 ent = find(ent, classname, "target_objective");
397         }
398
399         // reset round end triggers
400         ent = find(world, classname, "target_assault_roundend");
401         while (ent)
402         {
403                 oldself = self;
404                 self = ent;
405                 target_assault_roundend_reset();
406                 self = oldself;
407
408                 ent = find(ent, classname, "target_assault_roundend");
409         }
410
411         // reset all target_object_decrease
412         ent = find(world, classname, "target_objective_decrease");
413         while (ent)
414         {
415                 ent.cnt = 0;
416                 ent = find(ent, classname, "target_objective_decrease");
417         }
418
419         // reset all spawnfunc_func_assault_destructible
420         ent = find(world, classname, "func_assault_destructible");
421         while (ent)
422         {
423                 oldself = self;
424                 self = ent;
425
426         if(ent.team == COLOR_TEAM1)
427             ent.team = COLOR_TEAM2;
428         else
429             ent.team = COLOR_TEAM1;
430
431                 assault_destructible_reset();
432                 self = oldself;
433                 ent = find(ent, classname, "func_assault_destructible");
434         }
435
436         ent = find(world, classname, "target_assault_roundstart");
437         while (ent)
438         {
439                 oldself = self;
440                 self = ent;
441                 self.use();
442                 self = oldself;
443                 ent = find(ent, classname, "target_assault_roundstart");
444         }
445
446         // actually restart round... how to do that?
447         ent = find(world, classname, "player");
448         while(ent) {
449                 oldself = self;
450                 self = ent;
451                 PutClientInServer();
452                 self = oldself;
453                 ent = find(ent, classname, "player");
454
455         }
456
457
458 }
459
460