]> de.git.xonotic.org Git - voretournament/voretournament.git/blob - data/qcsrc/server/mode_onslaught.qc
Get VoreTournament code to compile with gmqcc. To be compiled with the same parameter...
[voretournament/voretournament.git] / data / qcsrc / server / mode_onslaught.qc
1 void onslaught_generator_updatesprite(entity e);\r
2 void onslaught_controlpoint_updatesprite(entity e);\r
3 void onslaught_link_checkupdate();\r
4 \r
5 .entity sprite;\r
6 .string target2;\r
7 .float iscaptured;\r
8 .float islinked;\r
9 .float isgenneighbor_red;\r
10 .float isgenneighbor_blue;\r
11 .float iscpneighbor_red;\r
12 .float iscpneighbor_blue;\r
13 .float isshielded;\r
14 .float lasthealth;\r
15 .float lastteam;\r
16 .float lastshielded;\r
17 .float lastcaptured;\r
18 \r
19 .string model1, model2, model3;\r
20 \r
21 void ons_gib_damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector vforce)\r
22 {\r
23         self.velocity = self.velocity + vforce;\r
24 }\r
25 \r
26 .float giblifetime;\r
27 void ons_throwgib_think()\r
28 {\r
29         float d;\r
30 \r
31         self.nextthink = time + 0.05;\r
32 \r
33         d = self.giblifetime - time;\r
34 \r
35         if(d<0)\r
36         {\r
37                 self.think = SUB_Remove;\r
38                 return;\r
39         }\r
40         if(d<1)\r
41                 self.alpha = d;\r
42 \r
43         if(d>2)\r
44         if(random()<0.6)\r
45                 pointparticles(particleeffectnum("onslaught_generator_gib_flame"), self.origin, '0 0 0', 1);\r
46 };\r
47 \r
48 void ons_throwgib(vector v_from, vector v_to, string smodel, float f_lifetime, float b_burn)\r
49 {\r
50         local entity gib;\r
51 \r
52         gib = spawn();\r
53 \r
54         setmodel(gib, smodel);\r
55         setorigin(gib, v_from);\r
56         gib.solid = SOLID_BBOX;\r
57         gib.movetype = MOVETYPE_BOUNCE;\r
58         gib.takedamage = DAMAGE_YES;\r
59         gib.event_damage = ons_gib_damage;\r
60         gib.health = -1;\r
61         gib.effects = EF_LOWPRECISION;\r
62         gib.flags = FL_NOTARGET;\r
63         gib.velocity = v_to;\r
64         gib.giblifetime = time + f_lifetime;\r
65 \r
66         if (b_burn)\r
67         {\r
68                 gib.think = ons_throwgib_think;\r
69                 gib.nextthink = time + 0.05;\r
70         }\r
71         else\r
72                 SUB_SetFade(gib, gib.giblifetime, 2);\r
73 };\r
74 \r
75 void onslaught_updatelinks()\r
76 {\r
77         local entity l, links;\r
78         local float stop, t1, t2, t3, t4;\r
79         // first check if the game has ended\r
80         dprint("--- updatelinks ---\n");\r
81         links = findchain(classname, "onslaught_link");\r
82         // mark generators as being shielded and networked\r
83         l = findchain(classname, "onslaught_generator");\r
84         while (l)\r
85         {\r
86                 if (l.iscaptured)\r
87                         dprint(etos(l), " (generator) belongs to team ", ftos(l.team), "\n");\r
88                 else\r
89                         dprint(etos(l), " (generator) is destroyed\n");\r
90                 l.islinked = l.iscaptured;\r
91                 l.isshielded = l.iscaptured;\r
92                 l = l.chain;\r
93         }\r
94         // mark points as shielded and not networked\r
95         l = findchain(classname, "onslaught_controlpoint");\r
96         while (l)\r
97         {\r
98                 l.islinked = FALSE;\r
99                 l.isshielded = TRUE;\r
100                 l.isgenneighbor_red = FALSE;\r
101                 l.isgenneighbor_blue = FALSE;\r
102                 l.iscpneighbor_red = FALSE;\r
103                 l.iscpneighbor_blue = FALSE;\r
104                 dprint(etos(l), " (point) belongs to team ", ftos(l.team), "\n");\r
105                 l = l.chain;\r
106         }\r
107         // flow power outward from the generators through the network\r
108         l = links;\r
109         while (l)\r
110         {\r
111                 dprint(etos(l), " (link) connects ", etos(l.goalentity), " with ", etos(l.enemy), "\n");\r
112                 l = l.chain;\r
113         }\r
114         stop = FALSE;\r
115         while (!stop)\r
116         {\r
117                 stop = TRUE;\r
118                 l = links;\r
119                 while (l)\r
120                 {\r
121                         // if both points are captured by the same team, and only one of\r
122                         // them is powered, mark the other one as powered as well\r
123                         if (l.enemy.iscaptured && l.goalentity.iscaptured)\r
124                                 if (l.enemy.islinked != l.goalentity.islinked)\r
125                                         if (l.enemy.team == l.goalentity.team)\r
126                                         {\r
127                                                 if (!l.goalentity.islinked)\r
128                                                 {\r
129                                                         stop = FALSE;\r
130                                                         l.goalentity.islinked = TRUE;\r
131                                                         dprint(etos(l), " (link) is marking ", etos(l.goalentity), " (point) because its team matches ", etos(l.enemy), " (point)\n");\r
132                                                 }\r
133                                                 else if (!l.enemy.islinked)\r
134                                                 {\r
135                                                         stop = FALSE;\r
136                                                         l.enemy.islinked = TRUE;\r
137                                                         dprint(etos(l), " (link) is marking ", etos(l.enemy), " (point) because its team matches ", etos(l.goalentity), " (point)\n");\r
138                                                 }\r
139                                         }\r
140                         l = l.chain;\r
141                 }\r
142         }\r
143         // now that we know which points are powered we can mark their neighbors\r
144         // as unshielded if team differs\r
145         l = links;\r
146         while (l)\r
147         {\r
148                 if (l.goalentity.islinked)\r
149                 {\r
150                         if (l.goalentity.team != l.enemy.team)\r
151                         {\r
152                                 dprint(etos(l), " (link) is unshielding ", etos(l.enemy), " (point) because its team does not match ", etos(l.goalentity), " (point)\n");\r
153                                 l.enemy.isshielded = FALSE;\r
154                         }\r
155                         if(l.goalentity.classname == "onslaught_generator")\r
156                         {\r
157                                 if(l.goalentity.team == COLOR_TEAM1)\r
158                                         l.enemy.isgenneighbor_red = TRUE;\r
159                                 else if(l.goalentity.team == COLOR_TEAM2)\r
160                                         l.enemy.isgenneighbor_blue = TRUE;\r
161                         }\r
162                         else\r
163                         {\r
164                                 if(l.goalentity.team == COLOR_TEAM1)\r
165                                         l.enemy.iscpneighbor_red = TRUE;\r
166                                 else if(l.goalentity.team == COLOR_TEAM2)\r
167                                         l.enemy.iscpneighbor_blue = TRUE;\r
168                         }\r
169                 }\r
170                 if (l.enemy.islinked)\r
171                 {\r
172                         if (l.goalentity.team != l.enemy.team)\r
173                         {\r
174                                 dprint(etos(l), " (link) is unshielding ", etos(l.goalentity), " (point) because its team does not match ", etos(l.enemy), " (point)\n");\r
175                                 l.goalentity.isshielded = FALSE;\r
176                         }\r
177                         if(l.enemy.classname == "onslaught_generator")\r
178                         {\r
179                                 if(l.enemy.team == COLOR_TEAM1)\r
180                                         l.goalentity.isgenneighbor_red = TRUE;\r
181                                 else if(l.enemy.team == COLOR_TEAM2)\r
182                                         l.goalentity.isgenneighbor_blue = TRUE;\r
183                         }\r
184                         else\r
185                         {\r
186                                 if(l.enemy.team == COLOR_TEAM1)\r
187                                         l.goalentity.iscpneighbor_red = TRUE;\r
188                                 else if(l.enemy.team == COLOR_TEAM2)\r
189                                         l.goalentity.iscpneighbor_blue = TRUE;\r
190                         }\r
191                 }\r
192                 l = l.chain;\r
193         }\r
194         // now update the takedamage and alpha variables on generator shields\r
195         l = findchain(classname, "onslaught_generator");\r
196         while (l)\r
197         {\r
198                 if (l.isshielded)\r
199                 {\r
200                         dprint(etos(l), " (generator) is shielded\n");\r
201                         l.enemy.alpha = 1;\r
202                         l.takedamage = DAMAGE_NO;\r
203                         l.bot_attack = FALSE;\r
204                 }\r
205                 else\r
206                 {\r
207                         dprint(etos(l), " (generator) is not shielded\n");\r
208                         l.enemy.alpha = -1;\r
209                         l.takedamage = DAMAGE_AIM;\r
210                         l.bot_attack = TRUE;\r
211                 }\r
212                 l = l.chain;\r
213         }\r
214         // now update the takedamage and alpha variables on control point icons\r
215         l = findchain(classname, "onslaught_controlpoint");\r
216         while (l)\r
217         {\r
218                 if (l.isshielded)\r
219                 {\r
220                         dprint(etos(l), " (point) is shielded\n");\r
221                         l.enemy.alpha = 1;\r
222                         if (l.goalentity)\r
223                         {\r
224                                 l.goalentity.takedamage = DAMAGE_NO;\r
225                                 l.goalentity.bot_attack = FALSE;\r
226                         }\r
227                 }\r
228                 else\r
229                 {\r
230                         dprint(etos(l), " (point) is not shielded\n");\r
231                         l.enemy.alpha = -1;\r
232                         if (l.goalentity)\r
233                         {\r
234                                 l.goalentity.takedamage = DAMAGE_AIM;\r
235                                 l.goalentity.bot_attack = TRUE;\r
236                         }\r
237                 }\r
238                 onslaught_controlpoint_updatesprite(l);\r
239                 l = l.chain;\r
240         }\r
241         // count generators owned by each team\r
242         t1 = t2 = t3 = t4 = 0;\r
243         l = findchain(classname, "onslaught_generator");\r
244         while (l)\r
245         {\r
246                 if (l.iscaptured)\r
247                 {\r
248                         if (l.team == COLOR_TEAM1) t1 = 1;\r
249                         if (l.team == COLOR_TEAM2) t2 = 1;\r
250                         if (l.team == COLOR_TEAM3) t3 = 1;\r
251                         if (l.team == COLOR_TEAM4) t4 = 1;\r
252                 }\r
253                 onslaught_generator_updatesprite(l);\r
254                 l = l.chain;\r
255         }\r
256         // see if multiple teams remain (if not, it's game over)\r
257         if (t1 + t2 + t3 + t4 < 2)\r
258                 dprint("--- game over ---\n");\r
259         else\r
260                 dprint("--- done updating links ---\n");\r
261 };\r
262 \r
263 float onslaught_controlpoint_can_be_linked(entity cp, float t)\r
264 {\r
265         if(t == COLOR_TEAM1)\r
266         {\r
267                 if(cp.isgenneighbor_red)\r
268                         return 2;\r
269                 if(cp.iscpneighbor_red)\r
270                         return 1;\r
271         }\r
272         else if(t == COLOR_TEAM2)\r
273         {\r
274                 if(cp.isgenneighbor_blue)\r
275                         return 2;\r
276                 if(cp.iscpneighbor_blue)\r
277                         return 1;\r
278         }\r
279         return 0;\r
280         /*\r
281            entity e;\r
282         // check to see if this player has a legitimate claim to capture this\r
283         // control point - more specifically that there is a captured path of\r
284         // points leading back to the team generator\r
285         e = findchain(classname, "onslaught_link");\r
286         while (e)\r
287         {\r
288         if (e.goalentity == cp)\r
289         {\r
290         dprint(etos(e), " (link) connects to ", etos(e.enemy), " (point)");\r
291         if (e.enemy.islinked)\r
292         {\r
293         dprint(" which is linked");\r
294         if (e.enemy.team == t)\r
295         {\r
296         dprint(" and has the correct team!\n");\r
297         return 1;\r
298         }\r
299         else\r
300         dprint(" but has the wrong team\n");\r
301         }\r
302         else\r
303         dprint("\n");\r
304         }\r
305         else if (e.enemy == cp)\r
306         {\r
307         dprint(etos(e), " (link) connects to ", etos(e.goalentity), " (point)");\r
308         if (e.goalentity.islinked)\r
309         {\r
310         dprint(" which is linked");\r
311         if (e.goalentity.team == t)\r
312         {\r
313         dprint(" and has a team!\n");\r
314         return 1;\r
315         }\r
316         else\r
317         dprint(" but has the wrong team\n");\r
318         }\r
319         else\r
320         dprint("\n");\r
321         }\r
322         e = e.chain;\r
323         }\r
324         return 0;\r
325          */\r
326 }\r
327 \r
328 float onslaught_controlpoint_attackable(entity cp, float t)\r
329         // -2: SAME TEAM, attackable by enemy!\r
330         // -1: SAME TEAM!\r
331         // 0: off limits\r
332         // 1: attack it\r
333         // 2: touch it\r
334         // 3: attack it (HIGH PRIO)\r
335         // 4: touch it (HIGH PRIO)\r
336 {\r
337         float a;\r
338 \r
339         if(cp.isshielded)\r
340         {\r
341                 return 0;\r
342         }\r
343         else if(cp.goalentity)\r
344         {\r
345                 // if there's already an icon built, nothing happens\r
346                 if(cp.team == t)\r
347                 {\r
348                         a = onslaught_controlpoint_can_be_linked(cp, COLOR_TEAM1 + COLOR_TEAM2 - t);\r
349                         if(a) // attackable by enemy?\r
350                                 return -2; // EMERGENCY!\r
351                         return -1;\r
352                 }\r
353                 // we know it can be linked, so no need to check\r
354                 // but...\r
355                 a = onslaught_controlpoint_can_be_linked(cp, t);\r
356                 if(a == 2) // near our generator?\r
357                         return 3; // EMERGENCY!\r
358                 return 1;\r
359         }\r
360         else\r
361         {\r
362                 // free point\r
363                 if(onslaught_controlpoint_can_be_linked(cp, t))\r
364                 {\r
365                         a = onslaught_controlpoint_can_be_linked(cp, COLOR_TEAM1 + COLOR_TEAM2 - t);\r
366                         if(a == 2)\r
367                                 return 4; // GET THIS ONE NOW!\r
368                         else\r
369                                 return 2; // TOUCH ME\r
370                 }\r
371         }\r
372         return 0;\r
373 }\r
374 \r
375 void onslaught_generator_think()\r
376 {\r
377         local float d;\r
378         local entity e;\r
379         self.nextthink = ceil(time + 1);\r
380         if (!gameover)\r
381         {\r
382                 if (cvar("timelimit"))\r
383                 if (time > game_starttime + cvar("timelimit") * 60)\r
384                 {\r
385                         // self.max_health / 300 gives 5 minutes of overtime.\r
386                         // control points reduce the overtime duration.\r
387                         sound(self, CHAN_TRIGGER, "onslaught/generator_decay.wav", VOL_BASE, ATTN_NORM);\r
388                         d = 1;\r
389                         e = findchain(classname, "onslaught_controlpoint");\r
390                         while (e)\r
391                         {\r
392                                 if (e.team != self.team)\r
393                                         if (e.islinked)\r
394                                                 d = d + 1;\r
395                                 e = e.chain;\r
396                         }\r
397                         d = d * self.max_health / 300;\r
398                         Damage(self, self, self, d, DEATH_HURTTRIGGER, self.origin, '0 0 0');\r
399                 }\r
400         }\r
401 };\r
402 \r
403 void onslaught_generator_ring_spawn(vector org)\r
404 {\r
405         modeleffect_spawn("models/onslaught/shockwavetransring.md3", 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, -16, 0.1, 1.25, 0.25);\r
406 };\r
407 \r
408 void onslaught_generator_ray_think()\r
409 {\r
410         self.nextthink = time + 0.05;\r
411         if(self.count > 10)\r
412         {\r
413                 self.think = SUB_Remove;\r
414                 return;\r
415         }\r
416 \r
417         if(self.count > 5)\r
418                 self.alpha -= 0.1;\r
419         else\r
420                 self.alpha += 0.1;\r
421 \r
422         self.scale += 0.2;\r
423         self.count +=1;\r
424 };\r
425 \r
426 void onslaught_generator_ray_spawn(vector org)\r
427 {\r
428         entity e;\r
429         e = spawn();\r
430         setmodel(e, "models/onslaught/ons_ray.md3");\r
431         setorigin(e, org);\r
432         e.angles = randomvec() * 360;\r
433         e.alpha = 0;\r
434         e.scale = random() * 5 + 8;\r
435         e.think = onslaught_generator_ray_think;\r
436         e.nextthink = time + 0.05;\r
437 };\r
438 \r
439 void onslaught_generator_shockwave_spawn(vector org)\r
440 {\r
441         shockwave_spawn("models/onslaught/shockwave.md3", org, -64, 0.75, 0.5);\r
442 };\r
443 \r
444 void onslaught_generator_damage_think()\r
445 {\r
446         if(self.owner.health < 0)\r
447         {\r
448                 self.think = SUB_Remove;\r
449                 return;\r
450         }\r
451         self.nextthink = time+0.1;\r
452 \r
453         // damaged fx (less probable the more damaged is the generator)\r
454         if(random() < 0.9 - self.owner.health / self.owner.max_health)\r
455                 if(random() < 0.01)\r
456                 {\r
457                         pointparticles(particleeffectnum("electric_ballexplode"), self.origin + randompos('-50 -50 -20', '50 50 50'), '0 0 0', 1);\r
458                         sound(self, CHAN_TRIGGER, "onslaught/electricity_explode.wav", VOL_BASE, ATTN_NORM);\r
459                 }\r
460                 else\r
461                         pointparticles(particleeffectnum("torch_small"), self.origin + randompos('-60 -60 -20', '60 60 60'), '0 0 0', 1);\r
462 };\r
463 \r
464 void onslaught_generator_damage_spawn(entity gd_owner)\r
465 {\r
466         entity e;\r
467         e = spawn();\r
468         e.owner = gd_owner;\r
469         e.health = self.owner.health;\r
470         setorigin(e, gd_owner.origin);\r
471         e.think = onslaught_generator_damage_think;\r
472         e.nextthink = time+1;\r
473 };\r
474 \r
475 void onslaught_generator_deaththink()\r
476 {\r
477         local vector org;\r
478         local float i;\r
479 \r
480         if not (self.count)\r
481                 self.count = 40;\r
482 \r
483         // White shockwave\r
484         if(self.count==40||self.count==20)\r
485         {\r
486                 onslaught_generator_ring_spawn(self.origin);\r
487                 sound(self, CHAN_TRIGGER, "onslaught/shockwave.wav", VOL_BASE, ATTN_NORM);\r
488         }\r
489 \r
490         // Throw some gibs\r
491         if(random() < 0.3)\r
492         {\r
493                 i = random();\r
494                 if(i < 0.3)\r
495                         ons_throwgib(self.origin + '0 0 40', (100 * randomvec() - '1 1 1') * 11 + '0 0 20', "models/onslaught/gen_gib1.md3", 6, TRUE);\r
496                 else if(i > 0.7)\r
497                         ons_throwgib(self.origin + '0 0 40', (100 * randomvec() - '1 1 1') * 12 + '0 0 20', "models/onslaught/gen_gib2.md3", 6, TRUE);\r
498                 else\r
499                         ons_throwgib(self.origin + '0 0 40', (100 * randomvec() - '1 1 1') * 13 + '0 0 20', "models/onslaught/gen_gib3.md3", 6, TRUE);\r
500         }\r
501 \r
502         // Spawn fire balls\r
503         for(i=0;i < 10;++i)\r
504         {\r
505                 org = self.origin + randompos('-30 -30 -30' * i + '0 0 -20', '30 30 30' * i + '0 0 20');\r
506                 pointparticles(particleeffectnum("onslaught_generator_gib_explode"), org, '0 0 0', 1);\r
507         }\r
508 \r
509         // Short explosion sound + small explosion\r
510         if(random() < 0.25)\r
511         {\r
512                 te_explosion(self.origin);\r
513                 sound(self, CHAN_TRIGGER, "weapons/grenade_impact.wav", VOL_BASE, ATTN_NORM);\r
514         }\r
515 \r
516         // Particles\r
517         org = self.origin + randompos(self.mins + '8 8 8', self.maxs + '-8 -8 -8');\r
518         pointparticles(particleeffectnum("onslaught_generator_smallexplosion"), org, '0 0 0', 1);\r
519 \r
520         // rays\r
521         if(random() > 0.25 )\r
522         {\r
523                 onslaught_generator_ray_spawn(self.origin);\r
524         }\r
525 \r
526         // Final explosion\r
527         if(self.count==1)\r
528         {\r
529                 org = self.origin;\r
530                 te_explosion(org);\r
531                 onslaught_generator_shockwave_spawn(org);\r
532                 pointparticles(particleeffectnum("onslaught_generator_finalexplosion"), org, '0 0 0', 1);\r
533                 sound(self, CHAN_TRIGGER, "weapons/rocket_impact.wav", VOL_BASE, ATTN_NORM);\r
534         }\r
535         else\r
536                 self.nextthink = time + 0.05;\r
537 \r
538         self.count = self.count - 1;\r
539 };\r
540 \r
541 void onslaught_generator_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)\r
542 {\r
543         local float i;\r
544         if (damage <= 0)\r
545                 return;\r
546         if(inWarmupStage)\r
547                 return;\r
548         if (attacker != self)\r
549         {\r
550                 if (self.isshielded)\r
551                 {\r
552                         // this is protected by a shield, so ignore the damage\r
553                         if (time > self.pain_finished)\r
554                                 if (attacker.classname == "player")\r
555                                 {\r
556                                         play2(attacker, "onslaught/damageblockedbyshield.wav");\r
557                                         self.pain_finished = time + 1;\r
558                                 }\r
559                         return;\r
560                 }\r
561                 if (time > self.pain_finished)\r
562                 {\r
563                         self.pain_finished = time + 10;\r
564                         bprint(ColoredTeamName(self.team), " generator under attack!\n");\r
565                         play2team(self.team, "onslaught/generator_underattack.wav");\r
566                 }\r
567         }\r
568         self.health = self.health - damage;\r
569         WaypointSprite_UpdateHealth(self.sprite, self.health);\r
570         // choose an animation frame based on health\r
571         self.frame = 10 * bound(0, (1 - self.health / self.max_health), 1);\r
572         // see if the generator is still functional, or dying\r
573         if (self.health > 0)\r
574         {\r
575 #ifdef ONSLAUGHT_SPAM\r
576                 float h, lh;\r
577                 lh = ceil(self.lasthealth / 100) * 100;\r
578                 h = ceil(self.health / 100) * 100;\r
579                 if(lh != h)\r
580                         bprint(ColoredTeamName(self.team), " generator has less than ", ftos(h), " health remaining\n");\r
581 #endif\r
582                 self.lasthealth = self.health;\r
583         }\r
584         else if not(inWarmupStage)\r
585         {\r
586                 if (attacker == self)\r
587                         bprint(ColoredTeamName(self.team), " generator spontaneously exploded due to overtime!\n");\r
588                 else\r
589                 {\r
590                         string t;\r
591                         t = ColoredTeamName(attacker.team);\r
592                         bprint(ColoredTeamName(self.team), " generator destroyed by ", t, "!\n");\r
593                 }\r
594                 self.iscaptured = FALSE;\r
595                 self.islinked = FALSE;\r
596                 self.isshielded = FALSE;\r
597                 self.takedamage = DAMAGE_NO; // can't be hurt anymore\r
598 #ifndef GMQCC\r
599                 self.event_damage = SUB_Null; // won't do anything if hurt\r
600 #else\r
601                 self.event_damage = nil;\r
602 #endif\r
603                 self.count = 0; // reset counter\r
604                 self.think = onslaught_generator_deaththink; // explosion sequence\r
605                 self.nextthink = time; // start exploding immediately\r
606                 self.think(); // do the first explosion now\r
607 \r
608                 WaypointSprite_UpdateMaxHealth(self.sprite, 0);\r
609 \r
610                 onslaught_updatelinks();\r
611         }\r
612 \r
613         if(self.health <= 0)\r
614                 setmodel(self, "models/onslaught/generator_dead.md3");\r
615         else if(self.health < self.max_health * 0.10)\r
616                 setmodel(self, "models/onslaught/generator_dmg9.md3");\r
617         else if(self.health < self.max_health * 0.20)\r
618                 setmodel(self, "models/onslaught/generator_dmg8.md3");\r
619         else if(self.health < self.max_health * 0.30)\r
620                 setmodel(self, "models/onslaught/generator_dmg7.md3");\r
621         else if(self.health < self.max_health * 0.40)\r
622                 setmodel(self, "models/onslaught/generator_dmg6.md3");\r
623         else if(self.health < self.max_health * 0.50)\r
624                 setmodel(self, "models/onslaught/generator_dmg5.md3");\r
625         else if(self.health < self.max_health * 0.60)\r
626                 setmodel(self, "models/onslaught/generator_dmg4.md3");\r
627         else if(self.health < self.max_health * 0.70)\r
628                 setmodel(self, "models/onslaught/generator_dmg3.md3");\r
629         else if(self.health < self.max_health * 0.80)\r
630                 setmodel(self, "models/onslaught/generator_dmg2.md3");\r
631         else if(self.health < self.max_health * 0.90)\r
632                 setmodel(self, "models/onslaught/generator_dmg1.md3");\r
633         setsize(self, '-52 -52 -14', '52 52 75');\r
634 \r
635         // Throw some flaming gibs on damage, more damage = more chance for gib\r
636         if(random() < damage/220)\r
637         {\r
638                 sound(self, CHAN_TRIGGER, "weapons/rocket_impact.wav", VOL_BASE, ATTN_NORM);\r
639                 i = random();\r
640                 if(i < 0.3)\r
641                         ons_throwgib(hitloc + '0 0 20', force * -1, "models/onslaught/gen_gib1.md3", 5, TRUE);\r
642                 else if(i > 0.7)\r
643                         ons_throwgib(hitloc + '0 0 20', force * -1, "models/onslaught/gen_gib2.md3", 5, TRUE);\r
644                 else\r
645                         ons_throwgib(hitloc + '0 0 20', force * -1, "models/onslaught/gen_gib3.md3", 5, TRUE);\r
646         }\r
647         else\r
648         {\r
649                 // particles on every hit\r
650                 pointparticles(particleeffectnum("sparks"), hitloc, force * -1, 1);\r
651 \r
652                 //sound on every hit\r
653                 if (random() < 0.5)\r
654                         sound(self, CHAN_TRIGGER, "onslaught/ons_hit1.wav", VOL_BASE, ATTN_NORM);\r
655                 else\r
656                         sound(self, CHAN_TRIGGER, "onslaught/ons_hit2.wav", VOL_BASE, ATTN_NORM);\r
657         }\r
658 \r
659         //throw some gibs on damage\r
660         if(random() < damage/200+0.2)\r
661                 if(random() < 0.5)\r
662                         ons_throwgib(hitloc + '0 0 20', randomvec()*360, "models/onslaught/gen_gib1.md3", 5, FALSE);\r
663 };\r
664 \r
665 // update links after a delay\r
666 void onslaught_generator_delayed()\r
667 {\r
668         onslaught_updatelinks();\r
669         // now begin normal thinking\r
670         self.think = onslaught_generator_think;\r
671         self.nextthink = time;\r
672 };\r
673 \r
674 string onslaught_generator_waypointsprite_for_team(entity e, float t)\r
675 {\r
676         if(t == e.team)\r
677         {\r
678                 if(e.team == COLOR_TEAM1)\r
679                         return "ons-gen-red";\r
680                 else if(e.team == COLOR_TEAM2)\r
681                         return "ons-gen-blue";\r
682         }\r
683         if(e.isshielded)\r
684                 return "ons-gen-shielded";\r
685         if(e.team == COLOR_TEAM1)\r
686                 return "ons-gen-red";\r
687         else if(e.team == COLOR_TEAM2)\r
688                 return "ons-gen-blue";\r
689         return "";\r
690 }\r
691 \r
692 void onslaught_generator_updatesprite(entity e)\r
693 {\r
694         string s1, s2, s3;\r
695         s1 = onslaught_generator_waypointsprite_for_team(e, COLOR_TEAM1);\r
696         s2 = onslaught_generator_waypointsprite_for_team(e, COLOR_TEAM2);\r
697         s3 = onslaught_generator_waypointsprite_for_team(e, -1);\r
698         WaypointSprite_UpdateSprites(e.sprite, s1, s2, s3);\r
699 \r
700         if(e.lastteam != e.team + 2 || e.lastshielded != e.isshielded)\r
701         {\r
702                 e.lastteam = e.team + 2;\r
703                 e.lastshielded = e.isshielded;\r
704                 if(e.lastshielded)\r
705                 {\r
706                         if(e.team == COLOR_TEAM1 || e.team == COLOR_TEAM2)\r
707                                 WaypointSprite_UpdateRadar(e.sprite, RADARICON_GENERATOR, 0.5 * colormapPaletteColor(e.team - 1, FALSE));\r
708                         else\r
709                                 WaypointSprite_UpdateRadar(e.sprite, RADARICON_GENERATOR, '0.5 0.5 0.5');\r
710                 }\r
711                 else\r
712                 {\r
713                         if(e.team == COLOR_TEAM1 || e.team == COLOR_TEAM2)\r
714                                 WaypointSprite_UpdateRadar(e.sprite, RADARICON_GENERATOR, colormapPaletteColor(e.team - 1, FALSE));\r
715                         else\r
716                                 WaypointSprite_UpdateRadar(e.sprite, RADARICON_GENERATOR, '0.75 0.75 0.75');\r
717                 }\r
718                 WaypointSprite_Ping(e.sprite);\r
719         }\r
720 }\r
721 \r
722 string onslaught_controlpoint_waypointsprite_for_team(entity e, float t)\r
723 {\r
724         float a;\r
725         if(t != -1)\r
726         {\r
727                 a = onslaught_controlpoint_attackable(e, t);\r
728                 if(a == 3 || a == 4) // ATTACK/TOUCH THIS ONE NOW\r
729                 {\r
730                         if(e.team == COLOR_TEAM1)\r
731                                 return "ons-cp-atck-red";\r
732                         else if(e.team == COLOR_TEAM2)\r
733                                 return "ons-cp-atck-blue";\r
734                         else\r
735                                 return "ons-cp-atck-neut";\r
736                 }\r
737                 else if(a == -2) // DEFEND THIS ONE NOW\r
738                 {\r
739                         if(e.team == COLOR_TEAM1)\r
740                                 return "ons-cp-dfnd-red";\r
741                         else if(e.team == COLOR_TEAM2)\r
742                                 return "ons-cp-dfnd-blue";\r
743                 }\r
744                 else if(e.team == t || a == -1 || a == 1) // own point, or fire at it\r
745                 {\r
746                         if(e.team == COLOR_TEAM1)\r
747                                 return "ons-cp-red";\r
748                         else if(e.team == COLOR_TEAM2)\r
749                                 return "ons-cp-blue";\r
750                 }\r
751                 else if(a == 2) // touch it\r
752                         return "ons-cp-neut";\r
753         }\r
754         else\r
755         {\r
756                 if(e.team == COLOR_TEAM1)\r
757                         return "ons-cp-red";\r
758                 else if(e.team == COLOR_TEAM2)\r
759                         return "ons-cp-blue";\r
760                 else\r
761                         return "ons-cp-neut";\r
762         }\r
763         return "";\r
764 }\r
765 \r
766 void onslaught_controlpoint_updatesprite(entity e)\r
767 {\r
768         string s1, s2, s3;\r
769         s1 = onslaught_controlpoint_waypointsprite_for_team(e, COLOR_TEAM1);\r
770         s2 = onslaught_controlpoint_waypointsprite_for_team(e, COLOR_TEAM2);\r
771         s3 = onslaught_controlpoint_waypointsprite_for_team(e, -1);\r
772         WaypointSprite_UpdateSprites(e.sprite, s1, s2, s3);\r
773 \r
774         float sh;\r
775         sh = !(onslaught_controlpoint_can_be_linked(e, COLOR_TEAM1) || onslaught_controlpoint_can_be_linked(e, COLOR_TEAM2));\r
776 \r
777         if(e.lastteam != e.team + 2 || e.lastshielded != sh || e.iscaptured != e.lastcaptured)\r
778         {\r
779                 if(e.iscaptured) // don't mess up build bars!\r
780                 {\r
781                         if(sh)\r
782                         {\r
783                                 WaypointSprite_UpdateMaxHealth(e.sprite, 0);\r
784                         }\r
785                         else\r
786                         {\r
787                                 WaypointSprite_UpdateMaxHealth(e.sprite, e.goalentity.max_health);\r
788                                 WaypointSprite_UpdateHealth(e.sprite, e.goalentity.health);\r
789                         }\r
790                 }\r
791                 if(e.lastshielded)\r
792                 {\r
793                         if(e.team == COLOR_TEAM1 || e.team == COLOR_TEAM2)\r
794                                 WaypointSprite_UpdateRadar(e.sprite, RADARICON_CONTROLPOINT, 0.5 * colormapPaletteColor(e.team - 1, FALSE));\r
795                         else\r
796                                 WaypointSprite_UpdateRadar(e.sprite, RADARICON_CONTROLPOINT, '0.5 0.5 0.5');\r
797                 }\r
798                 else\r
799                 {\r
800                         if(e.team == COLOR_TEAM1 || e.team == COLOR_TEAM2)\r
801                                 WaypointSprite_UpdateRadar(e.sprite, RADARICON_CONTROLPOINT, colormapPaletteColor(e.team - 1, FALSE));\r
802                         else\r
803                                 WaypointSprite_UpdateRadar(e.sprite, RADARICON_CONTROLPOINT, '0.75 0.75 0.75');\r
804                 }\r
805                 WaypointSprite_Ping(e.sprite);\r
806 \r
807                 e.lastteam = e.team + 2;\r
808                 e.lastshielded = sh;\r
809                 e.lastcaptured = e.iscaptured;\r
810         }\r
811 }\r
812 \r
813 void onslaught_generator_reset()\r
814 {\r
815         self.team = self.team_saved;\r
816         self.lasthealth = self.max_health = self.health = cvar("g_onslaught_gen_health");\r
817         self.takedamage = DAMAGE_AIM;\r
818         self.bot_attack = TRUE;\r
819         self.iscaptured = TRUE;\r
820         self.islinked = TRUE;\r
821         self.isshielded = TRUE;\r
822         self.enemy.solid = SOLID_NOT;\r
823         self.think = onslaught_generator_delayed;\r
824         self.nextthink = time + 0.2;\r
825         setmodel(self, "models/onslaught/generator.md3");\r
826 \r
827         WaypointSprite_UpdateMaxHealth(self.sprite, self.max_health);\r
828         WaypointSprite_UpdateHealth(self.sprite, self.health);\r
829 }\r
830 \r
831 /*QUAKED spawnfunc_onslaught_generator (0 .5 .8) (-32 -32 -24) (32 32 64)\r
832   Base generator.\r
833 \r
834   spawnfunc_onslaught_link entities can target this.\r
835 \r
836 keys:\r
837 "team" - team that owns this generator (5 = red, 14 = blue, etc), MUST BE SET.\r
838 "targetname" - name that spawnfunc_onslaught_link entities will use to target this.\r
839  */\r
840 void spawnfunc_onslaught_generator()\r
841 {\r
842         if (!g_onslaught)\r
843         {\r
844                 remove(self);\r
845                 return;\r
846         }\r
847 \r
848         local entity e;\r
849         precache_model("models/onslaught/generator.md3");\r
850         precache_model("models/onslaught/generator_shield.md3");\r
851         precache_model("models/onslaught/generator_dmg1.md3");\r
852         precache_model("models/onslaught/generator_dmg2.md3");\r
853         precache_model("models/onslaught/generator_dmg3.md3");\r
854         precache_model("models/onslaught/generator_dmg4.md3");\r
855         precache_model("models/onslaught/generator_dmg5.md3");\r
856         precache_model("models/onslaught/generator_dmg6.md3");\r
857         precache_model("models/onslaught/generator_dmg7.md3");\r
858         precache_model("models/onslaught/generator_dmg8.md3");\r
859         precache_model("models/onslaught/generator_dmg9.md3");\r
860         precache_model("models/onslaught/generator_dead.md3");\r
861         precache_model("models/onslaught/shockwave.md3");\r
862         precache_model("models/onslaught/shockwavetransring.md3");\r
863         precache_model("models/onslaught/gen_gib1.md3");\r
864         precache_model("models/onslaught/gen_gib2.md3");\r
865         precache_model("models/onslaught/gen_gib3.md3");\r
866         precache_model("models/onslaught/ons_ray.md3");\r
867         precache_sound("onslaught/generator_decay.wav");\r
868         precache_sound("weapons/grenade_impact.wav");\r
869         precache_sound("weapons/rocket_impact.wav");\r
870         precache_sound("onslaught/generator_underattack.wav");\r
871         precache_sound("onslaught/shockwave.wav");\r
872         precache_sound("onslaught/ons_hit1.wav");\r
873         precache_sound("onslaught/ons_hit2.wav");\r
874         precache_sound("onslaught/electricity_explode.wav");\r
875         if (!self.team)\r
876                 objerror("team must be set");\r
877         self.team_saved = self.team;\r
878         self.colormap = 1024 + (self.team - 1) * 17;\r
879         self.solid = SOLID_BBOX;\r
880         self.movetype = MOVETYPE_NONE;\r
881         self.lasthealth = self.max_health = self.health = cvar("g_onslaught_gen_health");\r
882         setmodel(self, "models/onslaught/generator.md3");\r
883         setsize(self, '-52 -52 -14', '52 52 75');\r
884         setorigin(self, self.origin);\r
885         self.takedamage = DAMAGE_AIM;\r
886         self.bot_attack = TRUE;\r
887         self.event_damage = onslaught_generator_damage;\r
888         self.iscaptured = TRUE;\r
889         self.islinked = TRUE;\r
890         self.isshielded = TRUE;\r
891         // helper entity that create fx when generator is damaged\r
892         onslaught_generator_damage_spawn(self);\r
893         // spawn shield model which indicates whether this can be damaged\r
894         self.enemy = e = spawn();\r
895         e.classname = "onslaught_generator_shield";\r
896         e.solid = SOLID_NOT;\r
897         e.movetype = MOVETYPE_NONE;\r
898         e.effects = EF_ADDITIVE;\r
899         setmodel(e, "models/onslaught/generator_shield.md3");\r
900         setorigin(e, self.origin);\r
901         e.colormap = self.colormap;\r
902         e.team = self.team;\r
903         self.think = onslaught_generator_delayed;\r
904         self.nextthink = time + 0.2;\r
905         InitializeEntity(self, onslaught_generator_delayed, INITPRIO_LAST);\r
906 \r
907         WaypointSprite_SpawnFixed(string_null, e.origin + '0 0 1' * e.maxs_z, self, sprite);\r
908         WaypointSprite_UpdateRule(self.sprite, COLOR_TEAM2, SPRITERULE_TEAMPLAY);\r
909         WaypointSprite_UpdateMaxHealth(self.sprite, self.max_health);\r
910         WaypointSprite_UpdateHealth(self.sprite, self.health);\r
911 \r
912         waypoint_spawnforitem(self);\r
913 \r
914         onslaught_updatelinks();\r
915 \r
916         self.reset = onslaught_generator_reset;\r
917 };\r
918 \r
919 .float waslinked;\r
920 .float cp_bob_spd;\r
921 .vector cp_origin, cp_bob_origin, cp_bob_dmg;\r
922 \r
923 float ons_notification_time_team1;\r
924 float ons_notification_time_team2;\r
925 \r
926 void onslaught_controlpoint_icon_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)\r
927 {\r
928         entity oself;\r
929         float nag;\r
930 \r
931         if (damage <= 0)\r
932                 return;\r
933         if (self.owner.isshielded)\r
934         {\r
935                 // this is protected by a shield, so ignore the damage\r
936                 if (time > self.pain_finished)\r
937                         if (attacker.classname == "player")\r
938                         {\r
939                                 play2(attacker, "onslaught/damageblockedbyshield.wav");\r
940                                 self.pain_finished = time + 1;\r
941                         }\r
942                 return;\r
943         }\r
944 \r
945         if (attacker.classname == "player")\r
946         {\r
947                 if(self.team == COLOR_TEAM1)\r
948                 {\r
949                         if(time - ons_notification_time_team1 > 10)\r
950                         {\r
951                                 nag = TRUE;\r
952                                 ons_notification_time_team1 = time;\r
953                         }\r
954                 }\r
955                 else if(self.team == COLOR_TEAM2)\r
956                 {\r
957                         if(time - ons_notification_time_team2 > 10)\r
958                         {\r
959                                 nag = TRUE;\r
960                                 ons_notification_time_team2 = time;\r
961                         }\r
962                 }\r
963                 else\r
964                         nag = TRUE;\r
965 \r
966                 if(nag)\r
967                         play2team(self.team, "onslaught/controlpoint_underattack.wav");\r
968         }\r
969 \r
970         self.health = self.health - damage;\r
971         if(self.owner.iscaptured)\r
972                 WaypointSprite_UpdateHealth(self.owner.sprite, self.health);\r
973         else\r
974                 WaypointSprite_UpdateBuildFinished(self.owner.sprite, time + (self.max_health - self.health) / (self.count / sys_frametime));\r
975         self.pain_finished = time + 1;\r
976         self.punchangle = (2 * randomvec() - '1 1 1') * 45;\r
977         self.cp_bob_dmg_z = (2 * random() - 1) * 15;\r
978         // colormod flash when shot\r
979         self.colormod = '2 2 2';\r
980         // particles on every hit\r
981         pointparticles(particleeffectnum("sparks"), hitloc, force*-1, 1);\r
982         //sound on every hit\r
983         if (random() < 0.5)\r
984                 sound(self, CHAN_TRIGGER, "onslaught/ons_hit1.wav", VOL_BASE+0.3, ATTN_NORM);\r
985         else\r
986                 sound(self, CHAN_TRIGGER, "onslaught/ons_hit2.wav", VOL_BASE+0.3, ATTN_NORM);\r
987 \r
988         if (self.health < 0)\r
989         {\r
990                 sound(self, CHAN_TRIGGER, "weapons/grenade_impact.wav", VOL_BASE, ATTN_NORM);\r
991                 pointparticles(particleeffectnum("rocket_explode"), self.origin, '0 0 0', 1);\r
992                 {\r
993                         string t;\r
994                         t = ColoredTeamName(attacker.team);\r
995                         bprint(ColoredTeamName(self.team), " ", self.message, " control point destroyed by ", t, "\n");\r
996                         ons_throwgib(self.origin, (2 * randomvec() - '1 1 1') * 25, "models/onslaught/controlpoint_icon_gib1.md3", 3, FALSE);\r
997                         ons_throwgib(self.origin, (2 * randomvec() - '1 1 1') * 45, "models/onslaught/controlpoint_icon_gib2.md3", 3, FALSE);\r
998                         ons_throwgib(self.origin, (2 * randomvec() - '1 1 1') * 45, "models/onslaught/controlpoint_icon_gib2.md3", 3, FALSE);\r
999                         ons_throwgib(self.origin, (2 * randomvec() - '1 1 1') * 75, "models/onslaught/controlpoint_icon_gib4.md3", 3, FALSE);\r
1000                         ons_throwgib(self.origin, (2 * randomvec() - '1 1 1') * 75, "models/onslaught/controlpoint_icon_gib4.md3", 3, FALSE);\r
1001                         ons_throwgib(self.origin, (2 * randomvec() - '1 1 1') * 75, "models/onslaught/controlpoint_icon_gib4.md3", 3, FALSE);\r
1002                         ons_throwgib(self.origin, (2 * randomvec() - '1 1 1') * 75, "models/onslaught/controlpoint_icon_gib4.md3", 3, FALSE);\r
1003                 }\r
1004                 self.owner.goalentity = world;\r
1005                 self.owner.islinked = FALSE;\r
1006                 self.owner.iscaptured = FALSE;\r
1007                 self.owner.team = 0;\r
1008                 self.owner.colormap = 1024;\r
1009 \r
1010                 WaypointSprite_UpdateMaxHealth(self.owner.sprite, 0);\r
1011 \r
1012                 onslaught_updatelinks();\r
1013 \r
1014                 // Use targets now (somebody make sure this is in the right place..)\r
1015                 oself = self;\r
1016                 self = self.owner;\r
1017                 activator = self;\r
1018                 SUB_UseTargets ();\r
1019                 self = oself;\r
1020 \r
1021 \r
1022                 self.owner.waslinked = self.owner.islinked;\r
1023                 if(self.owner.model != "models/onslaught/controlpoint_pad.md3")\r
1024                         setmodel(self.owner, "models/onslaught/controlpoint_pad.md3");\r
1025                 //setsize(self, '-32 -32 0', '32 32 8');\r
1026 \r
1027                 remove(self);\r
1028         }\r
1029 };\r
1030 \r
1031 void onslaught_controlpoint_icon_think()\r
1032 {\r
1033         entity oself;\r
1034         self.nextthink = time + sys_frametime;\r
1035         if (time > self.pain_finished + 5)\r
1036         {\r
1037                 if(self.health < self.max_health)\r
1038                 {\r
1039                         self.health = self.health + self.count;\r
1040                         if (self.health >= self.max_health)\r
1041                                 self.health = self.max_health;\r
1042                         WaypointSprite_UpdateHealth(self.owner.sprite, self.health);\r
1043                 }\r
1044         }\r
1045         if (self.health < self.max_health * 0.25)\r
1046                 setmodel(self, "models/onslaught/controlpoint_icon_dmg3.md3");\r
1047         else if (self.health < self.max_health * 0.50)\r
1048                 setmodel(self, "models/onslaught/controlpoint_icon_dmg2.md3");\r
1049         else if (self.health < self.max_health * 0.75)\r
1050                 setmodel(self, "models/onslaught/controlpoint_icon_dmg1.md3");\r
1051         else if (self.health < self.max_health * 0.90)\r
1052                 setmodel(self, "models/onslaught/controlpoint_icon.md3");\r
1053         // colormod flash when shot\r
1054         self.colormod = '1 1 1' * (2 - bound(0, (self.pain_finished - time) / 10, 1));\r
1055 \r
1056         if(self.owner.islinked != self.owner.waslinked)\r
1057         {\r
1058                 // unteam the spawnpoint if needed\r
1059                 float t;\r
1060                 t = self.owner.team;\r
1061                 if(!self.owner.islinked)\r
1062                         self.owner.team = 0;\r
1063 \r
1064                 oself = self;\r
1065                 self = self.owner;\r
1066                 activator = self;\r
1067                 SUB_UseTargets ();\r
1068                 self = oself;\r
1069 \r
1070                 self.owner.team = t;\r
1071 \r
1072                 self.owner.waslinked = self.owner.islinked;\r
1073         }\r
1074         if (self.punchangle_x > 2)\r
1075                 self.punchangle_x = self.punchangle_x - 2;\r
1076         else if (self.punchangle_x < -2)\r
1077                 self.punchangle_x = self.punchangle_x + 2;\r
1078         else\r
1079                 self.punchangle_x = 0;\r
1080         if (self.punchangle_y > 2)\r
1081                 self.punchangle_y = self.punchangle_y - 2;\r
1082         else if (self.punchangle_y < -2)\r
1083                 self.punchangle_y = self.punchangle_y + 2;\r
1084         else\r
1085                 self.punchangle_y = 0;\r
1086         if (self.punchangle_z > 2)\r
1087                 self.punchangle_z = self.punchangle_z - 2;\r
1088         else if (self.punchangle_z < -2)\r
1089                 self.punchangle_z = self.punchangle_z + 2;\r
1090         else\r
1091                 self.punchangle_z = 0;\r
1092         self.angles_x = self.punchangle_x;\r
1093         self.angles_y = self.punchangle_y + self.mangle_y;\r
1094         self.angles_z = self.punchangle_z;\r
1095         self.mangle_y = self.mangle_y + 1.5;\r
1096 \r
1097         self.cp_bob_origin_z = 4 * PI * (1 - cos(self.cp_bob_spd / 8));\r
1098         self.cp_bob_spd = self.cp_bob_spd + 0.5;\r
1099         if(self.cp_bob_dmg_z > 0)\r
1100                 self.cp_bob_dmg_z = self.cp_bob_dmg_z - 0.1;\r
1101         else\r
1102                 self.cp_bob_dmg_z = 0;\r
1103         setorigin(self,self.cp_origin + self.cp_bob_origin + self.cp_bob_dmg);\r
1104 \r
1105         // damaged fx\r
1106         if(random() < 0.6 - self.health / self.max_health)\r
1107         {\r
1108                 pointparticles(particleeffectnum("electricity_sparks"), self.origin + randompos('-10 -10 -20', '10 10 20'), '0 0 0', 1);\r
1109 \r
1110                 if(random() > 0.8)\r
1111                         sound(self, CHAN_PAIN, "onslaught/ons_spark1.wav", VOL_BASE, ATTN_NORM);\r
1112                 else if (random() > 0.5)\r
1113                         sound(self, CHAN_PAIN, "onslaught/ons_spark2.wav", VOL_BASE, ATTN_NORM);\r
1114         }\r
1115 };\r
1116 \r
1117 void onslaught_controlpoint_icon_buildthink()\r
1118 {\r
1119         local entity oself;\r
1120         float a;\r
1121 \r
1122         self.nextthink = time + sys_frametime;\r
1123 \r
1124         // only do this if there is power\r
1125         a = onslaught_controlpoint_can_be_linked(self.owner, self.owner.team);\r
1126         if(!a)\r
1127                 return;\r
1128 \r
1129         self.health = self.health + self.count;\r
1130 \r
1131         if (self.health >= self.max_health)\r
1132         {\r
1133                 self.health = self.max_health;\r
1134                 self.count = cvar("g_onslaught_cp_regen") * sys_frametime; // slow repair rate from now on\r
1135                 self.think = onslaught_controlpoint_icon_think;\r
1136                 sound(self, CHAN_TRIGGER, "onslaught/controlpoint_built.wav", VOL_BASE, ATTN_NORM);\r
1137                 bprint(ColoredTeamName(self.team), " captured ", self.owner.message, " control point\n");\r
1138                 self.owner.iscaptured = TRUE;\r
1139 \r
1140                 WaypointSprite_UpdateMaxHealth(self.owner.sprite, self.max_health);\r
1141                 WaypointSprite_UpdateHealth(self.owner.sprite, self.health);\r
1142 \r
1143                 onslaught_updatelinks();\r
1144 \r
1145                 // Use targets now (somebody make sure this is in the right place..)\r
1146                 oself = self;\r
1147                 self = self.owner;\r
1148                 activator = self;\r
1149                 SUB_UseTargets ();\r
1150                 self = oself;\r
1151                 self.cp_origin = self.origin;\r
1152                 self.cp_bob_origin = '0 0 0.1';\r
1153                 self.cp_bob_spd = 0;\r
1154         }\r
1155         self.alpha = self.health / self.max_health;\r
1156         // colormod flash when shot\r
1157         self.colormod = '1 1 1' * (2 - bound(0, (self.pain_finished - time) / 10, 1));\r
1158         if(self.owner.model != "models/onslaught/controlpoint_pad2.md3")\r
1159                 setmodel(self.owner, "models/onslaught/controlpoint_pad2.md3");\r
1160         //setsize(self, '-32 -32 0', '32 32 8');\r
1161 \r
1162         if(random() < 0.9 - self.health / self.max_health)\r
1163                 pointparticles(particleeffectnum("rage"), self.origin + 10 * randomvec(), '0 0 -1', 1);\r
1164 };\r
1165 \r
1166 \r
1167 \r
1168 \r
1169 void onslaught_controlpoint_touch()\r
1170 {\r
1171         local entity e;\r
1172         float a;\r
1173         if (other.classname != "player")\r
1174                 return;\r
1175         a = onslaught_controlpoint_attackable(self, other.team);\r
1176         if(a != 2 && a != 4)\r
1177                 return;\r
1178         // we've verified that this player has a legitimate claim to this point,\r
1179         // so start building the captured point icon (which only captures this\r
1180         // point if it successfully builds without being destroyed first)\r
1181         self.goalentity = e = spawn();\r
1182         e.classname = "onslaught_controlpoint_icon";\r
1183         e.owner = self;\r
1184         e.max_health = cvar("g_onslaught_cp_health");\r
1185         e.health = cvar("g_onslaught_cp_buildhealth");\r
1186         e.solid = SOLID_BBOX;\r
1187         e.movetype = MOVETYPE_NONE;\r
1188         setmodel(e, "models/onslaught/controlpoint_icon.md3");\r
1189         setsize(e, '-32 -32 -32', '32 32 32');\r
1190         setorigin(e, self.origin + '0 0 96');\r
1191         e.takedamage = DAMAGE_AIM;\r
1192         e.bot_attack = TRUE;\r
1193         e.event_damage = onslaught_controlpoint_icon_damage;\r
1194         e.team = other.team;\r
1195         e.colormap = 1024 + (e.team - 1) * 17;\r
1196         e.think = onslaught_controlpoint_icon_buildthink;\r
1197         e.nextthink = time + sys_frametime;\r
1198         e.count = (e.max_health - e.health) * sys_frametime / cvar("g_onslaught_cp_buildtime"); // how long it takes to build\r
1199         sound(e, CHAN_TRIGGER, "onslaught/controlpoint_build.wav", VOL_BASE, ATTN_NORM);\r
1200         self.team = e.team;\r
1201         self.colormap = e.colormap;\r
1202         WaypointSprite_UpdateBuildFinished(self.sprite, time + (e.max_health - e.health) / (e.count / sys_frametime));\r
1203         onslaught_updatelinks();\r
1204 };\r
1205 \r
1206 void onslaught_controlpoint_reset()\r
1207 {\r
1208         if(self.goalentity && self.goalentity != world)\r
1209                 remove(self.goalentity);\r
1210         self.goalentity = world;\r
1211         self.team = 0;\r
1212         self.colormap = 1024;\r
1213         self.iscaptured = FALSE;\r
1214         self.islinked = FALSE;\r
1215         self.isshielded = TRUE;\r
1216         self.enemy.solid = SOLID_NOT;\r
1217         self.enemy.colormap = self.colormap;\r
1218         self.think = self.enemy.think = SUB_Null;\r
1219         self.nextthink = 0; // don't like SUB_Null :P\r
1220         setmodel(self, "models/onslaught/controlpoint_pad.md3");\r
1221         //setsize(self, '-32 -32 0', '32 32 8');\r
1222 \r
1223         WaypointSprite_UpdateMaxHealth(self.sprite, 0);\r
1224 \r
1225         onslaught_updatelinks();\r
1226 \r
1227         activator = self;\r
1228         SUB_UseTargets(); // to reset the structures, playerspawns etc.\r
1229 }\r
1230 \r
1231 /*QUAKED spawnfunc_onslaught_controlpoint (0 .5 .8) (-32 -32 0) (32 32 128)\r
1232   Control point. Be sure to give this enough clearance so that the shootable part has room to exist\r
1233 \r
1234   This should link to an spawnfunc_onslaught_controlpoint entity or spawnfunc_onslaught_generator entity.\r
1235 \r
1236 keys:\r
1237 "targetname" - name that spawnfunc_onslaught_link entities will use to target this.\r
1238 "target" - target any entities that are tied to this control point, such as vehicles and buildable structure entities.\r
1239 "message" - name of this control point (should reflect the location in the map, such as "center bridge", "north tower", etc)\r
1240  */\r
1241 void spawnfunc_onslaught_controlpoint()\r
1242 {\r
1243         local entity e;\r
1244         if (!g_onslaught)\r
1245         {\r
1246                 remove(self);\r
1247                 return;\r
1248         }\r
1249         precache_model("models/onslaught/controlpoint_pad.md3");\r
1250         precache_model("models/onslaught/controlpoint_pad2.md3");\r
1251         precache_model("models/onslaught/controlpoint_shield.md3");\r
1252         precache_model("models/onslaught/controlpoint_icon.md3");\r
1253         precache_model("models/onslaught/controlpoint_icon_dmg1.md3");\r
1254         precache_model("models/onslaught/controlpoint_icon_dmg2.md3");\r
1255         precache_model("models/onslaught/controlpoint_icon_dmg3.md3");\r
1256         precache_model("models/onslaught/controlpoint_icon_gib1.md3");\r
1257         precache_model("models/onslaught/controlpoint_icon_gib2.md3");\r
1258         precache_model("models/onslaught/controlpoint_icon_gib4.md3");\r
1259         precache_sound("onslaught/controlpoint_build.wav");\r
1260         precache_sound("onslaught/controlpoint_built.wav");\r
1261         precache_sound("weapons/grenade_impact.wav");\r
1262         precache_sound("onslaught/damageblockedbyshield.wav");\r
1263         precache_sound("onslaught/controlpoint_underattack.wav");\r
1264         precache_sound("onslaught/ons_spark1.wav");\r
1265         precache_sound("onslaught/ons_spark2.wav");\r
1266         self.solid = SOLID_BBOX;\r
1267         self.movetype = MOVETYPE_NONE;\r
1268         setmodel(self, "models/onslaught/controlpoint_pad.md3");\r
1269         //setsize(self, '-32 -32 0', '32 32 8');\r
1270         setorigin(self, self.origin);\r
1271         self.touch = onslaught_controlpoint_touch;\r
1272         self.team = 0;\r
1273         self.colormap = 1024;\r
1274         self.iscaptured = FALSE;\r
1275         self.islinked = FALSE;\r
1276         self.isshielded = TRUE;\r
1277         // spawn shield model which indicates whether this can be damaged\r
1278         self.enemy = e = spawn();\r
1279         e.classname = "onslaught_controlpoint_shield";\r
1280         e.solid = SOLID_NOT;\r
1281         e.movetype = MOVETYPE_NONE;\r
1282         e.effects = EF_ADDITIVE;\r
1283         setmodel(e, "models/onslaught/controlpoint_shield.md3");\r
1284         //setsize(e, '-32 -32 0', '32 32 128');\r
1285         setorigin(e, self.origin);\r
1286         e.colormap = self.colormap;\r
1287 \r
1288         waypoint_spawnforitem(self);\r
1289 \r
1290         WaypointSprite_SpawnFixed(string_null, e.origin + '0 0 1' * e.maxs_z, self, sprite);\r
1291         WaypointSprite_UpdateRule(self.sprite, COLOR_TEAM2, SPRITERULE_TEAMPLAY);\r
1292 \r
1293         onslaught_updatelinks();\r
1294 \r
1295         self.reset = onslaught_controlpoint_reset;\r
1296 };\r
1297 \r
1298 float onslaught_link_send(entity to, float sendflags)\r
1299 {\r
1300         WriteByte(MSG_ENTITY, ENT_CLIENT_RADARLINK);\r
1301         WriteByte(MSG_ENTITY, sendflags);\r
1302         if(sendflags & 1)\r
1303         {\r
1304                 WriteCoord(MSG_ENTITY, self.goalentity.origin_x);\r
1305                 WriteCoord(MSG_ENTITY, self.goalentity.origin_y);\r
1306                 WriteCoord(MSG_ENTITY, self.goalentity.origin_z);\r
1307         }\r
1308         if(sendflags & 2)\r
1309         {\r
1310                 WriteCoord(MSG_ENTITY, self.enemy.origin_x);\r
1311                 WriteCoord(MSG_ENTITY, self.enemy.origin_y);\r
1312                 WriteCoord(MSG_ENTITY, self.enemy.origin_z);\r
1313         }\r
1314         if(sendflags & 4)\r
1315         {\r
1316                 WriteByte(MSG_ENTITY, self.clientcolors); // which is goalentity's color + enemy's color * 16\r
1317         }\r
1318         return TRUE;\r
1319 }\r
1320 \r
1321 void onslaught_link_checkupdate()\r
1322 {\r
1323         // TODO check if the two sides have moved (currently they won't move anyway)\r
1324         float redpower, bluepower;\r
1325 \r
1326         redpower = bluepower = 0;\r
1327         if(self.goalentity.islinked)\r
1328         {\r
1329                 if(self.goalentity.team == COLOR_TEAM1)\r
1330                         redpower = 1;\r
1331                 else if(self.goalentity.team == COLOR_TEAM2)\r
1332                         bluepower = 1;\r
1333         }\r
1334         if(self.enemy.islinked)\r
1335         {\r
1336                 if(self.enemy.team == COLOR_TEAM1)\r
1337                         redpower = 2;\r
1338                 else if(self.enemy.team == COLOR_TEAM2)\r
1339                         bluepower = 2;\r
1340         }\r
1341 \r
1342         float cc;\r
1343         if(redpower == 1 && bluepower == 2)\r
1344                 cc = (COLOR_TEAM1 - 1) * 0x01 + (COLOR_TEAM2 - 1) * 0x10;\r
1345         else if(redpower == 2 && bluepower == 1)\r
1346                 cc = (COLOR_TEAM1 - 1) * 0x10 + (COLOR_TEAM2 - 1) * 0x01;\r
1347         else if(redpower)\r
1348                 cc = (COLOR_TEAM1 - 1) * 0x11;\r
1349         else if(bluepower)\r
1350                 cc = (COLOR_TEAM2 - 1) * 0x11;\r
1351         else\r
1352                 cc = 0;\r
1353 \r
1354         //print(etos(self), " rp=", ftos(redpower), " bp=", ftos(bluepower), " ");\r
1355         //print("cc=", ftos(cc), "\n");\r
1356 \r
1357         if(cc != self.clientcolors)\r
1358         {\r
1359                 self.clientcolors = cc;\r
1360                 self.SendFlags |= 4;\r
1361         }\r
1362 \r
1363         self.nextthink = time;\r
1364 }\r
1365 \r
1366 void onslaught_link_delayed()\r
1367 {\r
1368         self.goalentity = find(world, targetname, self.target);\r
1369         self.enemy = find(world, targetname, self.target2);\r
1370         if (!self.goalentity)\r
1371                 objerror("can not find target\n");\r
1372         if (!self.enemy)\r
1373                 objerror("can not find target2\n");\r
1374         dprint(etos(self.goalentity), " linked with ", etos(self.enemy), "\n");\r
1375         self.SendFlags |= 3;\r
1376         self.think = onslaught_link_checkupdate;\r
1377         self.nextthink = time;\r
1378 }\r
1379 \r
1380 /*QUAKED spawnfunc_onslaught_link (0 .5 .8) (-16 -16 -16) (16 16 16)\r
1381   Link between control points.\r
1382 \r
1383   This entity targets two different spawnfunc_onslaught_controlpoint or spawnfunc_onslaught_generator entities, and suppresses shielding on both if they are owned by different teams.\r
1384 \r
1385 keys:\r
1386 "target" - first control point.\r
1387 "target2" - second control point.\r
1388  */\r
1389 void spawnfunc_onslaught_link()\r
1390 {\r
1391         if (!g_onslaught)\r
1392         {\r
1393                 remove(self);\r
1394                 return;\r
1395         }\r
1396         if (self.target == "" || self.target2 == "")\r
1397                 objerror("target and target2 must be set\n");\r
1398         InitializeEntity(self, onslaught_link_delayed, INITPRIO_FINDTARGET);\r
1399         Net_LinkEntity(self, FALSE, 0, onslaught_link_send);\r
1400 };\r