]> de.git.xonotic.org Git - voretournament/voretournament.git/blob - data/qcsrc/server/mode_onslaught.qc
Better Grabber impact effects
[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                 self.event_damage = SUB_Null; // won't do anything if hurt\r
599                 self.count = 0; // reset counter\r
600                 self.think = onslaught_generator_deaththink; // explosion sequence\r
601                 self.nextthink = time; // start exploding immediately\r
602                 self.think(); // do the first explosion now\r
603 \r
604                 WaypointSprite_UpdateMaxHealth(self.sprite, 0);\r
605 \r
606                 onslaught_updatelinks();\r
607         }\r
608 \r
609         if(self.health <= 0)\r
610                 setmodel(self, "models/onslaught/generator_dead.md3");\r
611         else if(self.health < self.max_health * 0.10)\r
612                 setmodel(self, "models/onslaught/generator_dmg9.md3");\r
613         else if(self.health < self.max_health * 0.20)\r
614                 setmodel(self, "models/onslaught/generator_dmg8.md3");\r
615         else if(self.health < self.max_health * 0.30)\r
616                 setmodel(self, "models/onslaught/generator_dmg7.md3");\r
617         else if(self.health < self.max_health * 0.40)\r
618                 setmodel(self, "models/onslaught/generator_dmg6.md3");\r
619         else if(self.health < self.max_health * 0.50)\r
620                 setmodel(self, "models/onslaught/generator_dmg5.md3");\r
621         else if(self.health < self.max_health * 0.60)\r
622                 setmodel(self, "models/onslaught/generator_dmg4.md3");\r
623         else if(self.health < self.max_health * 0.70)\r
624                 setmodel(self, "models/onslaught/generator_dmg3.md3");\r
625         else if(self.health < self.max_health * 0.80)\r
626                 setmodel(self, "models/onslaught/generator_dmg2.md3");\r
627         else if(self.health < self.max_health * 0.90)\r
628                 setmodel(self, "models/onslaught/generator_dmg1.md3");\r
629         setsize(self, '-52 -52 -14', '52 52 75');\r
630 \r
631         // Throw some flaming gibs on damage, more damage = more chance for gib\r
632         if(random() < damage/220)\r
633         {\r
634                 sound(self, CHAN_TRIGGER, "weapons/rocket_impact.wav", VOL_BASE, ATTN_NORM);\r
635                 i = random();\r
636                 if(i < 0.3)\r
637                         ons_throwgib(hitloc + '0 0 20', force * -1, "models/onslaught/gen_gib1.md3", 5, TRUE);\r
638                 else if(i > 0.7)\r
639                         ons_throwgib(hitloc + '0 0 20', force * -1, "models/onslaught/gen_gib2.md3", 5, TRUE);\r
640                 else\r
641                         ons_throwgib(hitloc + '0 0 20', force * -1, "models/onslaught/gen_gib3.md3", 5, TRUE);\r
642         }\r
643         else\r
644         {\r
645                 // particles on every hit\r
646                 pointparticles(particleeffectnum("sparks"), hitloc, force * -1, 1);\r
647 \r
648                 //sound on every hit\r
649                 if (random() < 0.5)\r
650                         sound(self, CHAN_TRIGGER, "onslaught/ons_hit1.wav", VOL_BASE, ATTN_NORM);\r
651                 else\r
652                         sound(self, CHAN_TRIGGER, "onslaught/ons_hit2.wav", VOL_BASE, ATTN_NORM);\r
653         }\r
654 \r
655         //throw some gibs on damage\r
656         if(random() < damage/200+0.2)\r
657                 if(random() < 0.5)\r
658                         ons_throwgib(hitloc + '0 0 20', randomvec()*360, "models/onslaught/gen_gib1.md3", 5, FALSE);\r
659 };\r
660 \r
661 // update links after a delay\r
662 void onslaught_generator_delayed()\r
663 {\r
664         onslaught_updatelinks();\r
665         // now begin normal thinking\r
666         self.think = onslaught_generator_think;\r
667         self.nextthink = time;\r
668 };\r
669 \r
670 string onslaught_generator_waypointsprite_for_team(entity e, float t)\r
671 {\r
672         if(t == e.team)\r
673         {\r
674                 if(e.team == COLOR_TEAM1)\r
675                         return "ons-gen-red";\r
676                 else if(e.team == COLOR_TEAM2)\r
677                         return "ons-gen-blue";\r
678         }\r
679         if(e.isshielded)\r
680                 return "ons-gen-shielded";\r
681         if(e.team == COLOR_TEAM1)\r
682                 return "ons-gen-red";\r
683         else if(e.team == COLOR_TEAM2)\r
684                 return "ons-gen-blue";\r
685         return "";\r
686 }\r
687 \r
688 void onslaught_generator_updatesprite(entity e)\r
689 {\r
690         string s1, s2, s3;\r
691         s1 = onslaught_generator_waypointsprite_for_team(e, COLOR_TEAM1);\r
692         s2 = onslaught_generator_waypointsprite_for_team(e, COLOR_TEAM2);\r
693         s3 = onslaught_generator_waypointsprite_for_team(e, -1);\r
694         WaypointSprite_UpdateSprites(e.sprite, s1, s2, s3);\r
695 \r
696         if(e.lastteam != e.team + 2 || e.lastshielded != e.isshielded)\r
697         {\r
698                 e.lastteam = e.team + 2;\r
699                 e.lastshielded = e.isshielded;\r
700                 if(e.lastshielded)\r
701                 {\r
702                         if(e.team == COLOR_TEAM1 || e.team == COLOR_TEAM2)\r
703                                 WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, 0.5 * colormapPaletteColor(e.team - 1, FALSE));\r
704                         else\r
705                                 WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, '0.5 0.5 0.5');\r
706                 }\r
707                 else\r
708                 {\r
709                         if(e.team == COLOR_TEAM1 || e.team == COLOR_TEAM2)\r
710                                 WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, colormapPaletteColor(e.team - 1, FALSE));\r
711                         else\r
712                                 WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, '0.75 0.75 0.75');\r
713                 }\r
714                 WaypointSprite_Ping(e.sprite);\r
715         }\r
716 }\r
717 \r
718 string onslaught_controlpoint_waypointsprite_for_team(entity e, float t)\r
719 {\r
720         float a;\r
721         if(t != -1)\r
722         {\r
723                 a = onslaught_controlpoint_attackable(e, t);\r
724                 if(a == 3 || a == 4) // ATTACK/TOUCH THIS ONE NOW\r
725                 {\r
726                         if(e.team == COLOR_TEAM1)\r
727                                 return "ons-cp-atck-red";\r
728                         else if(e.team == COLOR_TEAM2)\r
729                                 return "ons-cp-atck-blue";\r
730                         else\r
731                                 return "ons-cp-atck-neut";\r
732                 }\r
733                 else if(a == -2) // DEFEND THIS ONE NOW\r
734                 {\r
735                         if(e.team == COLOR_TEAM1)\r
736                                 return "ons-cp-dfnd-red";\r
737                         else if(e.team == COLOR_TEAM2)\r
738                                 return "ons-cp-dfnd-blue";\r
739                 }\r
740                 else if(e.team == t || a == -1 || a == 1) // own point, or fire at it\r
741                 {\r
742                         if(e.team == COLOR_TEAM1)\r
743                                 return "ons-cp-red";\r
744                         else if(e.team == COLOR_TEAM2)\r
745                                 return "ons-cp-blue";\r
746                 }\r
747                 else if(a == 2) // touch it\r
748                         return "ons-cp-neut";\r
749         }\r
750         else\r
751         {\r
752                 if(e.team == COLOR_TEAM1)\r
753                         return "ons-cp-red";\r
754                 else if(e.team == COLOR_TEAM2)\r
755                         return "ons-cp-blue";\r
756                 else\r
757                         return "ons-cp-neut";\r
758         }\r
759         return "";\r
760 }\r
761 \r
762 void onslaught_controlpoint_updatesprite(entity e)\r
763 {\r
764         string s1, s2, s3;\r
765         s1 = onslaught_controlpoint_waypointsprite_for_team(e, COLOR_TEAM1);\r
766         s2 = onslaught_controlpoint_waypointsprite_for_team(e, COLOR_TEAM2);\r
767         s3 = onslaught_controlpoint_waypointsprite_for_team(e, -1);\r
768         WaypointSprite_UpdateSprites(e.sprite, s1, s2, s3);\r
769 \r
770         float sh;\r
771         sh = !(onslaught_controlpoint_can_be_linked(e, COLOR_TEAM1) || onslaught_controlpoint_can_be_linked(e, COLOR_TEAM2));\r
772 \r
773         if(e.lastteam != e.team + 2 || e.lastshielded != sh || e.iscaptured != e.lastcaptured)\r
774         {\r
775                 if(e.iscaptured) // don't mess up build bars!\r
776                 {\r
777                         if(sh)\r
778                         {\r
779                                 WaypointSprite_UpdateMaxHealth(e.sprite, 0);\r
780                         }\r
781                         else\r
782                         {\r
783                                 WaypointSprite_UpdateMaxHealth(e.sprite, e.goalentity.max_health);\r
784                                 WaypointSprite_UpdateHealth(e.sprite, e.goalentity.health);\r
785                         }\r
786                 }\r
787                 if(e.lastshielded)\r
788                 {\r
789                         if(e.team == COLOR_TEAM1 || e.team == COLOR_TEAM2)\r
790                                 WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_CONTROLPOINT, 0.5 * colormapPaletteColor(e.team - 1, FALSE));\r
791                         else\r
792                                 WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_CONTROLPOINT, '0.5 0.5 0.5');\r
793                 }\r
794                 else\r
795                 {\r
796                         if(e.team == COLOR_TEAM1 || e.team == COLOR_TEAM2)\r
797                                 WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_CONTROLPOINT, colormapPaletteColor(e.team - 1, FALSE));\r
798                         else\r
799                                 WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_CONTROLPOINT, '0.75 0.75 0.75');\r
800                 }\r
801                 WaypointSprite_Ping(e.sprite);\r
802 \r
803                 e.lastteam = e.team + 2;\r
804                 e.lastshielded = sh;\r
805                 e.lastcaptured = e.iscaptured;\r
806         }\r
807 }\r
808 \r
809 void onslaught_generator_reset()\r
810 {\r
811         self.team = self.team_saved;\r
812         self.lasthealth = self.max_health = self.health = cvar("g_onslaught_gen_health");\r
813         self.takedamage = DAMAGE_AIM;\r
814         self.bot_attack = TRUE;\r
815         self.iscaptured = TRUE;\r
816         self.islinked = TRUE;\r
817         self.isshielded = TRUE;\r
818         self.enemy.solid = SOLID_NOT;\r
819         self.think = onslaught_generator_delayed;\r
820         self.nextthink = time + 0.2;\r
821         setmodel(self, "models/onslaught/generator.md3");\r
822 \r
823         WaypointSprite_UpdateMaxHealth(self.sprite, self.max_health);\r
824         WaypointSprite_UpdateHealth(self.sprite, self.health);\r
825 }\r
826 \r
827 /*QUAKED spawnfunc_onslaught_generator (0 .5 .8) (-32 -32 -24) (32 32 64)\r
828   Base generator.\r
829 \r
830   spawnfunc_onslaught_link entities can target this.\r
831 \r
832 keys:\r
833 "team" - team that owns this generator (5 = red, 14 = blue, etc), MUST BE SET.\r
834 "targetname" - name that spawnfunc_onslaught_link entities will use to target this.\r
835  */\r
836 void spawnfunc_onslaught_generator()\r
837 {\r
838         if (!g_onslaught)\r
839         {\r
840                 remove(self);\r
841                 return;\r
842         }\r
843 \r
844         local entity e;\r
845         precache_model("models/onslaught/generator.md3");\r
846         precache_model("models/onslaught/generator_shield.md3");\r
847         precache_model("models/onslaught/generator_dmg1.md3");\r
848         precache_model("models/onslaught/generator_dmg2.md3");\r
849         precache_model("models/onslaught/generator_dmg3.md3");\r
850         precache_model("models/onslaught/generator_dmg4.md3");\r
851         precache_model("models/onslaught/generator_dmg5.md3");\r
852         precache_model("models/onslaught/generator_dmg6.md3");\r
853         precache_model("models/onslaught/generator_dmg7.md3");\r
854         precache_model("models/onslaught/generator_dmg8.md3");\r
855         precache_model("models/onslaught/generator_dmg9.md3");\r
856         precache_model("models/onslaught/generator_dead.md3");\r
857         precache_model("models/onslaught/shockwave.md3");\r
858         precache_model("models/onslaught/shockwavetransring.md3");\r
859         precache_model("models/onslaught/gen_gib1.md3");\r
860         precache_model("models/onslaught/gen_gib2.md3");\r
861         precache_model("models/onslaught/gen_gib3.md3");\r
862         precache_model("models/onslaught/ons_ray.md3");\r
863         precache_sound("onslaught/generator_decay.wav");\r
864         precache_sound("weapons/grenade_impact.wav");\r
865         precache_sound("weapons/rocket_impact.wav");\r
866         precache_sound("onslaught/generator_underattack.wav");\r
867         precache_sound("onslaught/shockwave.wav");\r
868         precache_sound("onslaught/ons_hit1.wav");\r
869         precache_sound("onslaught/ons_hit2.wav");\r
870         precache_sound("onslaught/electricity_explode.wav");\r
871         if (!self.team)\r
872                 objerror("team must be set");\r
873         self.team_saved = self.team;\r
874         self.colormap = 1024 + (self.team - 1) * 17;\r
875         self.solid = SOLID_BBOX;\r
876         self.movetype = MOVETYPE_NONE;\r
877         self.lasthealth = self.max_health = self.health = cvar("g_onslaught_gen_health");\r
878         setmodel(self, "models/onslaught/generator.md3");\r
879         setsize(self, '-52 -52 -14', '52 52 75');\r
880         setorigin(self, self.origin);\r
881         self.takedamage = DAMAGE_AIM;\r
882         self.bot_attack = TRUE;\r
883         self.event_damage = onslaught_generator_damage;\r
884         self.iscaptured = TRUE;\r
885         self.islinked = TRUE;\r
886         self.isshielded = TRUE;\r
887         // helper entity that create fx when generator is damaged\r
888         onslaught_generator_damage_spawn(self);\r
889         // spawn shield model which indicates whether this can be damaged\r
890         self.enemy = e = spawn();\r
891         e.classname = "onslaught_generator_shield";\r
892         e.solid = SOLID_NOT;\r
893         e.movetype = MOVETYPE_NONE;\r
894         e.effects = EF_ADDITIVE;\r
895         setmodel(e, "models/onslaught/generator_shield.md3");\r
896         setorigin(e, self.origin);\r
897         e.colormap = self.colormap;\r
898         e.team = self.team;\r
899         self.think = onslaught_generator_delayed;\r
900         self.nextthink = time + 0.2;\r
901         InitializeEntity(self, onslaught_generator_delayed, INITPRIO_LAST);\r
902 \r
903         WaypointSprite_SpawnFixed(string_null, e.origin + '0 0 1' * e.maxs_z, self, sprite);\r
904         WaypointSprite_UpdateRule(self.sprite, COLOR_TEAM2, SPRITERULE_TEAMPLAY);\r
905         WaypointSprite_UpdateMaxHealth(self.sprite, self.max_health);\r
906         WaypointSprite_UpdateHealth(self.sprite, self.health);\r
907 \r
908         waypoint_spawnforitem(self);\r
909 \r
910         onslaught_updatelinks();\r
911 \r
912         self.reset = onslaught_generator_reset;\r
913 };\r
914 \r
915 .float waslinked;\r
916 .float cp_bob_spd;\r
917 .vector cp_origin, cp_bob_origin, cp_bob_dmg;\r
918 \r
919 float ons_notification_time_team1;\r
920 float ons_notification_time_team2;\r
921 \r
922 void onslaught_controlpoint_icon_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)\r
923 {\r
924         entity oself;\r
925         float nag;\r
926 \r
927         if (damage <= 0)\r
928                 return;\r
929         if (self.owner.isshielded)\r
930         {\r
931                 // this is protected by a shield, so ignore the damage\r
932                 if (time > self.pain_finished)\r
933                         if (attacker.classname == "player")\r
934                         {\r
935                                 play2(attacker, "onslaught/damageblockedbyshield.wav");\r
936                                 self.pain_finished = time + 1;\r
937                         }\r
938                 return;\r
939         }\r
940 \r
941         if (attacker.classname == "player")\r
942         {\r
943                 if(self.team == COLOR_TEAM1)\r
944                 {\r
945                         if(time - ons_notification_time_team1 > 10)\r
946                         {\r
947                                 nag = TRUE;\r
948                                 ons_notification_time_team1 = time;\r
949                         }\r
950                 }\r
951                 else if(self.team == COLOR_TEAM2)\r
952                 {\r
953                         if(time - ons_notification_time_team2 > 10)\r
954                         {\r
955                                 nag = TRUE;\r
956                                 ons_notification_time_team2 = time;\r
957                         }\r
958                 }\r
959                 else\r
960                         nag = TRUE;\r
961 \r
962                 if(nag)\r
963                         play2team(self.team, "onslaught/controlpoint_underattack.wav");\r
964         }\r
965 \r
966         self.health = self.health - damage;\r
967         if(self.owner.iscaptured)\r
968                 WaypointSprite_UpdateHealth(self.owner.sprite, self.health);\r
969         else\r
970                 WaypointSprite_UpdateBuildFinished(self.owner.sprite, time + (self.max_health - self.health) / (self.count / sys_frametime));\r
971         self.pain_finished = time + 1;\r
972         self.punchangle = (2 * randomvec() - '1 1 1') * 45;\r
973         self.cp_bob_dmg_z = (2 * random() - 1) * 15;\r
974         // colormod flash when shot\r
975         self.colormod = '2 2 2';\r
976         // particles on every hit\r
977         pointparticles(particleeffectnum("sparks"), hitloc, force*-1, 1);\r
978         //sound on every hit\r
979         if (random() < 0.5)\r
980                 sound(self, CHAN_TRIGGER, "onslaught/ons_hit1.wav", VOL_BASE+0.3, ATTN_NORM);\r
981         else\r
982                 sound(self, CHAN_TRIGGER, "onslaught/ons_hit2.wav", VOL_BASE+0.3, ATTN_NORM);\r
983 \r
984         if (self.health < 0)\r
985         {\r
986                 sound(self, CHAN_TRIGGER, "weapons/grenade_impact.wav", VOL_BASE, ATTN_NORM);\r
987                 pointparticles(particleeffectnum("rocket_explode"), self.origin, '0 0 0', 1);\r
988                 {\r
989                         string t;\r
990                         t = ColoredTeamName(attacker.team);\r
991                         bprint(ColoredTeamName(self.team), " ", self.message, " control point destroyed by ", t, "\n");\r
992                         ons_throwgib(self.origin, (2 * randomvec() - '1 1 1') * 25, "models/onslaught/controlpoint_icon_gib1.md3", 3, FALSE);\r
993                         ons_throwgib(self.origin, (2 * randomvec() - '1 1 1') * 45, "models/onslaught/controlpoint_icon_gib2.md3", 3, FALSE);\r
994                         ons_throwgib(self.origin, (2 * randomvec() - '1 1 1') * 45, "models/onslaught/controlpoint_icon_gib2.md3", 3, FALSE);\r
995                         ons_throwgib(self.origin, (2 * randomvec() - '1 1 1') * 75, "models/onslaught/controlpoint_icon_gib4.md3", 3, FALSE);\r
996                         ons_throwgib(self.origin, (2 * randomvec() - '1 1 1') * 75, "models/onslaught/controlpoint_icon_gib4.md3", 3, FALSE);\r
997                         ons_throwgib(self.origin, (2 * randomvec() - '1 1 1') * 75, "models/onslaught/controlpoint_icon_gib4.md3", 3, FALSE);\r
998                         ons_throwgib(self.origin, (2 * randomvec() - '1 1 1') * 75, "models/onslaught/controlpoint_icon_gib4.md3", 3, FALSE);\r
999                 }\r
1000                 self.owner.goalentity = world;\r
1001                 self.owner.islinked = FALSE;\r
1002                 self.owner.iscaptured = FALSE;\r
1003                 self.owner.team = 0;\r
1004                 self.owner.colormap = 1024;\r
1005 \r
1006                 WaypointSprite_UpdateMaxHealth(self.owner.sprite, 0);\r
1007 \r
1008                 onslaught_updatelinks();\r
1009 \r
1010                 // Use targets now (somebody make sure this is in the right place..)\r
1011                 oself = self;\r
1012                 self = self.owner;\r
1013                 activator = self;\r
1014                 SUB_UseTargets ();\r
1015                 self = oself;\r
1016 \r
1017 \r
1018                 self.owner.waslinked = self.owner.islinked;\r
1019                 if(self.owner.model != "models/onslaught/controlpoint_pad.md3")\r
1020                         setmodel(self.owner, "models/onslaught/controlpoint_pad.md3");\r
1021                 //setsize(self, '-32 -32 0', '32 32 8');\r
1022 \r
1023                 remove(self);\r
1024         }\r
1025 };\r
1026 \r
1027 void onslaught_controlpoint_icon_think()\r
1028 {\r
1029         entity oself;\r
1030         self.nextthink = time + sys_frametime;\r
1031         if (time > self.pain_finished + 5)\r
1032         {\r
1033                 if(self.health < self.max_health)\r
1034                 {\r
1035                         self.health = self.health + self.count;\r
1036                         if (self.health >= self.max_health)\r
1037                                 self.health = self.max_health;\r
1038                         WaypointSprite_UpdateHealth(self.owner.sprite, self.health);\r
1039                 }\r
1040         }\r
1041         if (self.health < self.max_health * 0.25)\r
1042                 setmodel(self, "models/onslaught/controlpoint_icon_dmg3.md3");\r
1043         else if (self.health < self.max_health * 0.50)\r
1044                 setmodel(self, "models/onslaught/controlpoint_icon_dmg2.md3");\r
1045         else if (self.health < self.max_health * 0.75)\r
1046                 setmodel(self, "models/onslaught/controlpoint_icon_dmg1.md3");\r
1047         else if (self.health < self.max_health * 0.90)\r
1048                 setmodel(self, "models/onslaught/controlpoint_icon.md3");\r
1049         // colormod flash when shot\r
1050         self.colormod = '1 1 1' * (2 - bound(0, (self.pain_finished - time) / 10, 1));\r
1051 \r
1052         if(self.owner.islinked != self.owner.waslinked)\r
1053         {\r
1054                 // unteam the spawnpoint if needed\r
1055                 float t;\r
1056                 t = self.owner.team;\r
1057                 if(!self.owner.islinked)\r
1058                         self.owner.team = 0;\r
1059 \r
1060                 oself = self;\r
1061                 self = self.owner;\r
1062                 activator = self;\r
1063                 SUB_UseTargets ();\r
1064                 self = oself;\r
1065 \r
1066                 self.owner.team = t;\r
1067 \r
1068                 self.owner.waslinked = self.owner.islinked;\r
1069         }\r
1070         if (self.punchangle_x > 2)\r
1071                 self.punchangle_x = self.punchangle_x - 2;\r
1072         else if (self.punchangle_x < -2)\r
1073                 self.punchangle_x = self.punchangle_x + 2;\r
1074         else\r
1075                 self.punchangle_x = 0;\r
1076         if (self.punchangle_y > 2)\r
1077                 self.punchangle_y = self.punchangle_y - 2;\r
1078         else if (self.punchangle_y < -2)\r
1079                 self.punchangle_y = self.punchangle_y + 2;\r
1080         else\r
1081                 self.punchangle_y = 0;\r
1082         if (self.punchangle_z > 2)\r
1083                 self.punchangle_z = self.punchangle_z - 2;\r
1084         else if (self.punchangle_z < -2)\r
1085                 self.punchangle_z = self.punchangle_z + 2;\r
1086         else\r
1087                 self.punchangle_z = 0;\r
1088         self.angles_x = self.punchangle_x;\r
1089         self.angles_y = self.punchangle_y + self.mangle_y;\r
1090         self.angles_z = self.punchangle_z;\r
1091         self.mangle_y = self.mangle_y + 1.5;\r
1092 \r
1093         self.cp_bob_origin_z = 4 * PI * (1 - cos(self.cp_bob_spd / 8));\r
1094         self.cp_bob_spd = self.cp_bob_spd + 0.5;\r
1095         if(self.cp_bob_dmg_z > 0)\r
1096                 self.cp_bob_dmg_z = self.cp_bob_dmg_z - 0.1;\r
1097         else\r
1098                 self.cp_bob_dmg_z = 0;\r
1099         setorigin(self,self.cp_origin + self.cp_bob_origin + self.cp_bob_dmg);\r
1100 \r
1101         // damaged fx\r
1102         if(random() < 0.6 - self.health / self.max_health)\r
1103         {\r
1104                 pointparticles(particleeffectnum("electricity_sparks"), self.origin + randompos('-10 -10 -20', '10 10 20'), '0 0 0', 1);\r
1105 \r
1106                 if(random() > 0.8)\r
1107                         sound(self, CHAN_PAIN, "onslaught/ons_spark1.wav", VOL_BASE, ATTN_NORM);\r
1108                 else if (random() > 0.5)\r
1109                         sound(self, CHAN_PAIN, "onslaught/ons_spark2.wav", VOL_BASE, ATTN_NORM);\r
1110         }\r
1111 };\r
1112 \r
1113 void onslaught_controlpoint_icon_buildthink()\r
1114 {\r
1115         local entity oself;\r
1116         float a;\r
1117 \r
1118         self.nextthink = time + sys_frametime;\r
1119 \r
1120         // only do this if there is power\r
1121         a = onslaught_controlpoint_can_be_linked(self.owner, self.owner.team);\r
1122         if(!a)\r
1123                 return;\r
1124 \r
1125         self.health = self.health + self.count;\r
1126 \r
1127         if (self.health >= self.max_health)\r
1128         {\r
1129                 self.health = self.max_health;\r
1130                 self.count = cvar("g_onslaught_cp_regen") * sys_frametime; // slow repair rate from now on\r
1131                 self.think = onslaught_controlpoint_icon_think;\r
1132                 sound(self, CHAN_TRIGGER, "onslaught/controlpoint_built.wav", VOL_BASE, ATTN_NORM);\r
1133                 bprint(ColoredTeamName(self.team), " captured ", self.owner.message, " control point\n");\r
1134                 self.owner.iscaptured = TRUE;\r
1135 \r
1136                 WaypointSprite_UpdateMaxHealth(self.owner.sprite, self.max_health);\r
1137                 WaypointSprite_UpdateHealth(self.owner.sprite, self.health);\r
1138 \r
1139                 onslaught_updatelinks();\r
1140 \r
1141                 // Use targets now (somebody make sure this is in the right place..)\r
1142                 oself = self;\r
1143                 self = self.owner;\r
1144                 activator = self;\r
1145                 SUB_UseTargets ();\r
1146                 self = oself;\r
1147                 self.cp_origin = self.origin;\r
1148                 self.cp_bob_origin = '0 0 0.1';\r
1149                 self.cp_bob_spd = 0;\r
1150         }\r
1151         self.alpha = self.health / self.max_health;\r
1152         // colormod flash when shot\r
1153         self.colormod = '1 1 1' * (2 - bound(0, (self.pain_finished - time) / 10, 1));\r
1154         if(self.owner.model != "models/onslaught/controlpoint_pad2.md3")\r
1155                 setmodel(self.owner, "models/onslaught/controlpoint_pad2.md3");\r
1156         //setsize(self, '-32 -32 0', '32 32 8');\r
1157 \r
1158         if(random() < 0.9 - self.health / self.max_health)\r
1159                 pointparticles(particleeffectnum("rage"), self.origin + 10 * randomvec(), '0 0 -1', 1);\r
1160 };\r
1161 \r
1162 \r
1163 \r
1164 \r
1165 void onslaught_controlpoint_touch()\r
1166 {\r
1167         local entity e;\r
1168         float a;\r
1169         if (other.classname != "player")\r
1170                 return;\r
1171         a = onslaught_controlpoint_attackable(self, other.team);\r
1172         if(a != 2 && a != 4)\r
1173                 return;\r
1174         // we've verified that this player has a legitimate claim to this point,\r
1175         // so start building the captured point icon (which only captures this\r
1176         // point if it successfully builds without being destroyed first)\r
1177         self.goalentity = e = spawn();\r
1178         e.classname = "onslaught_controlpoint_icon";\r
1179         e.owner = self;\r
1180         e.max_health = cvar("g_onslaught_cp_health");\r
1181         e.health = cvar("g_onslaught_cp_buildhealth");\r
1182         e.solid = SOLID_BBOX;\r
1183         e.movetype = MOVETYPE_NONE;\r
1184         setmodel(e, "models/onslaught/controlpoint_icon.md3");\r
1185         setsize(e, '-32 -32 -32', '32 32 32');\r
1186         setorigin(e, self.origin + '0 0 96');\r
1187         e.takedamage = DAMAGE_AIM;\r
1188         e.bot_attack = TRUE;\r
1189         e.event_damage = onslaught_controlpoint_icon_damage;\r
1190         e.team = other.team;\r
1191         e.colormap = 1024 + (e.team - 1) * 17;\r
1192         e.think = onslaught_controlpoint_icon_buildthink;\r
1193         e.nextthink = time + sys_frametime;\r
1194         e.count = (e.max_health - e.health) * sys_frametime / cvar("g_onslaught_cp_buildtime"); // how long it takes to build\r
1195         sound(e, CHAN_TRIGGER, "onslaught/controlpoint_build.wav", VOL_BASE, ATTN_NORM);\r
1196         self.team = e.team;\r
1197         self.colormap = e.colormap;\r
1198         WaypointSprite_UpdateBuildFinished(self.sprite, time + (e.max_health - e.health) / (e.count / sys_frametime));\r
1199         onslaught_updatelinks();\r
1200 };\r
1201 \r
1202 void onslaught_controlpoint_reset()\r
1203 {\r
1204         if(self.goalentity && self.goalentity != world)\r
1205                 remove(self.goalentity);\r
1206         self.goalentity = world;\r
1207         self.team = 0;\r
1208         self.colormap = 1024;\r
1209         self.iscaptured = FALSE;\r
1210         self.islinked = FALSE;\r
1211         self.isshielded = TRUE;\r
1212         self.enemy.solid = SOLID_NOT;\r
1213         self.enemy.colormap = self.colormap;\r
1214         self.think = self.enemy.think = SUB_Null;\r
1215         self.nextthink = 0; // don't like SUB_Null :P\r
1216         setmodel(self, "models/onslaught/controlpoint_pad.md3");\r
1217         //setsize(self, '-32 -32 0', '32 32 8');\r
1218 \r
1219         WaypointSprite_UpdateMaxHealth(self.sprite, 0);\r
1220 \r
1221         onslaught_updatelinks();\r
1222 \r
1223         activator = self;\r
1224         SUB_UseTargets(); // to reset the structures, playerspawns etc.\r
1225 }\r
1226 \r
1227 /*QUAKED spawnfunc_onslaught_controlpoint (0 .5 .8) (-32 -32 0) (32 32 128)\r
1228   Control point. Be sure to give this enough clearance so that the shootable part has room to exist\r
1229 \r
1230   This should link to an spawnfunc_onslaught_controlpoint entity or spawnfunc_onslaught_generator entity.\r
1231 \r
1232 keys:\r
1233 "targetname" - name that spawnfunc_onslaught_link entities will use to target this.\r
1234 "target" - target any entities that are tied to this control point, such as vehicles and buildable structure entities.\r
1235 "message" - name of this control point (should reflect the location in the map, such as "center bridge", "north tower", etc)\r
1236  */\r
1237 void spawnfunc_onslaught_controlpoint()\r
1238 {\r
1239         local entity e;\r
1240         if (!g_onslaught)\r
1241         {\r
1242                 remove(self);\r
1243                 return;\r
1244         }\r
1245         precache_model("models/onslaught/controlpoint_pad.md3");\r
1246         precache_model("models/onslaught/controlpoint_pad2.md3");\r
1247         precache_model("models/onslaught/controlpoint_shield.md3");\r
1248         precache_model("models/onslaught/controlpoint_icon.md3");\r
1249         precache_model("models/onslaught/controlpoint_icon_dmg1.md3");\r
1250         precache_model("models/onslaught/controlpoint_icon_dmg2.md3");\r
1251         precache_model("models/onslaught/controlpoint_icon_dmg3.md3");\r
1252         precache_model("models/onslaught/controlpoint_icon_gib1.md3");\r
1253         precache_model("models/onslaught/controlpoint_icon_gib2.md3");\r
1254         precache_model("models/onslaught/controlpoint_icon_gib4.md3");\r
1255         precache_sound("onslaught/controlpoint_build.wav");\r
1256         precache_sound("onslaught/controlpoint_built.wav");\r
1257         precache_sound("weapons/grenade_impact.wav");\r
1258         precache_sound("onslaught/damageblockedbyshield.wav");\r
1259         precache_sound("onslaught/controlpoint_underattack.wav");\r
1260         precache_sound("onslaught/ons_spark1.wav");\r
1261         precache_sound("onslaught/ons_spark2.wav");\r
1262         self.solid = SOLID_BBOX;\r
1263         self.movetype = MOVETYPE_NONE;\r
1264         setmodel(self, "models/onslaught/controlpoint_pad.md3");\r
1265         //setsize(self, '-32 -32 0', '32 32 8');\r
1266         setorigin(self, self.origin);\r
1267         self.touch = onslaught_controlpoint_touch;\r
1268         self.team = 0;\r
1269         self.colormap = 1024;\r
1270         self.iscaptured = FALSE;\r
1271         self.islinked = FALSE;\r
1272         self.isshielded = TRUE;\r
1273         // spawn shield model which indicates whether this can be damaged\r
1274         self.enemy = e = spawn();\r
1275         e.classname = "onslaught_controlpoint_shield";\r
1276         e.solid = SOLID_NOT;\r
1277         e.movetype = MOVETYPE_NONE;\r
1278         e.effects = EF_ADDITIVE;\r
1279         setmodel(e, "models/onslaught/controlpoint_shield.md3");\r
1280         //setsize(e, '-32 -32 0', '32 32 128');\r
1281         setorigin(e, self.origin);\r
1282         e.colormap = self.colormap;\r
1283 \r
1284         waypoint_spawnforitem(self);\r
1285 \r
1286         WaypointSprite_SpawnFixed(string_null, e.origin + '0 0 1' * e.maxs_z, self, sprite);\r
1287         WaypointSprite_UpdateRule(self.sprite, COLOR_TEAM2, SPRITERULE_TEAMPLAY);\r
1288 \r
1289         onslaught_updatelinks();\r
1290 \r
1291         self.reset = onslaught_controlpoint_reset;\r
1292 };\r
1293 \r
1294 float onslaught_link_send(entity to, float sendflags)\r
1295 {\r
1296         WriteByte(MSG_ENTITY, ENT_CLIENT_RADARLINK);\r
1297         WriteByte(MSG_ENTITY, sendflags);\r
1298         if(sendflags & 1)\r
1299         {\r
1300                 WriteCoord(MSG_ENTITY, self.goalentity.origin_x);\r
1301                 WriteCoord(MSG_ENTITY, self.goalentity.origin_y);\r
1302                 WriteCoord(MSG_ENTITY, self.goalentity.origin_z);\r
1303         }\r
1304         if(sendflags & 2)\r
1305         {\r
1306                 WriteCoord(MSG_ENTITY, self.enemy.origin_x);\r
1307                 WriteCoord(MSG_ENTITY, self.enemy.origin_y);\r
1308                 WriteCoord(MSG_ENTITY, self.enemy.origin_z);\r
1309         }\r
1310         if(sendflags & 4)\r
1311         {\r
1312                 WriteByte(MSG_ENTITY, self.clientcolors); // which is goalentity's color + enemy's color * 16\r
1313         }\r
1314         return TRUE;\r
1315 }\r
1316 \r
1317 void onslaught_link_checkupdate()\r
1318 {\r
1319         // TODO check if the two sides have moved (currently they won't move anyway)\r
1320         float redpower, bluepower;\r
1321 \r
1322         redpower = bluepower = 0;\r
1323         if(self.goalentity.islinked)\r
1324         {\r
1325                 if(self.goalentity.team == COLOR_TEAM1)\r
1326                         redpower = 1;\r
1327                 else if(self.goalentity.team == COLOR_TEAM2)\r
1328                         bluepower = 1;\r
1329         }\r
1330         if(self.enemy.islinked)\r
1331         {\r
1332                 if(self.enemy.team == COLOR_TEAM1)\r
1333                         redpower = 2;\r
1334                 else if(self.enemy.team == COLOR_TEAM2)\r
1335                         bluepower = 2;\r
1336         }\r
1337 \r
1338         float cc;\r
1339         if(redpower == 1 && bluepower == 2)\r
1340                 cc = (COLOR_TEAM1 - 1) * 0x01 + (COLOR_TEAM2 - 1) * 0x10;\r
1341         else if(redpower == 2 && bluepower == 1)\r
1342                 cc = (COLOR_TEAM1 - 1) * 0x10 + (COLOR_TEAM2 - 1) * 0x01;\r
1343         else if(redpower)\r
1344                 cc = (COLOR_TEAM1 - 1) * 0x11;\r
1345         else if(bluepower)\r
1346                 cc = (COLOR_TEAM2 - 1) * 0x11;\r
1347         else\r
1348                 cc = 0;\r
1349 \r
1350         //print(etos(self), " rp=", ftos(redpower), " bp=", ftos(bluepower), " ");\r
1351         //print("cc=", ftos(cc), "\n");\r
1352 \r
1353         if(cc != self.clientcolors)\r
1354         {\r
1355                 self.clientcolors = cc;\r
1356                 self.SendFlags |= 4;\r
1357         }\r
1358 \r
1359         self.nextthink = time;\r
1360 }\r
1361 \r
1362 void onslaught_link_delayed()\r
1363 {\r
1364         self.goalentity = find(world, targetname, self.target);\r
1365         self.enemy = find(world, targetname, self.target2);\r
1366         if (!self.goalentity)\r
1367                 objerror("can not find target\n");\r
1368         if (!self.enemy)\r
1369                 objerror("can not find target2\n");\r
1370         dprint(etos(self.goalentity), " linked with ", etos(self.enemy), "\n");\r
1371         self.SendFlags |= 3;\r
1372         self.think = onslaught_link_checkupdate;\r
1373         self.nextthink = time;\r
1374 }\r
1375 \r
1376 /*QUAKED spawnfunc_onslaught_link (0 .5 .8) (-16 -16 -16) (16 16 16)\r
1377   Link between control points.\r
1378 \r
1379   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
1380 \r
1381 keys:\r
1382 "target" - first control point.\r
1383 "target2" - second control point.\r
1384  */\r
1385 void spawnfunc_onslaught_link()\r
1386 {\r
1387         if (!g_onslaught)\r
1388         {\r
1389                 remove(self);\r
1390                 return;\r
1391         }\r
1392         if (self.target == "" || self.target2 == "")\r
1393                 objerror("target and target2 must be set\n");\r
1394         InitializeEntity(self, onslaught_link_delayed, INITPRIO_FINDTARGET);\r
1395         Net_LinkEntity(self, FALSE, 0, onslaught_link_send);\r
1396 };\r