1 void onslaught_generator_updatesprite(entity e);
2 void onslaught_controlpoint_updatesprite(entity e);
3 void onslaught_link_checkupdate();
9 .float isgenneighbor_red;
10 .float isgenneighbor_blue;
11 .float iscpneighbor_red;
12 .float iscpneighbor_blue;
19 .string model1, model2, model3;
21 void ons_gib_damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector vforce)
23 self.velocity = self.velocity + vforce;
27 void ons_throwgib_think()
31 self.nextthink = time + 0.05;
33 d = self.giblifetime - time;
37 self.think = SUB_Remove;
45 pointparticles(particleeffectnum("onslaught_generator_gib_flame"), self.origin, '0 0 0', 1);
48 void ons_throwgib(vector v_from, vector v_to, string smodel, float f_lifetime, float b_burn)
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;
61 gib.effects = EF_LOWPRECISION;
62 gib.flags = FL_NOTARGET;
64 gib.giblifetime = time + f_lifetime;
68 gib.think = ons_throwgib_think;
69 gib.nextthink = time + 0.05;
72 SUB_SetFade(gib, gib.giblifetime, 2);
75 void onslaught_updatelinks()
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");
87 dprint(etos(l), " (generator) belongs to team ", ftos(l.team), "\n");
89 dprint(etos(l), " (generator) is destroyed\n");
90 l.islinked = l.iscaptured;
91 l.isshielded = l.iscaptured;
94 // mark points as shielded and not networked
95 l = findchain(classname, "onslaught_controlpoint");
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");
107 // flow power outward from the generators through the network
111 dprint(etos(l), " (link) connects ", etos(l.goalentity), " with ", etos(l.enemy), "\n");
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)
127 if (!l.goalentity.islinked)
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");
133 else if (!l.enemy.islinked)
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");
143 // now that we know which points are powered we can mark their neighbors
144 // as unshielded if team differs
148 if (l.goalentity.islinked)
150 if (l.goalentity.team != l.enemy.team)
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;
155 if(l.goalentity.classname == "onslaught_generator")
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;
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;
170 if (l.enemy.islinked)
172 if (l.goalentity.team != l.enemy.team)
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;
177 if(l.enemy.classname == "onslaught_generator")
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;
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;
194 // now update the takedamage and alpha variables on generator shields
195 l = findchain(classname, "onslaught_generator");
200 dprint(etos(l), " (generator) is shielded\n");
202 l.takedamage = DAMAGE_NO;
203 l.bot_attack = FALSE;
207 dprint(etos(l), " (generator) is not shielded\n");
209 l.takedamage = DAMAGE_AIM;
214 // now update the takedamage and alpha variables on control point icons
215 l = findchain(classname, "onslaught_controlpoint");
220 dprint(etos(l), " (point) is shielded\n");
224 l.goalentity.takedamage = DAMAGE_NO;
225 l.goalentity.bot_attack = FALSE;
230 dprint(etos(l), " (point) is not shielded\n");
234 l.goalentity.takedamage = DAMAGE_AIM;
235 l.goalentity.bot_attack = TRUE;
238 onslaught_controlpoint_updatesprite(l);
241 // count generators owned by each team
242 t1 = t2 = t3 = t4 = 0;
243 l = findchain(classname, "onslaught_generator");
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;
253 onslaught_generator_updatesprite(l);
256 // see if multiple teams remain (if not, it's game over)
257 if (t1 + t2 + t3 + t4 < 2)
258 dprint("--- game over ---\n");
260 dprint("--- done updating links ---\n");
263 float onslaught_controlpoint_can_be_linked(entity cp, float t)
267 if(cp.isgenneighbor_red)
269 if(cp.iscpneighbor_red)
272 else if(t == COLOR_TEAM2)
274 if(cp.isgenneighbor_blue)
276 if(cp.iscpneighbor_blue)
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");
288 if (e.goalentity == cp)
290 dprint(etos(e), " (link) connects to ", etos(e.enemy), " (point)");
291 if (e.enemy.islinked)
293 dprint(" which is linked");
294 if (e.enemy.team == t)
296 dprint(" and has the correct team!\n");
300 dprint(" but has the wrong team\n");
305 else if (e.enemy == cp)
307 dprint(etos(e), " (link) connects to ", etos(e.goalentity), " (point)");
308 if (e.goalentity.islinked)
310 dprint(" which is linked");
311 if (e.goalentity.team == t)
313 dprint(" and has a team!\n");
317 dprint(" but has the wrong team\n");
328 float onslaught_controlpoint_attackable(entity cp, float t)
329 // -2: SAME TEAM, attackable by enemy!
334 // 3: attack it (HIGH PRIO)
335 // 4: touch it (HIGH PRIO)
343 else if(cp.goalentity)
345 // if there's already an icon built, nothing happens
348 a = onslaught_controlpoint_can_be_linked(cp, COLOR_TEAM1 + COLOR_TEAM2 - t);
349 if(a) // attackable by enemy?
350 return -2; // EMERGENCY!
353 // we know it can be linked, so no need to check
355 a = onslaught_controlpoint_can_be_linked(cp, t);
356 if(a == 2) // near our generator?
357 return 3; // EMERGENCY!
363 if(onslaught_controlpoint_can_be_linked(cp, t))
365 a = onslaught_controlpoint_can_be_linked(cp, COLOR_TEAM1 + COLOR_TEAM2 - t);
367 return 4; // GET THIS ONE NOW!
369 return 2; // TOUCH ME
375 void onslaught_generator_think()
379 self.nextthink = ceil(time + 1);
382 if (cvar("timelimit"))
383 if (time > game_starttime + cvar("timelimit") * 60)
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);
389 e = findchain(classname, "onslaught_controlpoint");
392 if (e.team != self.team)
397 d = d * self.max_health / 300;
398 Damage(self, self, self, d, DEATH_HURTTRIGGER, self.origin, '0 0 0');
403 void onslaught_generator_ring_spawn(vector org)
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);
408 void onslaught_generator_ray_think()
410 self.nextthink = time + 0.05;
413 self.think = SUB_Remove;
426 void onslaught_generator_ray_spawn(vector org)
430 setmodel(e, "models/onslaught/ons_ray.md3");
432 e.angles = randomvec() * 360;
434 e.scale = random() * 5 + 8;
435 e.think = onslaught_generator_ray_think;
436 e.nextthink = time + 0.05;
439 void onslaught_generator_shockwave_spawn(vector org)
441 shockwave_spawn("models/onslaught/shockwave.md3", org, -64, 0.75, 0.5);
444 void onslaught_generator_damage_think()
446 if(self.owner.health < 0)
448 self.think = SUB_Remove;
451 self.nextthink = time+0.1;
453 // damaged fx (less probable the more damaged is the generator)
454 if(random() < 0.9 - self.owner.health / self.owner.max_health)
457 pointparticles(particleeffectnum("electro_ballexplode"), self.origin + randompos('-50 -50 -20', '50 50 50'), '0 0 0', 1);
458 sound(self, CHAN_TRIGGER, "onslaught/electricity_explode.wav", VOL_BASE, ATTN_NORM);
461 pointparticles(particleeffectnum("torch_small"), self.origin + randompos('-60 -60 -20', '60 60 60'), '0 0 0', 1);
464 void onslaught_generator_damage_spawn(entity gd_owner)
469 e.health = self.owner.health;
470 setorigin(e, gd_owner.origin);
471 e.think = onslaught_generator_damage_think;
472 e.nextthink = time+1;
475 void onslaught_generator_deaththink()
484 if(self.count==40||self.count==20)
486 onslaught_generator_ring_spawn(self.origin);
487 sound(self, CHAN_TRIGGER, "onslaught/shockwave.wav", VOL_BASE, ATTN_NORM);
495 ons_throwgib(self.origin + '0 0 40', (100 * randomvec() - '1 1 1') * 11 + '0 0 20', "models/onslaught/gen_gib1.md3", 6, TRUE);
497 ons_throwgib(self.origin + '0 0 40', (100 * randomvec() - '1 1 1') * 12 + '0 0 20', "models/onslaught/gen_gib2.md3", 6, TRUE);
499 ons_throwgib(self.origin + '0 0 40', (100 * randomvec() - '1 1 1') * 13 + '0 0 20', "models/onslaught/gen_gib3.md3", 6, TRUE);
505 org = self.origin + randompos('-30 -30 -30' * i + '0 0 -20', '30 30 30' * i + '0 0 20');
506 pointparticles(particleeffectnum("onslaught_generator_gib_explode"), org, '0 0 0', 1);
509 // Short explosion sound + small explosion
512 te_explosion(self.origin);
513 sound(self, CHAN_TRIGGER, "weapons/grenade_impact.wav", VOL_BASE, ATTN_NORM);
517 org = self.origin + randompos(self.mins + '8 8 8', self.maxs + '-8 -8 -8');
518 pointparticles(particleeffectnum("onslaught_generator_smallexplosion"), org, '0 0 0', 1);
523 onslaught_generator_ray_spawn(self.origin);
531 onslaught_generator_shockwave_spawn(org);
532 pointparticles(particleeffectnum("onslaught_generator_finalexplosion"), org, '0 0 0', 1);
533 sound(self, CHAN_TRIGGER, "weapons/rocket_impact.wav", VOL_BASE, ATTN_NORM);
536 self.nextthink = time + 0.05;
538 self.count = self.count - 1;
541 void onslaught_generator_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
548 if (attacker != self)
552 // this is protected by a shield, so ignore the damage
553 if (time > self.pain_finished)
554 if (attacker.classname == "player")
556 play2(attacker, "onslaught/damageblockedbyshield.wav");
557 self.pain_finished = time + 1;
561 if (time > self.pain_finished)
563 self.pain_finished = time + 10;
564 bprint(ColoredTeamName(self.team), " generator under attack!\n");
565 play2team(self.team, "onslaught/generator_underattack.wav");
568 self.health = self.health - damage;
569 WaypointSprite_UpdateHealth(self.sprite, self.health);
570 // choose an animation frame based on health
571 self.frame = 10 * bound(0, (1 - self.health / self.max_health), 1);
572 // see if the generator is still functional, or dying
575 #ifdef ONSLAUGHT_SPAM
577 lh = ceil(self.lasthealth / 100) * 100;
578 h = ceil(self.health / 100) * 100;
580 bprint(ColoredTeamName(self.team), " generator has less than ", ftos(h), " health remaining\n");
582 self.lasthealth = self.health;
584 else if not(inWarmupStage)
586 if (attacker == self)
587 bprint(ColoredTeamName(self.team), " generator spontaneously exploded due to overtime!\n");
591 t = ColoredTeamName(attacker.team);
592 bprint(ColoredTeamName(self.team), " generator destroyed by ", t, "!\n");
594 self.iscaptured = FALSE;
595 self.islinked = FALSE;
596 self.isshielded = FALSE;
597 self.takedamage = DAMAGE_NO; // can't be hurt anymore
598 self.event_damage = SUB_Null; // won't do anything if hurt
599 self.count = 0; // reset counter
600 self.think = onslaught_generator_deaththink; // explosion sequence
601 self.nextthink = time; // start exploding immediately
602 self.think(); // do the first explosion now
604 WaypointSprite_UpdateMaxHealth(self.sprite, 0);
606 onslaught_updatelinks();
610 setmodel(self, "models/onslaught/generator_dead.md3");
611 else if(self.health < self.max_health * 0.10)
612 setmodel(self, "models/onslaught/generator_dmg9.md3");
613 else if(self.health < self.max_health * 0.20)
614 setmodel(self, "models/onslaught/generator_dmg8.md3");
615 else if(self.health < self.max_health * 0.30)
616 setmodel(self, "models/onslaught/generator_dmg7.md3");
617 else if(self.health < self.max_health * 0.40)
618 setmodel(self, "models/onslaught/generator_dmg6.md3");
619 else if(self.health < self.max_health * 0.50)
620 setmodel(self, "models/onslaught/generator_dmg5.md3");
621 else if(self.health < self.max_health * 0.60)
622 setmodel(self, "models/onslaught/generator_dmg4.md3");
623 else if(self.health < self.max_health * 0.70)
624 setmodel(self, "models/onslaught/generator_dmg3.md3");
625 else if(self.health < self.max_health * 0.80)
626 setmodel(self, "models/onslaught/generator_dmg2.md3");
627 else if(self.health < self.max_health * 0.90)
628 setmodel(self, "models/onslaught/generator_dmg1.md3");
629 setsize(self, '-52 -52 -14', '52 52 75');
631 // Throw some flaming gibs on damage, more damage = more chance for gib
632 if(random() < damage/220)
634 sound(self, CHAN_TRIGGER, "weapons/rocket_impact.wav", VOL_BASE, ATTN_NORM);
637 ons_throwgib(hitloc + '0 0 20', force * -1, "models/onslaught/gen_gib1.md3", 5, TRUE);
639 ons_throwgib(hitloc + '0 0 20', force * -1, "models/onslaught/gen_gib2.md3", 5, TRUE);
641 ons_throwgib(hitloc + '0 0 20', force * -1, "models/onslaught/gen_gib3.md3", 5, TRUE);
645 // particles on every hit
646 pointparticles(particleeffectnum("sparks"), hitloc, force * -1, 1);
650 sound(self, CHAN_TRIGGER, "onslaught/ons_hit1.wav", VOL_BASE, ATTN_NORM);
652 sound(self, CHAN_TRIGGER, "onslaught/ons_hit2.wav", VOL_BASE, ATTN_NORM);
655 //throw some gibs on damage
656 if(random() < damage/200+0.2)
658 ons_throwgib(hitloc + '0 0 20', randomvec()*360, "models/onslaught/gen_gib1.md3", 5, FALSE);
661 // update links after a delay
662 void onslaught_generator_delayed()
664 onslaught_updatelinks();
665 // now begin normal thinking
666 self.think = onslaught_generator_think;
667 self.nextthink = time;
670 string onslaught_generator_waypointsprite_for_team(entity e, float t)
674 if(e.team == COLOR_TEAM1)
675 return "ons-gen-red";
676 else if(e.team == COLOR_TEAM2)
677 return "ons-gen-blue";
680 return "ons-gen-shielded";
681 if(e.team == COLOR_TEAM1)
682 return "ons-gen-red";
683 else if(e.team == COLOR_TEAM2)
684 return "ons-gen-blue";
688 void onslaught_generator_updatesprite(entity e)
691 s1 = onslaught_generator_waypointsprite_for_team(e, COLOR_TEAM1);
692 s2 = onslaught_generator_waypointsprite_for_team(e, COLOR_TEAM2);
693 s3 = onslaught_generator_waypointsprite_for_team(e, -1);
694 WaypointSprite_UpdateSprites(e.sprite, s1, s2, s3);
696 if(e.lastteam != e.team + 2 || e.lastshielded != e.isshielded)
698 e.lastteam = e.team + 2;
699 e.lastshielded = e.isshielded;
702 if(e.team == COLOR_TEAM1 || e.team == COLOR_TEAM2)
703 WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, 0.5 * colormapPaletteColor(e.team - 1, FALSE));
705 WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, '0.5 0.5 0.5');
709 if(e.team == COLOR_TEAM1 || e.team == COLOR_TEAM2)
710 WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, colormapPaletteColor(e.team - 1, FALSE));
712 WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, '0.75 0.75 0.75');
714 WaypointSprite_Ping(e.sprite);
718 string onslaught_controlpoint_waypointsprite_for_team(entity e, float t)
723 a = onslaught_controlpoint_attackable(e, t);
724 if(a == 3 || a == 4) // ATTACK/TOUCH THIS ONE NOW
726 if(e.team == COLOR_TEAM1)
727 return "ons-cp-atck-red";
728 else if(e.team == COLOR_TEAM2)
729 return "ons-cp-atck-blue";
731 return "ons-cp-atck-neut";
733 else if(a == -2) // DEFEND THIS ONE NOW
735 if(e.team == COLOR_TEAM1)
736 return "ons-cp-dfnd-red";
737 else if(e.team == COLOR_TEAM2)
738 return "ons-cp-dfnd-blue";
740 else if(e.team == t || a == -1 || a == 1) // own point, or fire at it
742 if(e.team == COLOR_TEAM1)
744 else if(e.team == COLOR_TEAM2)
745 return "ons-cp-blue";
747 else if(a == 2) // touch it
748 return "ons-cp-neut";
752 if(e.team == COLOR_TEAM1)
754 else if(e.team == COLOR_TEAM2)
755 return "ons-cp-blue";
757 return "ons-cp-neut";
762 void onslaught_controlpoint_updatesprite(entity e)
765 s1 = onslaught_controlpoint_waypointsprite_for_team(e, COLOR_TEAM1);
766 s2 = onslaught_controlpoint_waypointsprite_for_team(e, COLOR_TEAM2);
767 s3 = onslaught_controlpoint_waypointsprite_for_team(e, -1);
768 WaypointSprite_UpdateSprites(e.sprite, s1, s2, s3);
771 sh = !(onslaught_controlpoint_can_be_linked(e, COLOR_TEAM1) || onslaught_controlpoint_can_be_linked(e, COLOR_TEAM2));
773 if(e.lastteam != e.team + 2 || e.lastshielded != sh || e.iscaptured != e.lastcaptured)
775 if(e.iscaptured) // don't mess up build bars!
779 WaypointSprite_UpdateMaxHealth(e.sprite, 0);
783 WaypointSprite_UpdateMaxHealth(e.sprite, e.goalentity.max_health);
784 WaypointSprite_UpdateHealth(e.sprite, e.goalentity.health);
789 if(e.team == COLOR_TEAM1 || e.team == COLOR_TEAM2)
790 WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_CONTROLPOINT, 0.5 * colormapPaletteColor(e.team - 1, FALSE));
792 WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_CONTROLPOINT, '0.5 0.5 0.5');
796 if(e.team == COLOR_TEAM1 || e.team == COLOR_TEAM2)
797 WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_CONTROLPOINT, colormapPaletteColor(e.team - 1, FALSE));
799 WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_CONTROLPOINT, '0.75 0.75 0.75');
801 WaypointSprite_Ping(e.sprite);
803 e.lastteam = e.team + 2;
805 e.lastcaptured = e.iscaptured;
809 void onslaught_generator_reset()
811 self.team = self.team_saved;
812 self.lasthealth = self.max_health = self.health = cvar("g_onslaught_gen_health");
813 self.takedamage = DAMAGE_AIM;
814 self.bot_attack = TRUE;
815 self.iscaptured = TRUE;
816 self.islinked = TRUE;
817 self.isshielded = TRUE;
818 self.enemy.solid = SOLID_NOT;
819 self.think = onslaught_generator_delayed;
820 self.nextthink = time + 0.2;
821 setmodel(self, "models/onslaught/generator.md3");
823 WaypointSprite_UpdateMaxHealth(self.sprite, self.max_health);
824 WaypointSprite_UpdateHealth(self.sprite, self.health);
827 /*QUAKED spawnfunc_onslaught_generator (0 .5 .8) (-32 -32 -24) (32 32 64)
830 spawnfunc_onslaught_link entities can target this.
833 "team" - team that owns this generator (5 = red, 14 = blue, etc), MUST BE SET.
834 "targetname" - name that spawnfunc_onslaught_link entities will use to target this.
836 void spawnfunc_onslaught_generator()
845 precache_model("models/onslaught/generator.md3");
846 precache_model("models/onslaught/generator_shield.md3");
847 precache_model("models/onslaught/generator_dmg1.md3");
848 precache_model("models/onslaught/generator_dmg2.md3");
849 precache_model("models/onslaught/generator_dmg3.md3");
850 precache_model("models/onslaught/generator_dmg4.md3");
851 precache_model("models/onslaught/generator_dmg5.md3");
852 precache_model("models/onslaught/generator_dmg6.md3");
853 precache_model("models/onslaught/generator_dmg7.md3");
854 precache_model("models/onslaught/generator_dmg8.md3");
855 precache_model("models/onslaught/generator_dmg9.md3");
856 precache_model("models/onslaught/generator_dead.md3");
857 precache_model("models/onslaught/shockwave.md3");
858 precache_model("models/onslaught/shockwavetransring.md3");
859 precache_model("models/onslaught/gen_gib1.md3");
860 precache_model("models/onslaught/gen_gib2.md3");
861 precache_model("models/onslaught/gen_gib3.md3");
862 precache_model("models/onslaught/ons_ray.md3");
863 precache_sound("onslaught/generator_decay.wav");
864 precache_sound("weapons/grenade_impact.wav");
865 precache_sound("weapons/rocket_impact.wav");
866 precache_sound("onslaught/generator_underattack.wav");
867 precache_sound("onslaught/shockwave.wav");
868 precache_sound("onslaught/ons_hit1.wav");
869 precache_sound("onslaught/ons_hit2.wav");
870 precache_sound("onslaught/electricity_explode.wav");
872 objerror("team must be set");
873 self.team_saved = self.team;
874 self.colormap = 1024 + (self.team - 1) * 17;
875 self.solid = SOLID_BBOX;
876 self.movetype = MOVETYPE_NONE;
877 self.lasthealth = self.max_health = self.health = cvar("g_onslaught_gen_health");
878 setmodel(self, "models/onslaught/generator.md3");
879 setsize(self, '-52 -52 -14', '52 52 75');
880 setorigin(self, self.origin);
881 self.takedamage = DAMAGE_AIM;
882 self.bot_attack = TRUE;
883 self.event_damage = onslaught_generator_damage;
884 self.iscaptured = TRUE;
885 self.islinked = TRUE;
886 self.isshielded = TRUE;
887 // helper entity that create fx when generator is damaged
888 onslaught_generator_damage_spawn(self);
889 // spawn shield model which indicates whether this can be damaged
890 self.enemy = e = spawn();
891 e.classname = "onslaught_generator_shield";
893 e.movetype = MOVETYPE_NONE;
894 e.effects = EF_ADDITIVE;
895 setmodel(e, "models/onslaught/generator_shield.md3");
896 setorigin(e, self.origin);
897 e.colormap = self.colormap;
899 self.think = onslaught_generator_delayed;
900 self.nextthink = time + 0.2;
901 InitializeEntity(self, onslaught_generator_delayed, INITPRIO_LAST);
903 WaypointSprite_SpawnFixed(string_null, e.origin + '0 0 1' * e.maxs_z, self, sprite);
904 WaypointSprite_UpdateRule(self.sprite, COLOR_TEAM2, SPRITERULE_TEAMPLAY);
905 WaypointSprite_UpdateMaxHealth(self.sprite, self.max_health);
906 WaypointSprite_UpdateHealth(self.sprite, self.health);
908 waypoint_spawnforitem(self);
910 onslaught_updatelinks();
912 self.reset = onslaught_generator_reset;
917 .vector cp_origin, cp_bob_origin, cp_bob_dmg;
919 float ons_notification_time_team1;
920 float ons_notification_time_team2;
922 void onslaught_controlpoint_icon_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
929 if (self.owner.isshielded)
931 // this is protected by a shield, so ignore the damage
932 if (time > self.pain_finished)
933 if (attacker.classname == "player")
935 play2(attacker, "onslaught/damageblockedbyshield.wav");
936 self.pain_finished = time + 1;
941 if (attacker.classname == "player")
943 if(self.team == COLOR_TEAM1)
945 if(time - ons_notification_time_team1 > 10)
948 ons_notification_time_team1 = time;
951 else if(self.team == COLOR_TEAM2)
953 if(time - ons_notification_time_team2 > 10)
956 ons_notification_time_team2 = time;
963 play2team(self.team, "onslaught/controlpoint_underattack.wav");
966 self.health = self.health - damage;
967 if(self.owner.iscaptured)
968 WaypointSprite_UpdateHealth(self.owner.sprite, self.health);
970 WaypointSprite_UpdateBuildFinished(self.owner.sprite, time + (self.max_health - self.health) / (self.count / sys_frametime));
971 self.pain_finished = time + 1;
972 self.punchangle = (2 * randomvec() - '1 1 1') * 45;
973 self.cp_bob_dmg_z = (2 * random() - 1) * 15;
974 // colormod flash when shot
975 self.colormod = '2 2 2';
976 // particles on every hit
977 pointparticles(particleeffectnum("sparks"), hitloc, force*-1, 1);
980 sound(self, CHAN_TRIGGER, "onslaught/ons_hit1.wav", VOL_BASE+0.3, ATTN_NORM);
982 sound(self, CHAN_TRIGGER, "onslaught/ons_hit2.wav", VOL_BASE+0.3, ATTN_NORM);
986 sound(self, CHAN_TRIGGER, "weapons/grenade_impact.wav", VOL_BASE, ATTN_NORM);
987 pointparticles(particleeffectnum("rocket_explode"), self.origin, '0 0 0', 1);
990 t = ColoredTeamName(attacker.team);
991 bprint(ColoredTeamName(self.team), " ", self.message, " control point destroyed by ", t, "\n");
992 ons_throwgib(self.origin, (2 * randomvec() - '1 1 1') * 25, "models/onslaught/controlpoint_icon_gib1.md3", 3, FALSE);
993 ons_throwgib(self.origin, (2 * randomvec() - '1 1 1') * 45, "models/onslaught/controlpoint_icon_gib2.md3", 3, FALSE);
994 ons_throwgib(self.origin, (2 * randomvec() - '1 1 1') * 45, "models/onslaught/controlpoint_icon_gib2.md3", 3, FALSE);
995 ons_throwgib(self.origin, (2 * randomvec() - '1 1 1') * 75, "models/onslaught/controlpoint_icon_gib4.md3", 3, FALSE);
996 ons_throwgib(self.origin, (2 * randomvec() - '1 1 1') * 75, "models/onslaught/controlpoint_icon_gib4.md3", 3, FALSE);
997 ons_throwgib(self.origin, (2 * randomvec() - '1 1 1') * 75, "models/onslaught/controlpoint_icon_gib4.md3", 3, FALSE);
998 ons_throwgib(self.origin, (2 * randomvec() - '1 1 1') * 75, "models/onslaught/controlpoint_icon_gib4.md3", 3, FALSE);
1000 self.owner.goalentity = world;
1001 self.owner.islinked = FALSE;
1002 self.owner.iscaptured = FALSE;
1003 self.owner.team = 0;
1004 self.owner.colormap = 1024;
1006 WaypointSprite_UpdateMaxHealth(self.owner.sprite, 0);
1008 onslaught_updatelinks();
1010 // Use targets now (somebody make sure this is in the right place..)
1018 self.owner.waslinked = self.owner.islinked;
1019 if(self.owner.model != "models/onslaught/controlpoint_pad.md3")
1020 setmodel(self.owner, "models/onslaught/controlpoint_pad.md3");
1021 //setsize(self, '-32 -32 0', '32 32 8');
1027 void onslaught_controlpoint_icon_think()
1030 self.nextthink = time + sys_frametime;
1031 if (time > self.pain_finished + 5)
1033 if(self.health < self.max_health)
1035 self.health = self.health + self.count;
1036 if (self.health >= self.max_health)
1037 self.health = self.max_health;
1038 WaypointSprite_UpdateHealth(self.owner.sprite, self.health);
1041 if (self.health < self.max_health * 0.25)
1042 setmodel(self, "models/onslaught/controlpoint_icon_dmg3.md3");
1043 else if (self.health < self.max_health * 0.50)
1044 setmodel(self, "models/onslaught/controlpoint_icon_dmg2.md3");
1045 else if (self.health < self.max_health * 0.75)
1046 setmodel(self, "models/onslaught/controlpoint_icon_dmg1.md3");
1047 else if (self.health < self.max_health * 0.90)
1048 setmodel(self, "models/onslaught/controlpoint_icon.md3");
1049 // colormod flash when shot
1050 self.colormod = '1 1 1' * (2 - bound(0, (self.pain_finished - time) / 10, 1));
1052 if(self.owner.islinked != self.owner.waslinked)
1054 // unteam the spawnpoint if needed
1056 t = self.owner.team;
1057 if(!self.owner.islinked)
1058 self.owner.team = 0;
1066 self.owner.team = t;
1068 self.owner.waslinked = self.owner.islinked;
1070 if (self.punchangle_x > 2)
1071 self.punchangle_x = self.punchangle_x - 2;
1072 else if (self.punchangle_x < -2)
1073 self.punchangle_x = self.punchangle_x + 2;
1075 self.punchangle_x = 0;
1076 if (self.punchangle_y > 2)
1077 self.punchangle_y = self.punchangle_y - 2;
1078 else if (self.punchangle_y < -2)
1079 self.punchangle_y = self.punchangle_y + 2;
1081 self.punchangle_y = 0;
1082 if (self.punchangle_z > 2)
1083 self.punchangle_z = self.punchangle_z - 2;
1084 else if (self.punchangle_z < -2)
1085 self.punchangle_z = self.punchangle_z + 2;
1087 self.punchangle_z = 0;
1088 self.angles_x = self.punchangle_x;
1089 self.angles_y = self.punchangle_y + self.mangle_y;
1090 self.angles_z = self.punchangle_z;
1091 self.mangle_y = self.mangle_y + 1.5;
1093 self.cp_bob_origin_z = 4 * PI * (1 - cos(self.cp_bob_spd / 8));
1094 self.cp_bob_spd = self.cp_bob_spd + 0.5;
1095 if(self.cp_bob_dmg_z > 0)
1096 self.cp_bob_dmg_z = self.cp_bob_dmg_z - 0.1;
1098 self.cp_bob_dmg_z = 0;
1099 setorigin(self,self.cp_origin + self.cp_bob_origin + self.cp_bob_dmg);
1102 if(random() < 0.6 - self.health / self.max_health)
1104 pointparticles(particleeffectnum("electricity_sparks"), self.origin + randompos('-10 -10 -20', '10 10 20'), '0 0 0', 1);
1107 sound(self, CHAN_PAIN, "onslaught/ons_spark1.wav", VOL_BASE, ATTN_NORM);
1108 else if (random() > 0.5)
1109 sound(self, CHAN_PAIN, "onslaught/ons_spark2.wav", VOL_BASE, ATTN_NORM);
1113 void onslaught_controlpoint_icon_buildthink()
1118 self.nextthink = time + sys_frametime;
1120 // only do this if there is power
1121 a = onslaught_controlpoint_can_be_linked(self.owner, self.owner.team);
1125 self.health = self.health + self.count;
1127 if (self.health >= self.max_health)
1129 self.health = self.max_health;
1130 self.count = cvar("g_onslaught_cp_regen") * sys_frametime; // slow repair rate from now on
1131 self.think = onslaught_controlpoint_icon_think;
1132 sound(self, CHAN_TRIGGER, "onslaught/controlpoint_built.wav", VOL_BASE, ATTN_NORM);
1133 bprint(ColoredTeamName(self.team), " captured ", self.owner.message, " control point\n");
1134 self.owner.iscaptured = TRUE;
1136 WaypointSprite_UpdateMaxHealth(self.owner.sprite, self.max_health);
1137 WaypointSprite_UpdateHealth(self.owner.sprite, self.health);
1139 onslaught_updatelinks();
1141 // Use targets now (somebody make sure this is in the right place..)
1147 self.cp_origin = self.origin;
1148 self.cp_bob_origin = '0 0 0.1';
1149 self.cp_bob_spd = 0;
1151 self.alpha = self.health / self.max_health;
1152 // colormod flash when shot
1153 self.colormod = '1 1 1' * (2 - bound(0, (self.pain_finished - time) / 10, 1));
1154 if(self.owner.model != "models/onslaught/controlpoint_pad2.md3")
1155 setmodel(self.owner, "models/onslaught/controlpoint_pad2.md3");
1156 //setsize(self, '-32 -32 0', '32 32 8');
1158 if(random() < 0.9 - self.health / self.max_health)
1159 pointparticles(particleeffectnum("rage"), self.origin + 10 * randomvec(), '0 0 -1', 1);
1165 void onslaught_controlpoint_touch()
1169 if (other.classname != "player")
1171 a = onslaught_controlpoint_attackable(self, other.team);
1172 if(a != 2 && a != 4)
1174 // we've verified that this player has a legitimate claim to this point,
1175 // so start building the captured point icon (which only captures this
1176 // point if it successfully builds without being destroyed first)
1177 self.goalentity = e = spawn();
1178 e.classname = "onslaught_controlpoint_icon";
1180 e.max_health = cvar("g_onslaught_cp_health");
1181 e.health = cvar("g_onslaught_cp_buildhealth");
1182 e.solid = SOLID_BBOX;
1183 e.movetype = MOVETYPE_NONE;
1184 setmodel(e, "models/onslaught/controlpoint_icon.md3");
1185 setsize(e, '-32 -32 -32', '32 32 32');
1186 setorigin(e, self.origin + '0 0 96');
1187 e.takedamage = DAMAGE_AIM;
1188 e.bot_attack = TRUE;
1189 e.event_damage = onslaught_controlpoint_icon_damage;
1190 e.team = other.team;
1191 e.colormap = 1024 + (e.team - 1) * 17;
1192 e.think = onslaught_controlpoint_icon_buildthink;
1193 e.nextthink = time + sys_frametime;
1194 e.count = (e.max_health - e.health) * sys_frametime / cvar("g_onslaught_cp_buildtime"); // how long it takes to build
1195 sound(e, CHAN_TRIGGER, "onslaught/controlpoint_build.wav", VOL_BASE, ATTN_NORM);
1197 self.colormap = e.colormap;
1198 WaypointSprite_UpdateBuildFinished(self.sprite, time + (e.max_health - e.health) / (e.count / sys_frametime));
1199 onslaught_updatelinks();
1202 void onslaught_controlpoint_reset()
1204 if(self.goalentity && self.goalentity != world)
1205 remove(self.goalentity);
1206 self.goalentity = world;
1208 self.colormap = 1024;
1209 self.iscaptured = FALSE;
1210 self.islinked = FALSE;
1211 self.isshielded = TRUE;
1212 self.enemy.solid = SOLID_NOT;
1213 self.enemy.colormap = self.colormap;
1214 self.think = self.enemy.think = SUB_Null;
1215 self.nextthink = 0; // don't like SUB_Null :P
1216 setmodel(self, "models/onslaught/controlpoint_pad.md3");
1217 //setsize(self, '-32 -32 0', '32 32 8');
1219 WaypointSprite_UpdateMaxHealth(self.sprite, 0);
1221 onslaught_updatelinks();
1224 SUB_UseTargets(); // to reset the structures, playerspawns etc.
1227 /*QUAKED spawnfunc_onslaught_controlpoint (0 .5 .8) (-32 -32 0) (32 32 128)
1228 Control point. Be sure to give this enough clearance so that the shootable part has room to exist
1230 This should link to an spawnfunc_onslaught_controlpoint entity or spawnfunc_onslaught_generator entity.
1233 "targetname" - name that spawnfunc_onslaught_link entities will use to target this.
1234 "target" - target any entities that are tied to this control point, such as vehicles and buildable structure entities.
1235 "message" - name of this control point (should reflect the location in the map, such as "center bridge", "north tower", etc)
1237 void spawnfunc_onslaught_controlpoint()
1245 precache_model("models/onslaught/controlpoint_pad.md3");
1246 precache_model("models/onslaught/controlpoint_pad2.md3");
1247 precache_model("models/onslaught/controlpoint_shield.md3");
1248 precache_model("models/onslaught/controlpoint_icon.md3");
1249 precache_model("models/onslaught/controlpoint_icon_dmg1.md3");
1250 precache_model("models/onslaught/controlpoint_icon_dmg2.md3");
1251 precache_model("models/onslaught/controlpoint_icon_dmg3.md3");
1252 precache_model("models/onslaught/controlpoint_icon_gib1.md3");
1253 precache_model("models/onslaught/controlpoint_icon_gib2.md3");
1254 precache_model("models/onslaught/controlpoint_icon_gib4.md3");
1255 precache_sound("onslaught/controlpoint_build.wav");
1256 precache_sound("onslaught/controlpoint_built.wav");
1257 precache_sound("weapons/grenade_impact.wav");
1258 precache_sound("onslaught/damageblockedbyshield.wav");
1259 precache_sound("onslaught/controlpoint_underattack.wav");
1260 precache_sound("onslaught/ons_spark1.wav");
1261 precache_sound("onslaught/ons_spark2.wav");
1262 self.solid = SOLID_BBOX;
1263 self.movetype = MOVETYPE_NONE;
1264 setmodel(self, "models/onslaught/controlpoint_pad.md3");
1265 //setsize(self, '-32 -32 0', '32 32 8');
1266 setorigin(self, self.origin);
1267 self.touch = onslaught_controlpoint_touch;
1269 self.colormap = 1024;
1270 self.iscaptured = FALSE;
1271 self.islinked = FALSE;
1272 self.isshielded = TRUE;
1273 // spawn shield model which indicates whether this can be damaged
1274 self.enemy = e = spawn();
1275 e.classname = "onslaught_controlpoint_shield";
1276 e.solid = SOLID_NOT;
1277 e.movetype = MOVETYPE_NONE;
1278 e.effects = EF_ADDITIVE;
1279 setmodel(e, "models/onslaught/controlpoint_shield.md3");
1280 //setsize(e, '-32 -32 0', '32 32 128');
1281 setorigin(e, self.origin);
1282 e.colormap = self.colormap;
1284 waypoint_spawnforitem(self);
1286 WaypointSprite_SpawnFixed(string_null, e.origin + '0 0 1' * e.maxs_z, self, sprite);
1287 WaypointSprite_UpdateRule(self.sprite, COLOR_TEAM2, SPRITERULE_TEAMPLAY);
1289 onslaught_updatelinks();
1291 self.reset = onslaught_controlpoint_reset;
1294 float onslaught_link_send(entity to, float sendflags)
1296 WriteByte(MSG_ENTITY, ENT_CLIENT_RADARLINK);
1297 WriteByte(MSG_ENTITY, sendflags);
1300 WriteCoord(MSG_ENTITY, self.goalentity.origin_x);
1301 WriteCoord(MSG_ENTITY, self.goalentity.origin_y);
1302 WriteCoord(MSG_ENTITY, self.goalentity.origin_z);
1306 WriteCoord(MSG_ENTITY, self.enemy.origin_x);
1307 WriteCoord(MSG_ENTITY, self.enemy.origin_y);
1308 WriteCoord(MSG_ENTITY, self.enemy.origin_z);
1312 WriteByte(MSG_ENTITY, self.clientcolors); // which is goalentity's color + enemy's color * 16
1317 void onslaught_link_checkupdate()
1319 // TODO check if the two sides have moved (currently they won't move anyway)
1320 float redpower, bluepower;
1322 redpower = bluepower = 0;
1323 if(self.goalentity.islinked)
1325 if(self.goalentity.team == COLOR_TEAM1)
1327 else if(self.goalentity.team == COLOR_TEAM2)
1330 if(self.enemy.islinked)
1332 if(self.enemy.team == COLOR_TEAM1)
1334 else if(self.enemy.team == COLOR_TEAM2)
1339 if(redpower == 1 && bluepower == 2)
1340 cc = (COLOR_TEAM1 - 1) * 0x01 + (COLOR_TEAM2 - 1) * 0x10;
1341 else if(redpower == 2 && bluepower == 1)
1342 cc = (COLOR_TEAM1 - 1) * 0x10 + (COLOR_TEAM2 - 1) * 0x01;
1344 cc = (COLOR_TEAM1 - 1) * 0x11;
1346 cc = (COLOR_TEAM2 - 1) * 0x11;
1350 //print(etos(self), " rp=", ftos(redpower), " bp=", ftos(bluepower), " ");
1351 //print("cc=", ftos(cc), "\n");
1353 if(cc != self.clientcolors)
1355 self.clientcolors = cc;
1356 self.SendFlags |= 4;
1359 self.nextthink = time;
1362 void onslaught_link_delayed()
1364 self.goalentity = find(world, targetname, self.target);
1365 self.enemy = find(world, targetname, self.target2);
1366 if (!self.goalentity)
1367 objerror("can not find target\n");
1369 objerror("can not find target2\n");
1370 dprint(etos(self.goalentity), " linked with ", etos(self.enemy), "\n");
1371 self.SendFlags |= 3;
1372 self.think = onslaught_link_checkupdate;
1373 self.nextthink = time;
1376 /*QUAKED spawnfunc_onslaught_link (0 .5 .8) (-16 -16 -16) (16 16 16)
1377 Link between control points.
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.
1382 "target" - first control point.
1383 "target2" - second control point.
1385 void spawnfunc_onslaught_link()
1392 if (self.target == "" || self.target2 == "")
1393 objerror("target and target2 must be set\n");
1394 InitializeEntity(self, onslaught_link_delayed, INITPRIO_FINDTARGET);
1395 Net_LinkEntity(self, FALSE, 0, onslaught_link_send);