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 float overtime_msg_time;
376 void onslaught_generator_think()
380 self.nextthink = ceil(time + 1);
383 if (autocvar_timelimit && time > game_starttime + autocvar_timelimit * 60)
385 if (!overtime_msg_time)
388 centerprint(e, "^3Now playing ^1OVERTIME^3!\n^3Generators start now to self-damaging.\n^3The more control points your team holds,\n^3the more damage the enemy generator gets.");
389 overtime_msg_time = time;
391 // self.max_health / 300 gives 5 minutes of overtime.
392 // control points reduce the overtime duration.
393 sound(self, CH_TRIGGER, "onslaught/generator_decay.wav", VOL_BASE, ATTN_NORM);
395 e = findchain(classname, "onslaught_controlpoint");
398 if (e.team != self.team)
403 d = d * self.max_health / 300;
404 Damage(self, self, self, d, DEATH_HURTTRIGGER, self.origin, '0 0 0');
406 else if (overtime_msg_time)
407 overtime_msg_time = 0;
411 void onslaught_generator_ring_spawn(vector org)
413 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);
416 void onslaught_generator_ray_think()
418 self.nextthink = time + 0.05;
421 self.think = SUB_Remove;
434 void onslaught_generator_ray_spawn(vector org)
438 setmodel(e, "models/onslaught/ons_ray.md3");
440 e.angles = randomvec() * 360;
442 e.scale = random() * 5 + 8;
443 e.think = onslaught_generator_ray_think;
444 e.nextthink = time + 0.05;
447 void onslaught_generator_shockwave_spawn(vector org)
449 shockwave_spawn("models/onslaught/shockwave.md3", org, -64, 0.75, 0.5);
452 void onslaught_generator_damage_think()
454 if(self.owner.health < 0)
456 self.think = SUB_Remove;
459 self.nextthink = time+0.1;
461 // damaged fx (less probable the more damaged is the generator)
462 if(random() < 0.9 - self.owner.health / self.owner.max_health)
465 pointparticles(particleeffectnum("electro_ballexplode"), self.origin + randompos('-50 -50 -20', '50 50 50'), '0 0 0', 1);
466 sound(self, CH_TRIGGER, "onslaught/electricity_explode.wav", VOL_BASE, ATTN_NORM);
469 pointparticles(particleeffectnum("torch_small"), self.origin + randompos('-60 -60 -20', '60 60 60'), '0 0 0', 1);
472 void onslaught_generator_damage_spawn(entity gd_owner)
477 e.health = self.owner.health;
478 setorigin(e, gd_owner.origin);
479 e.think = onslaught_generator_damage_think;
480 e.nextthink = time+1;
483 void onslaught_generator_deaththink()
492 if(self.count==40||self.count==20)
494 onslaught_generator_ring_spawn(self.origin);
495 sound(self, CH_TRIGGER, "onslaught/shockwave.wav", VOL_BASE, ATTN_NORM);
503 ons_throwgib(self.origin + '0 0 40', (100 * randomvec() - '1 1 1') * 11 + '0 0 20', "models/onslaught/gen_gib1.md3", 6, TRUE);
505 ons_throwgib(self.origin + '0 0 40', (100 * randomvec() - '1 1 1') * 12 + '0 0 20', "models/onslaught/gen_gib2.md3", 6, TRUE);
507 ons_throwgib(self.origin + '0 0 40', (100 * randomvec() - '1 1 1') * 13 + '0 0 20', "models/onslaught/gen_gib3.md3", 6, TRUE);
513 org = self.origin + randompos('-30 -30 -30' * i + '0 0 -20', '30 30 30' * i + '0 0 20');
514 pointparticles(particleeffectnum("onslaught_generator_gib_explode"), org, '0 0 0', 1);
517 // Short explosion sound + small explosion
520 te_explosion(self.origin);
521 sound(self, CH_TRIGGER, "weapons/grenade_impact.wav", VOL_BASE, ATTN_NORM);
525 org = self.origin + randompos(self.mins + '8 8 8', self.maxs + '-8 -8 -8');
526 pointparticles(particleeffectnum("onslaught_generator_smallexplosion"), org, '0 0 0', 1);
531 onslaught_generator_ray_spawn(self.origin);
539 onslaught_generator_shockwave_spawn(org);
540 pointparticles(particleeffectnum("onslaught_generator_finalexplosion"), org, '0 0 0', 1);
541 sound(self, CH_TRIGGER, "weapons/rocket_impact.wav", VOL_BASE, ATTN_NORM);
544 self.nextthink = time + 0.05;
546 self.count = self.count - 1;
549 void onslaught_generator_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
556 if (attacker != self)
560 // this is protected by a shield, so ignore the damage
561 if (time > self.pain_finished)
562 if (attacker.classname == "player")
564 play2(attacker, "onslaught/damageblockedbyshield.wav");
565 self.pain_finished = time + 1;
569 if (time > self.pain_finished)
571 self.pain_finished = time + 10;
572 bprint(ColoredTeamName(self.team), " generator under attack!\n");
573 play2team(self.team, "onslaught/generator_underattack.wav");
576 self.health = self.health - damage;
577 WaypointSprite_UpdateHealth(self.sprite, self.health);
578 // choose an animation frame based on health
579 self.frame = 10 * bound(0, (1 - self.health / self.max_health), 1);
580 // see if the generator is still functional, or dying
583 #ifdef ONSLAUGHT_SPAM
585 lh = ceil(self.lasthealth / 100) * 100;
586 h = ceil(self.health / 100) * 100;
588 bprint(ColoredTeamName(self.team), " generator has less than ", ftos(h), " health remaining\n");
590 self.lasthealth = self.health;
592 else if not(inWarmupStage)
594 if (attacker == self)
595 bprint(ColoredTeamName(self.team), " generator spontaneously exploded due to overtime!\n");
599 t = ColoredTeamName(attacker.team);
600 bprint(ColoredTeamName(self.team), " generator destroyed by ", t, "!\n");
602 self.iscaptured = FALSE;
603 self.islinked = FALSE;
604 self.isshielded = FALSE;
605 self.takedamage = DAMAGE_NO; // can't be hurt anymore
606 self.event_damage = SUB_Null; // won't do anything if hurt
607 self.count = 0; // reset counter
608 self.think = onslaught_generator_deaththink; // explosion sequence
609 self.nextthink = time; // start exploding immediately
610 self.think(); // do the first explosion now
612 WaypointSprite_UpdateMaxHealth(self.sprite, 0);
614 onslaught_updatelinks();
618 setmodel(self, "models/onslaught/generator_dead.md3");
619 else if(self.health < self.max_health * 0.10)
620 setmodel(self, "models/onslaught/generator_dmg9.md3");
621 else if(self.health < self.max_health * 0.20)
622 setmodel(self, "models/onslaught/generator_dmg8.md3");
623 else if(self.health < self.max_health * 0.30)
624 setmodel(self, "models/onslaught/generator_dmg7.md3");
625 else if(self.health < self.max_health * 0.40)
626 setmodel(self, "models/onslaught/generator_dmg6.md3");
627 else if(self.health < self.max_health * 0.50)
628 setmodel(self, "models/onslaught/generator_dmg5.md3");
629 else if(self.health < self.max_health * 0.60)
630 setmodel(self, "models/onslaught/generator_dmg4.md3");
631 else if(self.health < self.max_health * 0.70)
632 setmodel(self, "models/onslaught/generator_dmg3.md3");
633 else if(self.health < self.max_health * 0.80)
634 setmodel(self, "models/onslaught/generator_dmg2.md3");
635 else if(self.health < self.max_health * 0.90)
636 setmodel(self, "models/onslaught/generator_dmg1.md3");
637 setsize(self, '-52 -52 -14', '52 52 75');
639 // Throw some flaming gibs on damage, more damage = more chance for gib
640 if(random() < damage/220)
642 sound(self, CH_TRIGGER, "weapons/rocket_impact.wav", VOL_BASE, ATTN_NORM);
645 ons_throwgib(hitloc + '0 0 20', force * -1, "models/onslaught/gen_gib1.md3", 5, TRUE);
647 ons_throwgib(hitloc + '0 0 20', force * -1, "models/onslaught/gen_gib2.md3", 5, TRUE);
649 ons_throwgib(hitloc + '0 0 20', force * -1, "models/onslaught/gen_gib3.md3", 5, TRUE);
653 // particles on every hit
654 pointparticles(particleeffectnum("sparks"), hitloc, force * -1, 1);
658 sound(self, CH_TRIGGER, "onslaught/ons_hit1.wav", VOL_BASE, ATTN_NORM);
660 sound(self, CH_TRIGGER, "onslaught/ons_hit2.wav", VOL_BASE, ATTN_NORM);
663 //throw some gibs on damage
664 if(random() < damage/200+0.2)
666 ons_throwgib(hitloc + '0 0 20', randomvec()*360, "models/onslaught/gen_gib1.md3", 5, FALSE);
669 // update links after a delay
670 void onslaught_generator_delayed()
672 onslaught_updatelinks();
673 // now begin normal thinking
674 self.think = onslaught_generator_think;
675 self.nextthink = time;
678 string onslaught_generator_waypointsprite_for_team(entity e, float t)
682 if(e.team == COLOR_TEAM1)
683 return "ons-gen-red";
684 else if(e.team == COLOR_TEAM2)
685 return "ons-gen-blue";
688 return "ons-gen-shielded";
689 if(e.team == COLOR_TEAM1)
690 return "ons-gen-red";
691 else if(e.team == COLOR_TEAM2)
692 return "ons-gen-blue";
696 void onslaught_generator_updatesprite(entity e)
699 s1 = onslaught_generator_waypointsprite_for_team(e, COLOR_TEAM1);
700 s2 = onslaught_generator_waypointsprite_for_team(e, COLOR_TEAM2);
701 s3 = onslaught_generator_waypointsprite_for_team(e, -1);
702 WaypointSprite_UpdateSprites(e.sprite, s1, s2, s3);
704 if(e.lastteam != e.team + 2 || e.lastshielded != e.isshielded)
706 e.lastteam = e.team + 2;
707 e.lastshielded = e.isshielded;
710 if(e.team == COLOR_TEAM1 || e.team == COLOR_TEAM2)
711 WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, 0.5 * colormapPaletteColor(e.team - 1, FALSE));
713 WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, '0.5 0.5 0.5');
717 if(e.team == COLOR_TEAM1 || e.team == COLOR_TEAM2)
718 WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, colormapPaletteColor(e.team - 1, FALSE));
720 WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, '0.75 0.75 0.75');
722 WaypointSprite_Ping(e.sprite);
726 string onslaught_controlpoint_waypointsprite_for_team(entity e, float t)
731 a = onslaught_controlpoint_attackable(e, t);
732 if(a == 3 || a == 4) // ATTACK/TOUCH THIS ONE NOW
734 if(e.team == COLOR_TEAM1)
735 return "ons-cp-atck-red";
736 else if(e.team == COLOR_TEAM2)
737 return "ons-cp-atck-blue";
739 return "ons-cp-atck-neut";
741 else if(a == -2) // DEFEND THIS ONE NOW
743 if(e.team == COLOR_TEAM1)
744 return "ons-cp-dfnd-red";
745 else if(e.team == COLOR_TEAM2)
746 return "ons-cp-dfnd-blue";
748 else if(e.team == t || a == -1 || a == 1) // own point, or fire at it
750 if(e.team == COLOR_TEAM1)
752 else if(e.team == COLOR_TEAM2)
753 return "ons-cp-blue";
755 else if(a == 2) // touch it
756 return "ons-cp-neut";
760 if(e.team == COLOR_TEAM1)
762 else if(e.team == COLOR_TEAM2)
763 return "ons-cp-blue";
765 return "ons-cp-neut";
770 void onslaught_controlpoint_updatesprite(entity e)
773 s1 = onslaught_controlpoint_waypointsprite_for_team(e, COLOR_TEAM1);
774 s2 = onslaught_controlpoint_waypointsprite_for_team(e, COLOR_TEAM2);
775 s3 = onslaught_controlpoint_waypointsprite_for_team(e, -1);
776 WaypointSprite_UpdateSprites(e.sprite, s1, s2, s3);
779 sh = !(onslaught_controlpoint_can_be_linked(e, COLOR_TEAM1) || onslaught_controlpoint_can_be_linked(e, COLOR_TEAM2));
781 if(e.lastteam != e.team + 2 || e.lastshielded != sh || e.iscaptured != e.lastcaptured)
783 if(e.iscaptured) // don't mess up build bars!
787 WaypointSprite_UpdateMaxHealth(e.sprite, 0);
791 WaypointSprite_UpdateMaxHealth(e.sprite, e.goalentity.max_health);
792 WaypointSprite_UpdateHealth(e.sprite, e.goalentity.health);
797 if(e.team == COLOR_TEAM1 || e.team == COLOR_TEAM2)
798 WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_CONTROLPOINT, 0.5 * colormapPaletteColor(e.team - 1, FALSE));
800 WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_CONTROLPOINT, '0.5 0.5 0.5');
804 if(e.team == COLOR_TEAM1 || e.team == COLOR_TEAM2)
805 WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_CONTROLPOINT, colormapPaletteColor(e.team - 1, FALSE));
807 WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_CONTROLPOINT, '0.75 0.75 0.75');
809 WaypointSprite_Ping(e.sprite);
811 e.lastteam = e.team + 2;
813 e.lastcaptured = e.iscaptured;
817 void onslaught_generator_reset()
819 self.team = self.team_saved;
820 self.lasthealth = self.max_health = self.health = autocvar_g_onslaught_gen_health;
821 self.takedamage = DAMAGE_AIM;
822 self.bot_attack = TRUE;
823 self.iscaptured = TRUE;
824 self.islinked = TRUE;
825 self.isshielded = TRUE;
826 self.enemy.solid = SOLID_NOT;
827 self.think = onslaught_generator_delayed;
828 self.nextthink = time + 0.2;
829 setmodel(self, "models/onslaught/generator.md3");
831 WaypointSprite_UpdateMaxHealth(self.sprite, self.max_health);
832 WaypointSprite_UpdateHealth(self.sprite, self.health);
835 /*QUAKED spawnfunc_onslaught_generator (0 .5 .8) (-32 -32 -24) (32 32 64)
838 spawnfunc_onslaught_link entities can target this.
841 "team" - team that owns this generator (5 = red, 14 = blue, etc), MUST BE SET.
842 "targetname" - name that spawnfunc_onslaught_link entities will use to target this.
844 void spawnfunc_onslaught_generator()
853 precache_model("models/onslaught/generator.md3");
854 precache_model("models/onslaught/generator_shield.md3");
855 precache_model("models/onslaught/generator_dmg1.md3");
856 precache_model("models/onslaught/generator_dmg2.md3");
857 precache_model("models/onslaught/generator_dmg3.md3");
858 precache_model("models/onslaught/generator_dmg4.md3");
859 precache_model("models/onslaught/generator_dmg5.md3");
860 precache_model("models/onslaught/generator_dmg6.md3");
861 precache_model("models/onslaught/generator_dmg7.md3");
862 precache_model("models/onslaught/generator_dmg8.md3");
863 precache_model("models/onslaught/generator_dmg9.md3");
864 precache_model("models/onslaught/generator_dead.md3");
865 precache_model("models/onslaught/shockwave.md3");
866 precache_model("models/onslaught/shockwavetransring.md3");
867 precache_model("models/onslaught/gen_gib1.md3");
868 precache_model("models/onslaught/gen_gib2.md3");
869 precache_model("models/onslaught/gen_gib3.md3");
870 precache_model("models/onslaught/ons_ray.md3");
871 precache_sound("onslaught/generator_decay.wav");
872 precache_sound("weapons/grenade_impact.wav");
873 precache_sound("weapons/rocket_impact.wav");
874 precache_sound("onslaught/generator_underattack.wav");
875 precache_sound("onslaught/shockwave.wav");
876 precache_sound("onslaught/ons_hit1.wav");
877 precache_sound("onslaught/ons_hit2.wav");
878 precache_sound("onslaught/electricity_explode.wav");
880 objerror("team must be set");
881 self.team_saved = self.team;
882 self.colormap = 1024 + (self.team - 1) * 17;
883 self.solid = SOLID_BBOX;
884 self.movetype = MOVETYPE_NONE;
885 self.lasthealth = self.max_health = self.health = autocvar_g_onslaught_gen_health;
886 setmodel(self, "models/onslaught/generator.md3");
887 setsize(self, '-52 -52 -14', '52 52 75');
888 setorigin(self, self.origin);
889 self.takedamage = DAMAGE_AIM;
890 self.bot_attack = TRUE;
891 self.event_damage = onslaught_generator_damage;
892 self.iscaptured = TRUE;
893 self.islinked = TRUE;
894 self.isshielded = TRUE;
895 // helper entity that create fx when generator is damaged
896 onslaught_generator_damage_spawn(self);
897 // spawn shield model which indicates whether this can be damaged
898 self.enemy = e = spawn();
899 e.classname = "onslaught_generator_shield";
901 e.movetype = MOVETYPE_NONE;
902 e.effects = EF_ADDITIVE;
903 setmodel(e, "models/onslaught/generator_shield.md3");
904 setorigin(e, self.origin);
905 e.colormap = self.colormap;
907 self.think = onslaught_generator_delayed;
908 self.nextthink = time + 0.2;
909 InitializeEntity(self, onslaught_generator_delayed, INITPRIO_LAST);
911 WaypointSprite_SpawnFixed(string_null, e.origin + '0 0 1' * e.maxs_z, self, sprite, RADARICON_NONE, '0 0 0');
912 WaypointSprite_UpdateRule(self.sprite, COLOR_TEAM2, SPRITERULE_TEAMPLAY);
913 WaypointSprite_UpdateMaxHealth(self.sprite, self.max_health);
914 WaypointSprite_UpdateHealth(self.sprite, self.health);
916 waypoint_spawnforitem(self);
918 onslaught_updatelinks();
920 self.reset = onslaught_generator_reset;
925 .vector cp_origin, cp_bob_origin, cp_bob_dmg;
927 float ons_notification_time_team1;
928 float ons_notification_time_team2;
930 void onslaught_controlpoint_icon_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
937 if (self.owner.isshielded)
939 // this is protected by a shield, so ignore the damage
940 if (time > self.pain_finished)
941 if (attacker.classname == "player")
943 play2(attacker, "onslaught/damageblockedbyshield.wav");
944 self.pain_finished = time + 1;
949 if (attacker.classname == "player")
951 if(self.team == COLOR_TEAM1)
953 if(time - ons_notification_time_team1 > 10)
956 ons_notification_time_team1 = time;
959 else if(self.team == COLOR_TEAM2)
961 if(time - ons_notification_time_team2 > 10)
964 ons_notification_time_team2 = time;
971 play2team(self.team, "onslaught/controlpoint_underattack.wav");
974 self.health = self.health - damage;
975 if(self.owner.iscaptured)
976 WaypointSprite_UpdateHealth(self.owner.sprite, self.health);
978 WaypointSprite_UpdateBuildFinished(self.owner.sprite, time + (self.max_health - self.health) / (self.count / sys_frametime));
979 self.pain_finished = time + 1;
980 self.punchangle = (2 * randomvec() - '1 1 1') * 45;
981 self.cp_bob_dmg_z = (2 * random() - 1) * 15;
982 // colormod flash when shot
983 self.colormod = '2 2 2';
984 // particles on every hit
985 pointparticles(particleeffectnum("sparks"), hitloc, force*-1, 1);
988 sound(self, CH_TRIGGER, "onslaught/ons_hit1.wav", VOL_BASE+0.3, ATTN_NORM);
990 sound(self, CH_TRIGGER, "onslaught/ons_hit2.wav", VOL_BASE+0.3, ATTN_NORM);
994 sound(self, CH_TRIGGER, "weapons/grenade_impact.wav", VOL_BASE, ATTN_NORM);
995 pointparticles(particleeffectnum("rocket_explode"), self.origin, '0 0 0', 1);
998 t = ColoredTeamName(attacker.team);
999 bprint(ColoredTeamName(self.team), " ", self.message, " control point destroyed by ", t, "\n");
1000 ons_throwgib(self.origin, (2 * randomvec() - '1 1 1') * 25, "models/onslaught/controlpoint_icon_gib1.md3", 3, FALSE);
1001 ons_throwgib(self.origin, (2 * randomvec() - '1 1 1') * 45, "models/onslaught/controlpoint_icon_gib2.md3", 3, FALSE);
1002 ons_throwgib(self.origin, (2 * randomvec() - '1 1 1') * 45, "models/onslaught/controlpoint_icon_gib2.md3", 3, FALSE);
1003 ons_throwgib(self.origin, (2 * randomvec() - '1 1 1') * 75, "models/onslaught/controlpoint_icon_gib4.md3", 3, FALSE);
1004 ons_throwgib(self.origin, (2 * randomvec() - '1 1 1') * 75, "models/onslaught/controlpoint_icon_gib4.md3", 3, FALSE);
1005 ons_throwgib(self.origin, (2 * randomvec() - '1 1 1') * 75, "models/onslaught/controlpoint_icon_gib4.md3", 3, FALSE);
1006 ons_throwgib(self.origin, (2 * randomvec() - '1 1 1') * 75, "models/onslaught/controlpoint_icon_gib4.md3", 3, FALSE);
1008 self.owner.goalentity = world;
1009 self.owner.islinked = FALSE;
1010 self.owner.iscaptured = FALSE;
1011 self.owner.team = 0;
1012 self.owner.colormap = 1024;
1014 WaypointSprite_UpdateMaxHealth(self.owner.sprite, 0);
1016 onslaught_updatelinks();
1018 // Use targets now (somebody make sure this is in the right place..)
1026 self.owner.waslinked = self.owner.islinked;
1027 if(self.owner.model != "models/onslaught/controlpoint_pad.md3")
1028 setmodel(self.owner, "models/onslaught/controlpoint_pad.md3");
1029 //setsize(self, '-32 -32 0', '32 32 8');
1035 void onslaught_controlpoint_icon_think()
1038 self.nextthink = time + sys_frametime;
1039 if (time > self.pain_finished + 5)
1041 if(self.health < self.max_health)
1043 self.health = self.health + self.count;
1044 if (self.health >= self.max_health)
1045 self.health = self.max_health;
1046 WaypointSprite_UpdateHealth(self.owner.sprite, self.health);
1049 if (self.health < self.max_health * 0.25)
1050 setmodel(self, "models/onslaught/controlpoint_icon_dmg3.md3");
1051 else if (self.health < self.max_health * 0.50)
1052 setmodel(self, "models/onslaught/controlpoint_icon_dmg2.md3");
1053 else if (self.health < self.max_health * 0.75)
1054 setmodel(self, "models/onslaught/controlpoint_icon_dmg1.md3");
1055 else if (self.health < self.max_health * 0.90)
1056 setmodel(self, "models/onslaught/controlpoint_icon.md3");
1057 // colormod flash when shot
1058 self.colormod = '1 1 1' * (2 - bound(0, (self.pain_finished - time) / 10, 1));
1060 if(self.owner.islinked != self.owner.waslinked)
1062 // unteam the spawnpoint if needed
1064 t = self.owner.team;
1065 if(!self.owner.islinked)
1066 self.owner.team = 0;
1074 self.owner.team = t;
1076 self.owner.waslinked = self.owner.islinked;
1078 if (self.punchangle_x > 2)
1079 self.punchangle_x = self.punchangle_x - 2;
1080 else if (self.punchangle_x < -2)
1081 self.punchangle_x = self.punchangle_x + 2;
1083 self.punchangle_x = 0;
1084 if (self.punchangle_y > 2)
1085 self.punchangle_y = self.punchangle_y - 2;
1086 else if (self.punchangle_y < -2)
1087 self.punchangle_y = self.punchangle_y + 2;
1089 self.punchangle_y = 0;
1090 if (self.punchangle_z > 2)
1091 self.punchangle_z = self.punchangle_z - 2;
1092 else if (self.punchangle_z < -2)
1093 self.punchangle_z = self.punchangle_z + 2;
1095 self.punchangle_z = 0;
1096 self.angles_x = self.punchangle_x;
1097 self.angles_y = self.punchangle_y + self.mangle_y;
1098 self.angles_z = self.punchangle_z;
1099 self.mangle_y = self.mangle_y + 1.5;
1101 self.cp_bob_origin_z = 4 * PI * (1 - cos(self.cp_bob_spd / 8));
1102 self.cp_bob_spd = self.cp_bob_spd + 0.5;
1103 if(self.cp_bob_dmg_z > 0)
1104 self.cp_bob_dmg_z = self.cp_bob_dmg_z - 0.1;
1106 self.cp_bob_dmg_z = 0;
1107 setorigin(self,self.cp_origin + self.cp_bob_origin + self.cp_bob_dmg);
1110 if(random() < 0.6 - self.health / self.max_health)
1112 pointparticles(particleeffectnum("electricity_sparks"), self.origin + randompos('-10 -10 -20', '10 10 20'), '0 0 0', 1);
1115 sound(self, CH_PAIN, "onslaught/ons_spark1.wav", VOL_BASE, ATTN_NORM);
1116 else if (random() > 0.5)
1117 sound(self, CH_PAIN, "onslaught/ons_spark2.wav", VOL_BASE, ATTN_NORM);
1121 void onslaught_controlpoint_icon_buildthink()
1126 self.nextthink = time + sys_frametime;
1128 // only do this if there is power
1129 a = onslaught_controlpoint_can_be_linked(self.owner, self.owner.team);
1133 self.health = self.health + self.count;
1135 if (self.health >= self.max_health)
1137 self.health = self.max_health;
1138 self.count = autocvar_g_onslaught_cp_regen * sys_frametime; // slow repair rate from now on
1139 self.think = onslaught_controlpoint_icon_think;
1140 sound(self, CH_TRIGGER, "onslaught/controlpoint_built.wav", VOL_BASE, ATTN_NORM);
1141 bprint(ColoredTeamName(self.team), " captured ", self.owner.message, " control point\n");
1142 self.owner.iscaptured = TRUE;
1144 WaypointSprite_UpdateMaxHealth(self.owner.sprite, self.max_health);
1145 WaypointSprite_UpdateHealth(self.owner.sprite, self.health);
1147 onslaught_updatelinks();
1149 // Use targets now (somebody make sure this is in the right place..)
1155 self.cp_origin = self.origin;
1156 self.cp_bob_origin = '0 0 0.1';
1157 self.cp_bob_spd = 0;
1159 self.alpha = self.health / self.max_health;
1160 // colormod flash when shot
1161 self.colormod = '1 1 1' * (2 - bound(0, (self.pain_finished - time) / 10, 1));
1162 if(self.owner.model != "models/onslaught/controlpoint_pad2.md3")
1163 setmodel(self.owner, "models/onslaught/controlpoint_pad2.md3");
1164 //setsize(self, '-32 -32 0', '32 32 8');
1166 if(random() < 0.9 - self.health / self.max_health)
1167 pointparticles(particleeffectnum("rage"), self.origin + 10 * randomvec(), '0 0 -1', 1);
1173 void onslaught_controlpoint_touch()
1177 if (other.classname != "player")
1179 a = onslaught_controlpoint_attackable(self, other.team);
1180 if(a != 2 && a != 4)
1182 // we've verified that this player has a legitimate claim to this point,
1183 // so start building the captured point icon (which only captures this
1184 // point if it successfully builds without being destroyed first)
1185 self.goalentity = e = spawn();
1186 e.classname = "onslaught_controlpoint_icon";
1188 e.max_health = autocvar_g_onslaught_cp_health;
1189 e.health = autocvar_g_onslaught_cp_buildhealth;
1190 e.solid = SOLID_BBOX;
1191 e.movetype = MOVETYPE_NONE;
1192 setmodel(e, "models/onslaught/controlpoint_icon.md3");
1193 setsize(e, '-32 -32 -32', '32 32 32');
1194 setorigin(e, self.origin + '0 0 96');
1195 e.takedamage = DAMAGE_AIM;
1196 e.bot_attack = TRUE;
1197 e.event_damage = onslaught_controlpoint_icon_damage;
1198 e.team = other.team;
1199 e.colormap = 1024 + (e.team - 1) * 17;
1200 e.think = onslaught_controlpoint_icon_buildthink;
1201 e.nextthink = time + sys_frametime;
1202 e.count = (e.max_health - e.health) * sys_frametime / autocvar_g_onslaught_cp_buildtime; // how long it takes to build
1203 sound(e, CH_TRIGGER, "onslaught/controlpoint_build.wav", VOL_BASE, ATTN_NORM);
1205 self.colormap = e.colormap;
1206 WaypointSprite_UpdateBuildFinished(self.sprite, time + (e.max_health - e.health) / (e.count / sys_frametime));
1207 onslaught_updatelinks();
1210 void onslaught_controlpoint_reset()
1212 if(self.goalentity && self.goalentity != world)
1213 remove(self.goalentity);
1214 self.goalentity = world;
1216 self.colormap = 1024;
1217 self.iscaptured = FALSE;
1218 self.islinked = FALSE;
1219 self.isshielded = TRUE;
1220 self.enemy.solid = SOLID_NOT;
1221 self.enemy.colormap = self.colormap;
1222 self.think = self.enemy.think = SUB_Null;
1223 self.nextthink = 0; // don't like SUB_Null :P
1224 setmodel(self, "models/onslaught/controlpoint_pad.md3");
1225 //setsize(self, '-32 -32 0', '32 32 8');
1227 WaypointSprite_UpdateMaxHealth(self.sprite, 0);
1229 onslaught_updatelinks();
1232 SUB_UseTargets(); // to reset the structures, playerspawns etc.
1235 /*QUAKED spawnfunc_onslaught_controlpoint (0 .5 .8) (-32 -32 0) (32 32 128)
1236 Control point. Be sure to give this enough clearance so that the shootable part has room to exist
1238 This should link to an spawnfunc_onslaught_controlpoint entity or spawnfunc_onslaught_generator entity.
1241 "targetname" - name that spawnfunc_onslaught_link entities will use to target this.
1242 "target" - target any entities that are tied to this control point, such as vehicles and buildable structure entities.
1243 "message" - name of this control point (should reflect the location in the map, such as "center bridge", "north tower", etc)
1245 void spawnfunc_onslaught_controlpoint()
1253 precache_model("models/onslaught/controlpoint_pad.md3");
1254 precache_model("models/onslaught/controlpoint_pad2.md3");
1255 precache_model("models/onslaught/controlpoint_shield.md3");
1256 precache_model("models/onslaught/controlpoint_icon.md3");
1257 precache_model("models/onslaught/controlpoint_icon_dmg1.md3");
1258 precache_model("models/onslaught/controlpoint_icon_dmg2.md3");
1259 precache_model("models/onslaught/controlpoint_icon_dmg3.md3");
1260 precache_model("models/onslaught/controlpoint_icon_gib1.md3");
1261 precache_model("models/onslaught/controlpoint_icon_gib2.md3");
1262 precache_model("models/onslaught/controlpoint_icon_gib4.md3");
1263 precache_sound("onslaught/controlpoint_build.wav");
1264 precache_sound("onslaught/controlpoint_built.wav");
1265 precache_sound("weapons/grenade_impact.wav");
1266 precache_sound("onslaught/damageblockedbyshield.wav");
1267 precache_sound("onslaught/controlpoint_underattack.wav");
1268 precache_sound("onslaught/ons_spark1.wav");
1269 precache_sound("onslaught/ons_spark2.wav");
1270 self.solid = SOLID_BBOX;
1271 self.movetype = MOVETYPE_NONE;
1272 setmodel(self, "models/onslaught/controlpoint_pad.md3");
1273 //setsize(self, '-32 -32 0', '32 32 8');
1274 setorigin(self, self.origin);
1275 self.touch = onslaught_controlpoint_touch;
1277 self.colormap = 1024;
1278 self.iscaptured = FALSE;
1279 self.islinked = FALSE;
1280 self.isshielded = TRUE;
1281 // spawn shield model which indicates whether this can be damaged
1282 self.enemy = e = spawn();
1283 e.classname = "onslaught_controlpoint_shield";
1284 e.solid = SOLID_NOT;
1285 e.movetype = MOVETYPE_NONE;
1286 e.effects = EF_ADDITIVE;
1287 setmodel(e, "models/onslaught/controlpoint_shield.md3");
1288 //setsize(e, '-32 -32 0', '32 32 128');
1289 setorigin(e, self.origin);
1290 e.colormap = self.colormap;
1292 waypoint_spawnforitem(self);
1294 WaypointSprite_SpawnFixed(string_null, e.origin + '0 0 1' * e.maxs_z, self, sprite, RADARICON_NONE, '0 0 0');
1295 WaypointSprite_UpdateRule(self.sprite, COLOR_TEAM2, SPRITERULE_TEAMPLAY);
1297 onslaught_updatelinks();
1299 self.reset = onslaught_controlpoint_reset;
1302 float onslaught_link_send(entity to, float sendflags)
1304 WriteByte(MSG_ENTITY, ENT_CLIENT_RADARLINK);
1305 WriteByte(MSG_ENTITY, sendflags);
1308 WriteCoord(MSG_ENTITY, self.goalentity.origin_x);
1309 WriteCoord(MSG_ENTITY, self.goalentity.origin_y);
1310 WriteCoord(MSG_ENTITY, self.goalentity.origin_z);
1314 WriteCoord(MSG_ENTITY, self.enemy.origin_x);
1315 WriteCoord(MSG_ENTITY, self.enemy.origin_y);
1316 WriteCoord(MSG_ENTITY, self.enemy.origin_z);
1320 WriteByte(MSG_ENTITY, self.clientcolors); // which is goalentity's color + enemy's color * 16
1325 void onslaught_link_checkupdate()
1327 // TODO check if the two sides have moved (currently they won't move anyway)
1328 float redpower, bluepower;
1330 redpower = bluepower = 0;
1331 if(self.goalentity.islinked)
1333 if(self.goalentity.team == COLOR_TEAM1)
1335 else if(self.goalentity.team == COLOR_TEAM2)
1338 if(self.enemy.islinked)
1340 if(self.enemy.team == COLOR_TEAM1)
1342 else if(self.enemy.team == COLOR_TEAM2)
1347 if(redpower == 1 && bluepower == 2)
1348 cc = (COLOR_TEAM1 - 1) * 0x01 + (COLOR_TEAM2 - 1) * 0x10;
1349 else if(redpower == 2 && bluepower == 1)
1350 cc = (COLOR_TEAM1 - 1) * 0x10 + (COLOR_TEAM2 - 1) * 0x01;
1352 cc = (COLOR_TEAM1 - 1) * 0x11;
1354 cc = (COLOR_TEAM2 - 1) * 0x11;
1358 //print(etos(self), " rp=", ftos(redpower), " bp=", ftos(bluepower), " ");
1359 //print("cc=", ftos(cc), "\n");
1361 if(cc != self.clientcolors)
1363 self.clientcolors = cc;
1364 self.SendFlags |= 4;
1367 self.nextthink = time;
1370 void onslaught_link_delayed()
1372 self.goalentity = find(world, targetname, self.target);
1373 self.enemy = find(world, targetname, self.target2);
1374 if (!self.goalentity)
1375 objerror("can not find target\n");
1377 objerror("can not find target2\n");
1378 dprint(etos(self.goalentity), " linked with ", etos(self.enemy), "\n");
1379 self.SendFlags |= 3;
1380 self.think = onslaught_link_checkupdate;
1381 self.nextthink = time;
1384 /*QUAKED spawnfunc_onslaught_link (0 .5 .8) (-16 -16 -16) (16 16 16)
1385 Link between control points.
1387 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.
1390 "target" - first control point.
1391 "target2" - second control point.
1393 void spawnfunc_onslaught_link()
1400 if (self.target == "" || self.target2 == "")
1401 objerror("target and target2 must be set\n");
1402 InitializeEntity(self, onslaught_link_delayed, INITPRIO_FINDTARGET);
1403 Net_LinkEntity(self, FALSE, 0, onslaught_link_send);