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");
830 setsize(self, '-52 -52 -14', '52 52 75');
832 WaypointSprite_UpdateMaxHealth(self.sprite, self.max_health);
833 WaypointSprite_UpdateHealth(self.sprite, self.health);
836 /*QUAKED spawnfunc_onslaught_generator (0 .5 .8) (-32 -32 -24) (32 32 64)
839 spawnfunc_onslaught_link entities can target this.
842 "team" - team that owns this generator (5 = red, 14 = blue, etc), MUST BE SET.
843 "targetname" - name that spawnfunc_onslaught_link entities will use to target this.
845 void spawnfunc_onslaught_generator()
854 precache_model("models/onslaught/generator.md3");
855 precache_model("models/onslaught/generator_shield.md3");
856 precache_model("models/onslaught/generator_dmg1.md3");
857 precache_model("models/onslaught/generator_dmg2.md3");
858 precache_model("models/onslaught/generator_dmg3.md3");
859 precache_model("models/onslaught/generator_dmg4.md3");
860 precache_model("models/onslaught/generator_dmg5.md3");
861 precache_model("models/onslaught/generator_dmg6.md3");
862 precache_model("models/onslaught/generator_dmg7.md3");
863 precache_model("models/onslaught/generator_dmg8.md3");
864 precache_model("models/onslaught/generator_dmg9.md3");
865 precache_model("models/onslaught/generator_dead.md3");
866 precache_model("models/onslaught/shockwave.md3");
867 precache_model("models/onslaught/shockwavetransring.md3");
868 precache_model("models/onslaught/gen_gib1.md3");
869 precache_model("models/onslaught/gen_gib2.md3");
870 precache_model("models/onslaught/gen_gib3.md3");
871 precache_model("models/onslaught/ons_ray.md3");
872 precache_sound("onslaught/generator_decay.wav");
873 precache_sound("weapons/grenade_impact.wav");
874 precache_sound("weapons/rocket_impact.wav");
875 precache_sound("onslaught/generator_underattack.wav");
876 precache_sound("onslaught/shockwave.wav");
877 precache_sound("onslaught/ons_hit1.wav");
878 precache_sound("onslaught/ons_hit2.wav");
879 precache_sound("onslaught/electricity_explode.wav");
881 objerror("team must be set");
882 self.team_saved = self.team;
883 self.colormap = 1024 + (self.team - 1) * 17;
884 self.solid = SOLID_BBOX;
885 self.movetype = MOVETYPE_NONE;
886 self.lasthealth = self.max_health = self.health = autocvar_g_onslaught_gen_health;
887 setmodel(self, "models/onslaught/generator.md3");
888 setsize(self, '-52 -52 -14', '52 52 75');
889 setorigin(self, self.origin);
890 self.takedamage = DAMAGE_AIM;
891 self.bot_attack = TRUE;
892 self.event_damage = onslaught_generator_damage;
893 self.iscaptured = TRUE;
894 self.islinked = TRUE;
895 self.isshielded = TRUE;
896 // helper entity that create fx when generator is damaged
897 onslaught_generator_damage_spawn(self);
898 // spawn shield model which indicates whether this can be damaged
899 self.enemy = e = spawn();
900 e.classname = "onslaught_generator_shield";
902 e.movetype = MOVETYPE_NONE;
903 e.effects = EF_ADDITIVE;
904 setmodel(e, "models/onslaught/generator_shield.md3");
905 setorigin(e, self.origin);
906 e.colormap = self.colormap;
908 self.think = onslaught_generator_delayed;
909 self.nextthink = time + 0.2;
910 InitializeEntity(self, onslaught_generator_delayed, INITPRIO_LAST);
912 WaypointSprite_SpawnFixed(string_null, e.origin + '0 0 1' * e.maxs_z, self, sprite, RADARICON_NONE, '0 0 0');
913 WaypointSprite_UpdateRule(self.sprite, COLOR_TEAM2, SPRITERULE_TEAMPLAY);
914 WaypointSprite_UpdateMaxHealth(self.sprite, self.max_health);
915 WaypointSprite_UpdateHealth(self.sprite, self.health);
917 waypoint_spawnforitem(self);
919 onslaught_updatelinks();
921 self.reset = onslaught_generator_reset;
926 .vector cp_origin, cp_bob_origin, cp_bob_dmg;
928 float ons_notification_time_team1;
929 float ons_notification_time_team2;
931 void onslaught_controlpoint_icon_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
938 if (self.owner.isshielded)
940 // this is protected by a shield, so ignore the damage
941 if (time > self.pain_finished)
942 if (attacker.classname == "player")
944 play2(attacker, "onslaught/damageblockedbyshield.wav");
945 self.pain_finished = time + 1;
950 if (attacker.classname == "player")
952 if(self.team == COLOR_TEAM1)
954 if(time - ons_notification_time_team1 > 10)
957 ons_notification_time_team1 = time;
960 else if(self.team == COLOR_TEAM2)
962 if(time - ons_notification_time_team2 > 10)
965 ons_notification_time_team2 = time;
972 play2team(self.team, "onslaught/controlpoint_underattack.wav");
975 self.health = self.health - damage;
976 if(self.owner.iscaptured)
977 WaypointSprite_UpdateHealth(self.owner.sprite, self.health);
979 WaypointSprite_UpdateBuildFinished(self.owner.sprite, time + (self.max_health - self.health) / (self.count / sys_frametime));
980 self.pain_finished = time + 1;
981 self.punchangle = (2 * randomvec() - '1 1 1') * 45;
982 self.cp_bob_dmg_z = (2 * random() - 1) * 15;
983 // colormod flash when shot
984 self.colormod = '2 2 2';
985 // particles on every hit
986 pointparticles(particleeffectnum("sparks"), hitloc, force*-1, 1);
989 sound(self, CH_TRIGGER, "onslaught/ons_hit1.wav", VOL_BASE+0.3, ATTN_NORM);
991 sound(self, CH_TRIGGER, "onslaught/ons_hit2.wav", VOL_BASE+0.3, ATTN_NORM);
995 sound(self, CH_TRIGGER, "weapons/grenade_impact.wav", VOL_BASE, ATTN_NORM);
996 pointparticles(particleeffectnum("rocket_explode"), self.origin, '0 0 0', 1);
999 t = ColoredTeamName(attacker.team);
1000 bprint(ColoredTeamName(self.team), " ", self.message, " control point destroyed by ", t, "\n");
1001 ons_throwgib(self.origin, (2 * randomvec() - '1 1 1') * 25, "models/onslaught/controlpoint_icon_gib1.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') * 45, "models/onslaught/controlpoint_icon_gib2.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);
1007 ons_throwgib(self.origin, (2 * randomvec() - '1 1 1') * 75, "models/onslaught/controlpoint_icon_gib4.md3", 3, FALSE);
1009 self.owner.goalentity = world;
1010 self.owner.islinked = FALSE;
1011 self.owner.iscaptured = FALSE;
1012 self.owner.team = 0;
1013 self.owner.colormap = 1024;
1015 WaypointSprite_UpdateMaxHealth(self.owner.sprite, 0);
1017 onslaught_updatelinks();
1019 // Use targets now (somebody make sure this is in the right place..)
1027 self.owner.waslinked = self.owner.islinked;
1028 if(self.owner.model != "models/onslaught/controlpoint_pad.md3")
1029 setmodel(self.owner, "models/onslaught/controlpoint_pad.md3");
1030 //setsize(self, '-32 -32 0', '32 32 8');
1036 void onslaught_controlpoint_icon_think()
1039 self.nextthink = time + sys_frametime;
1040 if (time > self.pain_finished + 5)
1042 if(self.health < self.max_health)
1044 self.health = self.health + self.count;
1045 if (self.health >= self.max_health)
1046 self.health = self.max_health;
1047 WaypointSprite_UpdateHealth(self.owner.sprite, self.health);
1050 if (self.health < self.max_health * 0.25)
1051 setmodel(self, "models/onslaught/controlpoint_icon_dmg3.md3");
1052 else if (self.health < self.max_health * 0.50)
1053 setmodel(self, "models/onslaught/controlpoint_icon_dmg2.md3");
1054 else if (self.health < self.max_health * 0.75)
1055 setmodel(self, "models/onslaught/controlpoint_icon_dmg1.md3");
1056 else if (self.health < self.max_health * 0.90)
1057 setmodel(self, "models/onslaught/controlpoint_icon.md3");
1058 // colormod flash when shot
1059 self.colormod = '1 1 1' * (2 - bound(0, (self.pain_finished - time) / 10, 1));
1061 if(self.owner.islinked != self.owner.waslinked)
1063 // unteam the spawnpoint if needed
1065 t = self.owner.team;
1066 if(!self.owner.islinked)
1067 self.owner.team = 0;
1075 self.owner.team = t;
1077 self.owner.waslinked = self.owner.islinked;
1080 if (self.punchangle_x > 0)
1082 self.punchangle_x = self.punchangle_x - 60 * sys_frametime;
1083 if (self.punchangle_x < 0)
1084 self.punchangle_x = 0;
1086 else if (self.punchangle_x < 0)
1088 self.punchangle_x = self.punchangle_x + 60 * sys_frametime;
1089 if (self.punchangle_x > 0)
1090 self.punchangle_x = 0;
1093 if (self.punchangle_y > 0)
1095 self.punchangle_y = self.punchangle_y - 60 * sys_frametime;
1096 if (self.punchangle_y < 0)
1097 self.punchangle_y = 0;
1099 else if (self.punchangle_y < 0)
1101 self.punchangle_y = self.punchangle_y + 60 * sys_frametime;
1102 if (self.punchangle_y > 0)
1103 self.punchangle_y = 0;
1106 if (self.punchangle_z > 0)
1108 self.punchangle_z = self.punchangle_z - 60 * sys_frametime;
1109 if (self.punchangle_z < 0)
1110 self.punchangle_z = 0;
1112 else if (self.punchangle_z < 0)
1114 self.punchangle_z = self.punchangle_z + 60 * sys_frametime;
1115 if (self.punchangle_z > 0)
1116 self.punchangle_z = 0;
1119 self.angles_x = self.punchangle_x;
1120 self.angles_y = self.punchangle_y + self.mangle_y;
1121 self.angles_z = self.punchangle_z;
1122 self.mangle_y = self.mangle_y + 45 * sys_frametime;
1124 self.cp_bob_origin_z = 4 * PI * (1 - cos(self.cp_bob_spd));
1125 self.cp_bob_spd = self.cp_bob_spd + 1.875 * sys_frametime;
1126 if(self.cp_bob_dmg_z > 0)
1127 self.cp_bob_dmg_z = self.cp_bob_dmg_z - 3 * sys_frametime;
1129 self.cp_bob_dmg_z = 0;
1130 setorigin(self,self.cp_origin + self.cp_bob_origin + self.cp_bob_dmg);
1133 if(random() < 0.6 - self.health / self.max_health)
1135 pointparticles(particleeffectnum("electricity_sparks"), self.origin + randompos('-10 -10 -20', '10 10 20'), '0 0 0', 1);
1138 sound(self, CH_PAIN, "onslaught/ons_spark1.wav", VOL_BASE, ATTN_NORM);
1139 else if (random() > 0.5)
1140 sound(self, CH_PAIN, "onslaught/ons_spark2.wav", VOL_BASE, ATTN_NORM);
1144 void onslaught_controlpoint_icon_buildthink()
1149 self.nextthink = time + sys_frametime;
1151 // only do this if there is power
1152 a = onslaught_controlpoint_can_be_linked(self.owner, self.owner.team);
1156 self.health = self.health + self.count;
1158 if (self.health >= self.max_health)
1160 self.health = self.max_health;
1161 self.count = autocvar_g_onslaught_cp_regen * sys_frametime; // slow repair rate from now on
1162 self.think = onslaught_controlpoint_icon_think;
1163 sound(self, CH_TRIGGER, "onslaught/controlpoint_built.wav", VOL_BASE, ATTN_NORM);
1164 bprint(ColoredTeamName(self.team), " captured ", self.owner.message, " control point\n");
1165 self.owner.iscaptured = TRUE;
1167 WaypointSprite_UpdateMaxHealth(self.owner.sprite, self.max_health);
1168 WaypointSprite_UpdateHealth(self.owner.sprite, self.health);
1170 onslaught_updatelinks();
1172 // Use targets now (somebody make sure this is in the right place..)
1178 self.cp_origin = self.origin;
1179 self.cp_bob_origin = '0 0 0.1';
1180 self.cp_bob_spd = 0;
1182 self.alpha = self.health / self.max_health;
1183 // colormod flash when shot
1184 self.colormod = '1 1 1' * (2 - bound(0, (self.pain_finished - time) / 10, 1));
1185 if(self.owner.model != "models/onslaught/controlpoint_pad2.md3")
1186 setmodel(self.owner, "models/onslaught/controlpoint_pad2.md3");
1187 //setsize(self, '-32 -32 0', '32 32 8');
1189 if(random() < 0.9 - self.health / self.max_health)
1190 pointparticles(particleeffectnum("rage"), self.origin + 10 * randomvec(), '0 0 -1', 1);
1196 void onslaught_controlpoint_touch()
1200 if (other.classname != "player")
1202 a = onslaught_controlpoint_attackable(self, other.team);
1203 if(a != 2 && a != 4)
1205 // we've verified that this player has a legitimate claim to this point,
1206 // so start building the captured point icon (which only captures this
1207 // point if it successfully builds without being destroyed first)
1208 self.goalentity = e = spawn();
1209 e.classname = "onslaught_controlpoint_icon";
1211 e.max_health = autocvar_g_onslaught_cp_health;
1212 e.health = autocvar_g_onslaught_cp_buildhealth;
1213 e.solid = SOLID_BBOX;
1214 e.movetype = MOVETYPE_NONE;
1215 setmodel(e, "models/onslaught/controlpoint_icon.md3");
1216 setsize(e, '-32 -32 -32', '32 32 32');
1217 setorigin(e, self.origin + '0 0 96');
1218 e.takedamage = DAMAGE_AIM;
1219 e.bot_attack = TRUE;
1220 e.event_damage = onslaught_controlpoint_icon_damage;
1221 e.team = other.team;
1222 e.colormap = 1024 + (e.team - 1) * 17;
1223 e.think = onslaught_controlpoint_icon_buildthink;
1224 e.nextthink = time + sys_frametime;
1225 e.count = (e.max_health - e.health) * sys_frametime / autocvar_g_onslaught_cp_buildtime; // how long it takes to build
1226 sound(e, CH_TRIGGER, "onslaught/controlpoint_build.wav", VOL_BASE, ATTN_NORM);
1228 self.colormap = e.colormap;
1229 WaypointSprite_UpdateBuildFinished(self.sprite, time + (e.max_health - e.health) / (e.count / sys_frametime));
1230 onslaught_updatelinks();
1233 void onslaught_controlpoint_reset()
1235 if(self.goalentity && self.goalentity != world)
1236 remove(self.goalentity);
1237 self.goalentity = world;
1239 self.colormap = 1024;
1240 self.iscaptured = FALSE;
1241 self.islinked = FALSE;
1242 self.isshielded = TRUE;
1243 self.enemy.solid = SOLID_NOT;
1244 self.enemy.colormap = self.colormap;
1245 self.think = self.enemy.think = SUB_Null;
1246 self.nextthink = 0; // don't like SUB_Null :P
1247 setmodel(self, "models/onslaught/controlpoint_pad.md3");
1248 //setsize(self, '-32 -32 0', '32 32 8');
1250 WaypointSprite_UpdateMaxHealth(self.sprite, 0);
1252 onslaught_updatelinks();
1255 SUB_UseTargets(); // to reset the structures, playerspawns etc.
1258 /*QUAKED spawnfunc_onslaught_controlpoint (0 .5 .8) (-32 -32 0) (32 32 128)
1259 Control point. Be sure to give this enough clearance so that the shootable part has room to exist
1261 This should link to an spawnfunc_onslaught_controlpoint entity or spawnfunc_onslaught_generator entity.
1264 "targetname" - name that spawnfunc_onslaught_link entities will use to target this.
1265 "target" - target any entities that are tied to this control point, such as vehicles and buildable structure entities.
1266 "message" - name of this control point (should reflect the location in the map, such as "center bridge", "north tower", etc)
1268 void spawnfunc_onslaught_controlpoint()
1276 precache_model("models/onslaught/controlpoint_pad.md3");
1277 precache_model("models/onslaught/controlpoint_pad2.md3");
1278 precache_model("models/onslaught/controlpoint_shield.md3");
1279 precache_model("models/onslaught/controlpoint_icon.md3");
1280 precache_model("models/onslaught/controlpoint_icon_dmg1.md3");
1281 precache_model("models/onslaught/controlpoint_icon_dmg2.md3");
1282 precache_model("models/onslaught/controlpoint_icon_dmg3.md3");
1283 precache_model("models/onslaught/controlpoint_icon_gib1.md3");
1284 precache_model("models/onslaught/controlpoint_icon_gib2.md3");
1285 precache_model("models/onslaught/controlpoint_icon_gib4.md3");
1286 precache_sound("onslaught/controlpoint_build.wav");
1287 precache_sound("onslaught/controlpoint_built.wav");
1288 precache_sound("weapons/grenade_impact.wav");
1289 precache_sound("onslaught/damageblockedbyshield.wav");
1290 precache_sound("onslaught/controlpoint_underattack.wav");
1291 precache_sound("onslaught/ons_spark1.wav");
1292 precache_sound("onslaught/ons_spark2.wav");
1293 self.solid = SOLID_BBOX;
1294 self.movetype = MOVETYPE_NONE;
1295 setmodel(self, "models/onslaught/controlpoint_pad.md3");
1296 //setsize(self, '-32 -32 0', '32 32 8');
1297 setorigin(self, self.origin);
1298 self.touch = onslaught_controlpoint_touch;
1300 self.colormap = 1024;
1301 self.iscaptured = FALSE;
1302 self.islinked = FALSE;
1303 self.isshielded = TRUE;
1304 // spawn shield model which indicates whether this can be damaged
1305 self.enemy = e = spawn();
1306 e.classname = "onslaught_controlpoint_shield";
1307 e.solid = SOLID_NOT;
1308 e.movetype = MOVETYPE_NONE;
1309 e.effects = EF_ADDITIVE;
1310 setmodel(e, "models/onslaught/controlpoint_shield.md3");
1311 //setsize(e, '-32 -32 0', '32 32 128');
1312 setorigin(e, self.origin);
1313 e.colormap = self.colormap;
1315 waypoint_spawnforitem(self);
1317 WaypointSprite_SpawnFixed(string_null, e.origin + '0 0 1' * e.maxs_z, self, sprite, RADARICON_NONE, '0 0 0');
1318 WaypointSprite_UpdateRule(self.sprite, COLOR_TEAM2, SPRITERULE_TEAMPLAY);
1320 onslaught_updatelinks();
1322 self.reset = onslaught_controlpoint_reset;
1325 float onslaught_link_send(entity to, float sendflags)
1327 WriteByte(MSG_ENTITY, ENT_CLIENT_RADARLINK);
1328 WriteByte(MSG_ENTITY, sendflags);
1331 WriteCoord(MSG_ENTITY, self.goalentity.origin_x);
1332 WriteCoord(MSG_ENTITY, self.goalentity.origin_y);
1333 WriteCoord(MSG_ENTITY, self.goalentity.origin_z);
1337 WriteCoord(MSG_ENTITY, self.enemy.origin_x);
1338 WriteCoord(MSG_ENTITY, self.enemy.origin_y);
1339 WriteCoord(MSG_ENTITY, self.enemy.origin_z);
1343 WriteByte(MSG_ENTITY, self.clientcolors); // which is goalentity's color + enemy's color * 16
1348 void onslaught_link_checkupdate()
1350 // TODO check if the two sides have moved (currently they won't move anyway)
1351 float redpower, bluepower;
1353 redpower = bluepower = 0;
1354 if(self.goalentity.islinked)
1356 if(self.goalentity.team == COLOR_TEAM1)
1358 else if(self.goalentity.team == COLOR_TEAM2)
1361 if(self.enemy.islinked)
1363 if(self.enemy.team == COLOR_TEAM1)
1365 else if(self.enemy.team == COLOR_TEAM2)
1370 if(redpower == 1 && bluepower == 2)
1371 cc = (COLOR_TEAM1 - 1) * 0x01 + (COLOR_TEAM2 - 1) * 0x10;
1372 else if(redpower == 2 && bluepower == 1)
1373 cc = (COLOR_TEAM1 - 1) * 0x10 + (COLOR_TEAM2 - 1) * 0x01;
1375 cc = (COLOR_TEAM1 - 1) * 0x11;
1377 cc = (COLOR_TEAM2 - 1) * 0x11;
1381 //print(etos(self), " rp=", ftos(redpower), " bp=", ftos(bluepower), " ");
1382 //print("cc=", ftos(cc), "\n");
1384 if(cc != self.clientcolors)
1386 self.clientcolors = cc;
1387 self.SendFlags |= 4;
1390 self.nextthink = time;
1393 void onslaught_link_delayed()
1395 self.goalentity = find(world, targetname, self.target);
1396 self.enemy = find(world, targetname, self.target2);
1397 if (!self.goalentity)
1398 objerror("can not find target\n");
1400 objerror("can not find target2\n");
1401 dprint(etos(self.goalentity), " linked with ", etos(self.enemy), "\n");
1402 self.SendFlags |= 3;
1403 self.think = onslaught_link_checkupdate;
1404 self.nextthink = time;
1407 /*QUAKED spawnfunc_onslaught_link (0 .5 .8) (-16 -16 -16) (16 16 16)
1408 Link between control points.
1410 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.
1413 "target" - first control point.
1414 "target2" - second control point.
1416 void spawnfunc_onslaught_link()
1423 if (self.target == "" || self.target2 == "")
1424 objerror("target and target2 must be set\n");
1425 InitializeEntity(self, onslaught_link_delayed, INITPRIO_FINDTARGET);
1426 Net_LinkEntity(self, FALSE, 0, onslaught_link_send);