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