Merge branch 'master' into Mario/ons_updates
authorMario <zacjardine@y7mail.com>
Sun, 23 Aug 2015 10:20:26 +0000 (20:20 +1000)
committerMario <zacjardine@y7mail.com>
Sun, 23 Aug 2015 10:20:26 +0000 (20:20 +1000)
# Conflicts:
# qcsrc/client/view.qc
# qcsrc/common/constants.qh
# qcsrc/server/g_damage.qc
# qcsrc/server/mutators/gamemode_onslaught.qc

20 files changed:
1  2 
qcsrc/client/autocvars.qh
qcsrc/client/command/cl_cmd.qc
qcsrc/client/hud.qc
qcsrc/client/main.qc
qcsrc/client/progs.src
qcsrc/client/view.qc
qcsrc/client/waypointsprites.qc
qcsrc/common/constants.qh
qcsrc/common/notifications.qh
qcsrc/common/stats.qh
qcsrc/server/autocvars.qh
qcsrc/server/bot/havocbot/roles.qc
qcsrc/server/g_world.qc
qcsrc/server/miscfunctions.qc
qcsrc/server/miscfunctions.qh
qcsrc/server/mutators/gamemode_onslaught.qc
qcsrc/server/mutators/mutators_include.qh
qcsrc/server/progs.src
qcsrc/server/teamplay.qc
qcsrc/server/waypointsprites.qc

Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
@@@ -1102,24 -1112,8 +1112,25 @@@ void CSQC_UpdateView(float w, float h
        // event chase camera
        if(autocvar_chase_active <= 0) // greater than 0 means it's enabled manually, and this code is skipped
        {
 -              if(WantEventchase())
+               float vehicle_chase = (hud != HUD_NORMAL && (autocvar_cl_eventchase_vehicle || spectatee_status > 0));
 +              float ons_roundlost = (gametype == MAPINFO_TYPE_ONSLAUGHT && getstati(STAT_ROUNDLOST));
 +              entity gen = world;
 +              
 +              if(ons_roundlost)
 +              {
 +                      entity e;
 +                      for(e = world; (e = find(e, classname, "onslaught_generator")); )
 +                      {
 +                              if(e.health <= 0)
 +                              {
 +                                      gen = e;
 +                                      break;
 +                              }
 +                      }
 +                      if(!gen)
 +                              ons_roundlost = FALSE; // don't enforce the 3rd person camera if there is no dead generator to show
 +              }
 +              if(WantEventchase() || (!autocvar_cl_orthoview && ons_roundlost))
                {
                        eventchase_running = true;
  
  
                        // detect maximum viewoffset and use it
                        vector view_offset = autocvar_cl_eventchase_viewoffset;
+                       if(vehicle_chase && autocvar_cl_eventchase_vehicle_viewoffset) { view_offset = autocvar_cl_eventchase_vehicle_viewoffset; }
 +                      if(ons_roundlost) { view_offset = autocvar_cl_eventchase_generator_viewoffset; }
  
                        if(view_offset)
                        {
  
                        // make the camera smooth back
                        float chase_distance = autocvar_cl_eventchase_distance;
+                       if(vehicle_chase && autocvar_cl_eventchase_vehicle_distance) { chase_distance = autocvar_cl_eventchase_vehicle_distance; }
 +                      if(ons_roundlost) { chase_distance = autocvar_cl_eventchase_generator_distance; }
  
                        if(autocvar_cl_eventchase_speed && eventchase_current_distance < chase_distance)
                                eventchase_current_distance += autocvar_cl_eventchase_speed * (chase_distance - eventchase_current_distance) * frametime; // slow down the further we get
  
        if(autocvar__hud_configure)
                HUD_Panel_Mouse();
 +      else 
 +              HUD_Radar_Mouse();
  
      if(hud && !intermission)
-     {
-         if(hud == HUD_SPIDERBOT)
-             CSQC_SPIDER_HUD();
-         else if(hud == HUD_WAKIZASHI)
-             CSQC_WAKIZASHI_HUD();
-         else if(hud == HUD_RAPTOR)
-             CSQC_RAPTOR_HUD();
-         else if(hud == HUD_BUMBLEBEE)
-             CSQC_BUMBLE_HUD();
-         else if(hud == HUD_BUMBLEBEE_GUN)
-             CSQC_BUMBLE_GUN_HUD();
-     }
+     if(hud == HUD_BUMBLEBEE_GUN)
+       CSQC_BUMBLE_GUN_HUD();
+     else
+               VEH_ACTION(hud, VR_HUD);
  
        cl_notice_run();
  
Simple merge
@@@ -115,8 -123,7 +123,9 @@@ const int ENT_CLIENT_TRIGGER_IMPULSE = 
  const int ENT_CLIENT_SWAMP = 69;
  const int ENT_CLIENT_CORNER = 70;
  const int ENT_CLIENT_KEYLOCK = 71;
 -const int ENT_CLIENT_EFFECT = 72;
 +const int ENT_CLIENT_GENERATOR = 72;
 +const int ENT_CLIENT_CONTROLPOINT_ICON = 73;
++const int ENT_CLIENT_EFFECT = 74;
  const int ENT_CLIENT_VIEWLOC = 78;
  const int ENT_CLIENT_VIEWLOC_TRIGGER = 79;
  
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
@@@ -353,270 -383,415 +353,270 @@@ int ons_ControlPoint_Attackable(entity 
        return 0;
  }
  
 -float overtime_msg_time;
 -void onslaught_generator_think()
 +void ons_ControlPoint_Icon_Damage(entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
  {
 -      float d;
 -      entity e;
 -      self.nextthink = ceil(time + 1);
 -      if (!gameover)
 +      entity oself;
 +      
 +      if(damage <= 0) { return; }
 +
 +      if (self.owner.isshielded)
        {
 -              if (autocvar_timelimit && time > game_starttime + autocvar_timelimit * 60)
 -              {
 -                      if (!overtime_msg_time)
 -                      {
 -                              Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_OVERTIME_CONTROLPOINT);
 -                              overtime_msg_time = time;
 -                      }
 -                      // self.max_health / 300 gives 5 minutes of overtime.
 -                      // control points reduce the overtime duration.
 -                      sound(self, CH_TRIGGER, "onslaught/generator_decay.wav", VOL_BASE, ATTEN_NORM);
 -                      d = 1;
 -                      e = findchain(classname, "onslaught_controlpoint");
 -                      while (e)
 +              // this is protected by a shield, so ignore the damage
 +              if (time > self.pain_finished)
 +                      if (IS_PLAYER(attacker))
                        {
 -                              if (e.team != self.team)
 -                                      if (e.islinked)
 -                                              d = d + 1;
 -                              e = e.chain;
 +                              play2(attacker, "onslaught/damageblockedbyshield.wav");
 +                              self.pain_finished = time + 1;
 +                              attacker.typehitsound += 1; // play both sounds (shield is way too quiet)
                        }
  
 -                      if(autocvar_g_campaign && autocvar__campaign_testrun)
 -                              d = d * self.max_health;
 -                      else
 -                              d = d * self.max_health / max(30, 60 * autocvar_timelimit_suddendeath);
 -
 -                      Damage(self, self, self, d, DEATH_HURTTRIGGER, self.origin, '0 0 0');
 -              }
 -              else if (overtime_msg_time)
 -                      overtime_msg_time = 0;
 -
 -        if(!self.isshielded && self.wait < time)
 -        {
 -            self.wait = time + 5;
 -            FOR_EACH_REALPLAYER(e)
 -            {
 -                              if(SAME_TEAM(e, self))
 -                {
 -                                      Send_Notification(NOTIF_ONE, e, MSG_CENTER, CENTER_ONS_NOTSHIELDED);
 -                    soundto(MSG_ONE, e, CHAN_AUTO, "kh/alarm.wav", VOL_BASE, ATTEN_NONE);    // FIXME: Uniqe sound?
 -                }
 -            }
 -        }
 +              return;
        }
 -}
 -
 -void onslaught_generator_ring_spawn(vector org)
 -{
 -      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);
 -}
  
 -void onslaught_generator_ray_think()
 -{
 -      self.nextthink = time + 0.05;
 -      if(self.count > 10)
 +      if(IS_PLAYER(attacker))
 +      if(time - ons_notification_time[self.team] > 10)
        {
 -              self.think = SUB_Remove;
 -              return;
 +              play2team(self.team, "onslaught/controlpoint_underattack.wav");
 +              ons_notification_time[self.team] = time;
        }
  
 -      if(self.count > 5)
 -              self.alpha -= 0.1;
 +      self.health = self.health - damage;
 +      if(self.owner.iscaptured)
 +              WaypointSprite_UpdateHealth(self.owner.sprite, self.health);
 +      else
 +              WaypointSprite_UpdateBuildFinished(self.owner.sprite, time + (self.max_health - self.health) / (self.count / ONS_CP_THINKRATE));
 +      self.pain_finished = time + 1;
 +      // particles on every hit
 +      pointparticles(particleeffectnum("sparks"), hitloc, force*-1, 1);
 +      //sound on every hit
 +      if (random() < 0.5)
 +              sound(self, CH_TRIGGER, "onslaught/ons_hit1.wav", VOL_BASE+0.3, ATTEN_NORM);
        else
 -              self.alpha += 0.1;
 +              sound(self, CH_TRIGGER, "onslaught/ons_hit2.wav", VOL_BASE+0.3, ATTEN_NORM);
  
 -      self.scale += 0.2;
 -      self.count +=1;
 -}
 +      if (self.health < 0)
 +      {
 +              sound(self, CH_TRIGGER, "weapons/grenade_impact.wav", VOL_BASE, ATTEN_NORM);
 +              pointparticles(particleeffectnum("rocket_explode"), self.origin, '0 0 0', 1);
 +              Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_NUM_4(self.team, INFO_ONSLAUGHT_CPDESTROYED_), self.owner.message, attacker.netname);
 +              
 +              PlayerScore_Add(attacker, SP_ONS_TAKES, 1);
 +              PlayerScore_Add(attacker, SP_SCORE, 10);
 +              
 +              self.owner.goalentity = world;
 +              self.owner.islinked = false;
 +              self.owner.iscaptured = false;
 +              self.owner.team = 0;
 +              self.owner.colormap = 1024;
  
 -void onslaught_generator_ray_spawn(vector org)
 -{
 -      entity e;
 -      e = spawn();
 -      setmodel(e, "models/onslaught/ons_ray.md3");
 -      setorigin(e, org);
 -      e.angles = randomvec() * 360;
 -      e.alpha = 0;
 -      e.scale = random() * 5 + 8;
 -      e.think = onslaught_generator_ray_think;
 -      e.nextthink = time + 0.05;
 -}
 +              WaypointSprite_UpdateMaxHealth(self.owner.sprite, 0);
  
 -void onslaught_generator_shockwave_spawn(vector org)
 -{
 -      shockwave_spawn("models/onslaught/shockwave.md3", org, -64, 0.75, 0.5);
 -}
 +              onslaught_updatelinks();
  
 -void onslaught_generator_damage_think()
 -{
 -      if(self.owner.health < 0)
 -      {
 -              self.think = SUB_Remove;
 -              return;
 -      }
 -      self.nextthink = time+0.1;
 +              // Use targets now (somebody make sure this is in the right place..)
 +              oself = self;
 +              self = self.owner;
 +              activator = self;
 +              SUB_UseTargets ();
 +              self = oself;
  
 -      // damaged fx (less probable the more damaged is the generator)
 -      if(random() < 0.9 - self.owner.health / self.owner.max_health)
 -              if(random() < 0.01)
 -              {
 -                      Send_Effect("electro_ballexplode", self.origin + randompos('-50 -50 -20', '50 50 50'), '0 0 0', 1);
 -                      sound(self, CH_TRIGGER, "onslaught/electricity_explode.wav", VOL_BASE, ATTEN_NORM);
 -              }
 -              else
 -                      Send_Effect("torch_small", self.origin + randompos('-60 -60 -20', '60 60 60'), '0 0 0', 1);
 -}
 +              self.owner.waslinked = self.owner.islinked;
 +              if(self.owner.model != "models/onslaught/controlpoint_pad.md3")
 +                      setmodel_fixsize(self.owner, "models/onslaught/controlpoint_pad.md3");
 +              //setsize(self, '-32 -32 0', '32 32 8');
  
 -void onslaught_generator_damage_spawn(entity gd_owner)
 -{
 -      entity e;
 -      e = spawn();
 -      e.owner = gd_owner;
 -      e.health = self.owner.health;
 -      setorigin(e, gd_owner.origin);
 -      e.think = onslaught_generator_damage_think;
 -      e.nextthink = time+1;
 +              remove(self);
 +      }
 +      
 +      self.SendFlags |= CPSF_STATUS;
  }
  
 -void onslaught_generator_deaththink()
 +void ons_ControlPoint_Icon_Think()
  {
 -      vector org;
 -      float i;
 -
 -      if (!self.count)
 -              self.count = 40;
 +      entity oself;
 +      self.nextthink = time + ONS_CP_THINKRATE;
  
 -      // White shockwave
 -      if(self.count==40||self.count==20)
 +      if(autocvar_g_onslaught_cp_proxydecap)
        {
 -              onslaught_generator_ring_spawn(self.origin);
 -              sound(self, CH_TRIGGER, "onslaught/shockwave.wav", VOL_BASE, ATTEN_NORM);
 -      }
 +        int _enemy_count = 0;
 +        int _friendly_count = 0;
 +        float _dist;
 +        entity _player;
  
 -      // Throw some gibs
 -      if(random() < 0.3)
 -      {
 -              i = random();
 -              if(i < 0.3)
 -                      ons_throwgib(self.origin + '0 0 40', (100 * randomvec() - '1 1 1') * 11 + '0 0 20', "models/onslaught/gen_gib1.md3", 6, true);
 -              else if(i > 0.7)
 -                      ons_throwgib(self.origin + '0 0 40', (100 * randomvec() - '1 1 1') * 12 + '0 0 20', "models/onslaught/gen_gib2.md3", 6, true);
 -              else
 -                      ons_throwgib(self.origin + '0 0 40', (100 * randomvec() - '1 1 1') * 13 + '0 0 20', "models/onslaught/gen_gib3.md3", 6, true);
 -      }
 +        FOR_EACH_PLAYER(_player)
 +        {
 +            if(!_player.deadflag)
 +            {
 +                _dist = vlen(_player.origin - self.origin);
 +                if(_dist < autocvar_g_onslaught_cp_proxydecap_distance)
 +                {
 +                                      if(SAME_TEAM(_player, self))
 +                        ++_friendly_count;
 +                    else
 +                        ++_enemy_count;
 +                }
 +            }
 +        }
 +
 +        _friendly_count = _friendly_count * (autocvar_g_onslaught_cp_proxydecap_dps * ONS_CP_THINKRATE);
 +        _enemy_count = _enemy_count * (autocvar_g_onslaught_cp_proxydecap_dps * ONS_CP_THINKRATE);
 +
 +        self.health = bound(0, self.health + (_friendly_count - _enemy_count), self.max_health);
 +              self.SendFlags |= CPSF_STATUS;
 +        if(self.health <= 0)
 +        {
 +            ons_ControlPoint_Icon_Damage(self, self, 1, 0, self.origin, '0 0 0');
 +            return;
 +        }
 +    }
  
 -      // Spawn fire balls
 -      for(i=0;i < 10;++i)
 +      if (time > self.pain_finished + 5)
        {
 -              org = self.origin + randompos('-30 -30 -30' * i + '0 0 -20', '30 30 30' * i + '0 0 20');
 -              Send_Effect("onslaught_generator_gib_explode", org, '0 0 0', 1);
 +              if(self.health < self.max_health)
 +              {
 +                      self.health = self.health + self.count;
 +                      if (self.health >= self.max_health)
 +                              self.health = self.max_health;
 +                      WaypointSprite_UpdateHealth(self.owner.sprite, self.health);
 +              }
        }
  
 -      // Short explosion sound + small explosion
 -      if(random() < 0.25)
 +      if(self.owner.islinked != self.owner.waslinked)
        {
 -              te_explosion(self.origin);
 -              sound(self, CH_TRIGGER, "weapons/grenade_impact.wav", VOL_BASE, ATTEN_NORM);
 -      }
 +              // unteam the spawnpoint if needed
 +              int t = self.owner.team;
 +              if(!self.owner.islinked)
 +                      self.owner.team = 0;
 +
 +              oself = self;
 +              self = self.owner;
 +              activator = self;
 +              SUB_UseTargets ();
 +              self = oself;
  
 -      // Particles
 -      org = self.origin + randompos(self.mins + '8 8 8', self.maxs + '-8 -8 -8');
 -      Send_Effect("onslaught_generator_smallexplosion", org, '0 0 0', 1);
 +              self.owner.team = t;
  
 -      // rays
 -      if(random() > 0.25 )
 -      {
 -              onslaught_generator_ray_spawn(self.origin);
 +              self.owner.waslinked = self.owner.islinked;
        }
  
 -      // Final explosion
 -      if(self.count==1)
 +      // damaged fx
 +      if(random() < 0.6 - self.health / self.max_health)
        {
-               pointparticles(particleeffectnum("electricity_sparks"), self.origin + randompos('-10 -10 -20', '10 10 20'), '0 0 0', 1);
 -              org = self.origin;
 -              te_explosion(org);
 -              onslaught_generator_shockwave_spawn(org);
 -              Send_Effect("onslaught_generator_finalexplosion", org, '0 0 0', 1);
 -              sound(self, CH_TRIGGER, "weapons/rocket_impact.wav", VOL_BASE, ATTEN_NORM);
 -      }
 -      else
 -              self.nextthink = time + 0.05;
++              Send_Effect("electricity_sparks", self.origin + randompos('-10 -10 -20', '10 10 20'), '0 0 0', 1);
  
 -      self.count = self.count - 1;
 +              if(random() > 0.8)
 +                      sound(self, CH_PAIN, "onslaught/ons_spark1.wav", VOL_BASE, ATTEN_NORM);
 +              else if (random() > 0.5)
 +                      sound(self, CH_PAIN, "onslaught/ons_spark2.wav", VOL_BASE, ATTEN_NORM);
 +      }
  }
  
 -void onslaught_generator_damage(entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
 +void ons_ControlPoint_Icon_BuildThink()
  {
 -      float i;
 -      if (damage <= 0)
 -              return;
 -      if(warmup_stage)
 +      entity oself;
 +      int a;
 +
 +      self.nextthink = time + ONS_CP_THINKRATE;
 +
 +      // only do this if there is power
 +      a = ons_ControlPoint_CanBeLinked(self.owner, self.owner.team);
 +      if(!a)
                return;
 -      if (attacker != self)
 +
 +      self.health = self.health + self.count;
 +      
 +      self.SendFlags |= CPSF_STATUS;
 +
 +      if (self.health >= self.max_health)
        {
 -              if (self.isshielded)
 -              {
 -                      // this is protected by a shield, so ignore the damage
 -                      if (time > self.pain_finished)
 -                              if (IS_PLAYER(attacker))
 -                              {
 -                                      play2(attacker, "onslaught/damageblockedbyshield.wav");
 -                                      self.pain_finished = time + 1;
 -                              }
 -                      return;
 -              }
 -              if (time > self.pain_finished)
 -              {
 -                      self.pain_finished = time + 10;
 -                      bprint(Team_ColoredFullName(self.team), " generator under attack!\n");
 -                      play2team(self.team, "onslaught/generator_underattack.wav");
 -              }
 -      }
 -      self.health = self.health - damage;
 -      WaypointSprite_UpdateHealth(self.sprite, self.health);
 -      // choose an animation frame based on health
 -      self.frame = 10 * bound(0, (1 - self.health / self.max_health), 1);
 -      // see if the generator is still functional, or dying
 -      if (self.health > 0)
 -      {
 -#ifdef ONSLAUGHT_SPAM
 -              float h, lh;
 -              lh = ceil(self.lasthealth / 100) * 100;
 -              h = ceil(self.health / 100) * 100;
 -              if(lh != h)
 -                      bprint(Team_ColoredFullName(self.team), " generator has less than ", ftos(h), " health remaining\n");
 -#endif
 -              self.lasthealth = self.health;
 -      }
 -      else if (!warmup_stage)
 -      {
 -              if (attacker == self)
 -                      bprint(Team_ColoredFullName(self.team), " generator spontaneously exploded due to overtime!\n");
 -              else
 -              {
 -                      string t;
 -                      t = Team_ColoredFullName(attacker.team);
 -                      bprint(Team_ColoredFullName(self.team), " generator destroyed by ", t, "!\n");
 -              }
 -              self.iscaptured = false;
 -              self.islinked = false;
 -              self.isshielded = false;
 -              self.takedamage = DAMAGE_NO; // can't be hurt anymore
 -              self.event_damage = func_null; // won't do anything if hurt
 -              self.count = 0; // reset counter
 -              self.think = onslaught_generator_deaththink; // explosion sequence
 -              self.nextthink = time; // start exploding immediately
 -              self.think(); // do the first explosion now
 +              self.health = self.max_health;
 +              self.count = autocvar_g_onslaught_cp_regen * ONS_CP_THINKRATE; // slow repair rate from now on
 +              self.think = ons_ControlPoint_Icon_Think;
 +              sound(self, CH_TRIGGER, "onslaught/controlpoint_built.wav", VOL_BASE, ATTEN_NORM);
 +              self.owner.iscaptured = true;
 +              self.solid = SOLID_BBOX;
  
 -              WaypointSprite_UpdateMaxHealth(self.sprite, 0);
 +              pointparticles(particleeffectnum(sprintf("%s_cap", Static_Team_ColorName_Lower(self.owner.team))), self.owner.origin, '0 0 0', 1);
  
 -              onslaught_updatelinks();
 -      }
 +              WaypointSprite_UpdateMaxHealth(self.owner.sprite, self.max_health);
 +              WaypointSprite_UpdateHealth(self.owner.sprite, self.health);
  
 -      if(self.health <= 0)
 -              setmodel(self, "models/onslaught/generator_dead.md3");
 -      else if(self.health < self.max_health * 0.10)
 -              setmodel(self, "models/onslaught/generator_dmg9.md3");
 -      else if(self.health < self.max_health * 0.20)
 -              setmodel(self, "models/onslaught/generator_dmg8.md3");
 -      else if(self.health < self.max_health * 0.30)
 -              setmodel(self, "models/onslaught/generator_dmg7.md3");
 -      else if(self.health < self.max_health * 0.40)
 -              setmodel(self, "models/onslaught/generator_dmg6.md3");
 -      else if(self.health < self.max_health * 0.50)
 -              setmodel(self, "models/onslaught/generator_dmg5.md3");
 -      else if(self.health < self.max_health * 0.60)
 -              setmodel(self, "models/onslaught/generator_dmg4.md3");
 -      else if(self.health < self.max_health * 0.70)
 -              setmodel(self, "models/onslaught/generator_dmg3.md3");
 -      else if(self.health < self.max_health * 0.80)
 -              setmodel(self, "models/onslaught/generator_dmg2.md3");
 -      else if(self.health < self.max_health * 0.90)
 -              setmodel(self, "models/onslaught/generator_dmg1.md3");
 -      setsize(self, '-52 -52 -14', '52 52 75');
 +              if(IS_PLAYER(self.owner.ons_toucher))
 +              {
 +                      Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_ONSLAUGHT_CAPTURE, self.owner.ons_toucher.netname, self.owner.message);
 +                      Send_Notification(NOTIF_ALL_EXCEPT, self.owner.ons_toucher, MSG_CENTER, APP_TEAM_ENT_4(self.owner.ons_toucher, CENTER_ONS_CAPTURE_), self.owner.message);
 +                      Send_Notification(NOTIF_ONE, self.owner.ons_toucher, MSG_CENTER, CENTER_ONS_CAPTURE, self.owner.message);
 +                      PlayerScore_Add(self.owner.ons_toucher, SP_ONS_CAPS, 1);
 +                      PlayerTeamScore_AddScore(self.owner.ons_toucher, 10);
 +              }
 +              
 +              self.owner.ons_toucher = world;
  
 -      // Throw some flaming gibs on damage, more damage = more chance for gib
 -      if(random() < damage/220)
 -      {
 -              sound(self, CH_TRIGGER, "weapons/rocket_impact.wav", VOL_BASE, ATTEN_NORM);
 -              i = random();
 -              if(i < 0.3)
 -                      ons_throwgib(hitloc + '0 0 20', force * -1, "models/onslaught/gen_gib1.md3", 5, true);
 -              else if(i > 0.7)
 -                      ons_throwgib(hitloc + '0 0 20', force * -1, "models/onslaught/gen_gib2.md3", 5, true);
 -              else
 -                      ons_throwgib(hitloc + '0 0 20', force * -1, "models/onslaught/gen_gib3.md3", 5, true);
 -      }
 -      else
 -      {
 -              // particles on every hit
 -              Send_Effect("sparks", hitloc, force * -1, 1);
 +              onslaught_updatelinks();
  
 -              //sound on every hit
 -              if (random() < 0.5)
 -                      sound(self, CH_TRIGGER, "onslaught/ons_hit1.wav", VOL_BASE, ATTEN_NORM);
 -              else
 -                      sound(self, CH_TRIGGER, "onslaught/ons_hit2.wav", VOL_BASE, ATTEN_NORM);
 +              // Use targets now (somebody make sure this is in the right place..)
 +              oself = self;
 +              self = self.owner;
 +              activator = self;
 +              SUB_UseTargets ();
 +              self = oself;
 +              
 +              self.SendFlags |= CPSF_SETUP;
        }
 -
 -      //throw some gibs on damage
 -      if(random() < damage/200+0.2)
 -              if(random() < 0.5)
 -                      ons_throwgib(hitloc + '0 0 20', randomvec()*360, "models/onslaught/gen_gib1.md3", 5, false);
 +      if(self.owner.model != "models/onslaught/controlpoint_pad2.md3")
 +              setmodel_fixsize(self.owner, "models/onslaught/controlpoint_pad2.md3");
 +              
 +      if(random() < 0.9 - self.health / self.max_health)
 +              pointparticles(particleeffectnum("rage"), self.origin + 10 * randomvec(), '0 0 -1', 1);
  }
  
 -// update links after a delay
 -void onslaught_generator_delayed()
 +void ons_ControlPoint_Icon_Spawn(entity cp, entity player)
  {
 -      onslaught_updatelinks();
 -      // now begin normal thinking
 -      self.think = onslaught_generator_think;
 -      self.nextthink = time;
 -}
 +      entity e = spawn();
 +      
 +      setsize(e, CPICON_MIN, CPICON_MAX);
 +      setorigin(e, cp.origin + CPICON_OFFSET);
 +      
 +      e.classname = "onslaught_controlpoint_icon";
 +      e.owner = cp;
 +      e.max_health = autocvar_g_onslaught_cp_health;
 +      e.health = autocvar_g_onslaught_cp_buildhealth;
 +      e.solid = SOLID_NOT;
 +      e.takedamage = DAMAGE_AIM;
 +      e.bot_attack = true;
 +      e.event_damage = ons_ControlPoint_Icon_Damage;
 +      e.team = player.team;
 +      e.colormap = 1024 + (e.team - 1) * 17;
 +      e.count = (e.max_health - e.health) * ONS_CP_THINKRATE / autocvar_g_onslaught_cp_buildtime; // how long it takes to build
 +      
 +      sound(e, CH_TRIGGER, "onslaught/controlpoint_build.wav", VOL_BASE, ATTEN_NORM);
 +      
 +      cp.goalentity = e;
 +      cp.team = e.team;
 +      cp.colormap = e.colormap;
  
 -string onslaught_generator_waypointsprite_for_team(entity e, float t)
 -{
 -      if(t == e.team)
 -      {
 -              if(e.team == NUM_TEAM_1)
 -                      return "ons-gen-red";
 -              else if(e.team == NUM_TEAM_2)
 -                      return "ons-gen-blue";
 -      }
 -      if(e.isshielded)
 -              return "ons-gen-shielded";
 -      if(e.team == NUM_TEAM_1)
 -              return "ons-gen-red";
 -      else if(e.team == NUM_TEAM_2)
 -              return "ons-gen-blue";
 -      return "";
 -}
 +      pointparticles(particleeffectnum(sprintf("%sflag_touch", Static_Team_ColorName_Lower(player.team))), e.origin, '0 0 0', 1);
  
 -void onslaught_generator_updatesprite(entity e)
 -{
 -      string s1, s2, s3;
 -      s1 = onslaught_generator_waypointsprite_for_team(e, NUM_TEAM_1);
 -      s2 = onslaught_generator_waypointsprite_for_team(e, NUM_TEAM_2);
 -      s3 = onslaught_generator_waypointsprite_for_team(e, -1);
 -      WaypointSprite_UpdateSprites(e.sprite, s1, s2, s3);
 +      WaypointSprite_UpdateBuildFinished(cp.sprite, time + (e.max_health - e.health) / (e.count / ONS_CP_THINKRATE));
 +      WaypointSprite_UpdateRule(cp.sprite,cp.team,SPRITERULE_TEAMPLAY);
 +      cp.sprite.SendFlags |= 16;
  
 -      if(e.lastteam != e.team + 2 || e.lastshielded != e.isshielded)
 -      {
 -              e.lastteam = e.team + 2;
 -              e.lastshielded = e.isshielded;
 -              if(e.lastshielded)
 -              {
 -                      if(e.team == NUM_TEAM_1 || e.team == NUM_TEAM_2)
 -                              WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, 0.5 * colormapPaletteColor(e.team - 1, false));
 -                      else
 -                              WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, '0.5 0.5 0.5');
 -              }
 -              else
 -              {
 -                      if(e.team == NUM_TEAM_1 || e.team == NUM_TEAM_2)
 -                              WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, colormapPaletteColor(e.team - 1, false));
 -                      else
 -                              WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, '0.75 0.75 0.75');
 -              }
 -              WaypointSprite_Ping(e.sprite);
 -      }
 +      onslaught_controlpoint_icon_link(e, ons_ControlPoint_Icon_BuildThink);
  }
  
 -string onslaught_controlpoint_waypointsprite_for_team(entity e, float t)
 +string ons_ControlPoint_Waypoint(entity e)
  {
 -      float a;
 -      if(t != -1)
 +      if(e.team)
        {
 -              a = onslaught_controlpoint_attackable(e, t);
 -              if(a == 3 || a == 4) // ATTACK/TOUCH THIS ONE NOW
 -              {
 -                      if(e.team == NUM_TEAM_1)
 -                              return "ons-cp-atck-red";
 -                      else if(e.team == NUM_TEAM_2)
 -                              return "ons-cp-atck-blue";
 -                      else
 -                              return "ons-cp-atck-neut";
 -              }
 -              else if(a == -2) // DEFEND THIS ONE NOW
 -              {
 -                      if(e.team == NUM_TEAM_1)
 -                              return "ons-cp-dfnd-red";
 -                      else if(e.team == NUM_TEAM_2)
 -                              return "ons-cp-dfnd-blue";
 -              }
 -              else if(e.team == t || a == -1 || a == 1) // own point, or fire at it
 -              {
 -                      if(e.team == NUM_TEAM_1)
 -                              return "ons-cp-red";
 -                      else if(e.team == NUM_TEAM_2)
 -                              return "ons-cp-blue";
 -              }
 -              else if(a == 2) // touch it
 -                      return "ons-cp-neut";
 +              int a = ons_ControlPoint_Attackable(e, e.team);
 +              
 +              if(a == -2) { return "ons-cp-dfnd"; } // defend now
 +              if(a == -1 || a == 1 || a == 2) { return "ons-cp"; } // touch
 +              if(a == 3 || a == 4) { return "ons-cp-atck"; } // attack
        }
        else
 -      {
 -              if(e.team == NUM_TEAM_1)
 -                      return "ons-cp-red";
 -              else if(e.team == NUM_TEAM_2)
 -                      return "ons-cp-blue";
 -              else
 -                      return "ons-cp-neut";
 -      }
 +              return "ons-cp";
 +
        return "";
  }
  
@@@ -2039,134 -1572,154 +2039,140 @@@ MUTATOR_HOOKFUNCTION(ons_SV_ParseClient
        return 0;
  }
  
 -MUTATOR_HOOKFUNCTION(ons_PlayerSpawn)
 +MUTATOR_HOOKFUNCTION(ons_PlayerUseKey)
  {
 -    if(!autocvar_g_onslaught_spawn_at_controlpoints)
 -        return 0;
 +      if(MUTATOR_RETURNVALUE || gameover) { return false; }
 +      
 +      if((time > self.teleport_antispam) && (self.deadflag == DEAD_NO) && !self.vehicle)
 +      {
 +              entity source_point = ons_Nearest_ControlPoint(self.origin, autocvar_g_onslaught_teleport_radius);
 +              if ( source_point )
 +              {
 +                      stuffcmd(self, "qc_cmd_cl hud clickradar\n");
 +                      return true;
 +              }
 +      }
 +      
 +      return false;
 +}
  
 -    if(random() < 0.5)  // 50/50 chane to use default spawnsystem.
 -        return 0;
++MUTATOR_HOOKFUNCTION(ons_PlayHitsound)
++{
++      return (frag_victim.classname == "onslaught_generator" && !frag_victim.isshielded)
++              || (frag_victim.classname == "onslaught_controlpoint_icon" && !frag_victim.owner.isshielded);
++}
 -    float _close_to_home = ((random() > 0.5) ? true : false);
 -    entity _best = world, _trg_gen = world;
 -    float _score, _best_score = MAX_SHOT_DISTANCE;
 +// ==========
 +// Spawnfuncs
 +// ==========
  
 -      RandomSelection_Init();
 +/*QUAKED spawnfunc_onslaught_link (0 .5 .8) (-16 -16 -16) (16 16 16)
 +  Link between control points.
  
 -      if(self.team == NUM_TEAM_1)
 -      {
 -          if(!_close_to_home)
 -            _trg_gen = ons_blue_generator;
 -        else
 -            _trg_gen  = ons_red_generator;
 -      }
 +  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.
  
 -      if(self.team == NUM_TEAM_2)
 -      {
 -          if(_close_to_home)
 -            _trg_gen = ons_blue_generator;
 -        else
 -            _trg_gen  = ons_red_generator;
 -      }
 +keys:
 +"target" - first control point.
 +"target2" - second control point.
 + */
 +void spawnfunc_onslaught_link()
 +{
 +      if(!g_onslaught) { remove(self); return; }
  
 -      entity _cp = findchain(classname, "onslaught_controlpoint");
 -      while(_cp)
 -      {
 -          if(_cp.team == self.team)
 -        {
 -            _score = vlen(_trg_gen.origin - _cp.origin);
 -            if(_score < _best_score)
 -            {
 -                _best = _cp;
 -                _best_score = _score;
 -            }
 -        }
 -              _cp = _cp.chain;
 -      }
 +      if (self.target == "" || self.target2 == "")
 +              objerror("target and target2 must be set\n");
  
 -    vector _loc;
 -    float i;
 -    if(_best)
 -    {
 -        for(i = 0; i < 10; ++i)
 -        {
 -            _loc = _best.origin + '0 0 96';
 -            _loc += ('0 1 0' * random()) * 128;
 -            tracebox(_loc, PL_MIN, PL_MAX, _loc, MOVE_NORMAL, self);
 -            if(trace_fraction == 1.0 && !trace_startsolid)
 -            {
 -                setorigin(self, _loc);
 -                self.angles = normalize(_loc - _best.origin) * RAD2DEG;
 -                return 0;
 -            }
 -        }
 -    }
 -    else
 -    {
 -        if(!autocvar_g_onslaught_spawn_at_generator)
 -            return 0;
 +      self.ons_worldlinknext = ons_worldlinklist; // link into ons_worldlinklist
 +      ons_worldlinklist = self;
  
 -        _trg_gen = ((self.team == NUM_TEAM_1) ? ons_red_generator : ons_blue_generator);
 +      InitializeEntity(self, ons_DelayedLinkSetup, INITPRIO_FINDTARGET);
 +      Net_LinkEntity(self, false, 0, ons_Link_Send);
 +}
  
 -        for(i = 0; i < 10; ++i)
 -        {
 -            _loc = _trg_gen.origin + '0 0 96';
 -            _loc += ('0 1 0' * random()) * 128;
 -            tracebox(_loc, PL_MIN, PL_MAX, _loc, MOVE_NORMAL, self);
 -            if(trace_fraction == 1.0 && !trace_startsolid)
 -            {
 -                setorigin(self, _loc);
 -                self.angles = normalize(_loc - _trg_gen.origin) * RAD2DEG;
 -                return 0;
 -            }
 -        }
 -    }
 +/*QUAKED spawnfunc_onslaught_controlpoint (0 .5 .8) (-32 -32 0) (32 32 128)
 +  Control point. Be sure to give this enough clearance so that the shootable part has room to exist
  
 -    return 0;
 -}
 +  This should link to an spawnfunc_onslaught_controlpoint entity or spawnfunc_onslaught_generator entity.
  
 -MUTATOR_HOOKFUNCTION(ons_TurretSpawn)
 -{
 -      entity e, ee = world;
 -      if(self.targetname)
 -      {
 -              e = find(world, target, self.targetname);
 +keys:
 +"targetname" - name that spawnfunc_onslaught_link entities will use to target this.
 +"target" - target any entities that are tied to this control point, such as vehicles and buildable structure entities.
 +"message" - name of this control point (should reflect the location in the map, such as "center bridge", "north tower", etc)
 + */
  
 -              if(e != world)
 -              {
 -                      self.team = e.team;
 -                      ee = e;
 -              }
 -      }
 +void spawnfunc_onslaught_controlpoint()
 +{
 +      if(!g_onslaught) { remove(self); return; }
        
 -      if(ee)
 -      {
 -              activator = ee;
 -              self.use();
 -      }
 -
 -      return FALSE;
 +      ons_ControlPoint_Setup(self);
  }
  
 -MUTATOR_HOOKFUNCTION(ons_MonsterThink)
 +/*QUAKED spawnfunc_onslaught_generator (0 .5 .8) (-32 -32 -24) (32 32 64)
 +  Base generator.
 +
 +  spawnfunc_onslaught_link entities can target this.
 +
 +keys:
 +"team" - team that owns this generator (5 = red, 14 = blue, etc), MUST BE SET.
 +"targetname" - name that spawnfunc_onslaught_link entities will use to target this.
 + */
 +void spawnfunc_onslaught_generator()
  {
 -      entity e = find(world, targetname, self.target);
 -      if (e != world)
 -              self.team = e.team;
 +      if(!g_onslaught) { remove(self); return; }
 +      if(!self.team) { objerror("team must be set"); }
  
 -      return false;
 +      ons_GeneratorSetup(self);
  }
  
 -MUTATOR_HOOKFUNCTION(ons_MonsterSpawn)
 +// scoreboard setup
 +void ons_ScoreRules()
  {
 -      entity e, ee = world;
 -
 -      if(self.targetname)
 -      {
 -              e = find(world,target,self.targetname);
 -        activator = ee;
 -        self.use();
 -    }
 +      CheckAllowedTeams(world);
 +      ScoreRules_basics(((c4>=0) ? 4 : (c3>=0) ? 3 : 2), SFL_SORT_PRIO_PRIMARY, 0, true);
 +      ScoreInfo_SetLabel_TeamScore  (ST_ONS_CAPS,     "destroyed", SFL_SORT_PRIO_PRIMARY);
 +      ScoreInfo_SetLabel_PlayerScore(SP_ONS_CAPS,     "caps",      SFL_SORT_PRIO_SECONDARY);
 +      ScoreInfo_SetLabel_PlayerScore(SP_ONS_TAKES,    "takes",     0);
 +      ScoreRules_basics_end();
 +}
  
 -      return false;
 +void ons_DelayedInit() // Do this check with a delay so we can wait for teams to be set up
 +{
 +      ons_ScoreRules();
 +      
 +      round_handler_Spawn(Onslaught_CheckPlayers, Onslaught_CheckWinner, Onslaught_RoundStart);
 +      round_handler_Init(5, autocvar_g_onslaught_warmup, autocvar_g_onslaught_round_timelimit);
  }
  
 -MUTATOR_HOOKFUNCTION(ons_PlayHitsound)
 +void ons_Initialize()
  {
 -      return (frag_victim.classname == "onslaught_generator" && !frag_victim.isshielded)
 -              || (frag_victim.classname == "onslaught_controlpoint_icon" && !frag_victim.owner.isshielded);
 +      precache_sound("ctf/red_capture.wav");
 +      precache_sound("ctf/blue_capture.wav");
 +      precache_sound("ctf/yellow_capture.wav");
 +      precache_sound("ctf/pink_capture.wav");
 +
 +      ons_captureshield_force = autocvar_g_onslaught_shield_force;
 +
 +      addstat(STAT_ROUNDLOST, AS_INT, ons_roundlost);
 +
 +      InitializeEntity(world, ons_DelayedInit, INITPRIO_GAMETYPE);
  }
  
  MUTATOR_DEFINITION(gamemode_onslaught)
  {
 -      MUTATOR_HOOK(BuildMutatorsPrettyString, ons_BuildMutatorsPrettyString, CBC_ORDER_ANY);
 -      MUTATOR_HOOK(BuildMutatorsString, ons_BuildMutatorsString, CBC_ORDER_ANY);
 +      MUTATOR_HOOK(reset_map_global, ons_ResetMap, CBC_ORDER_ANY);
 +      MUTATOR_HOOK(MakePlayerObserver, ons_RemovePlayer, CBC_ORDER_ANY);
 +      MUTATOR_HOOK(ClientDisconnect, ons_RemovePlayer, CBC_ORDER_ANY);
        MUTATOR_HOOK(PlayerSpawn, ons_PlayerSpawn, CBC_ORDER_ANY);
 -      MUTATOR_HOOK(TurretSpawn, ons_TurretSpawn, CBC_ORDER_ANY);
 +      MUTATOR_HOOK(PlayerDies, ons_PlayerDies, CBC_ORDER_ANY);
        MUTATOR_HOOK(MonsterMove, ons_MonsterThink, CBC_ORDER_ANY);
        MUTATOR_HOOK(MonsterSpawn, ons_MonsterSpawn, CBC_ORDER_ANY);
 -      //MUTATOR_HOOK(Spawn_Score, ons_Spawn_Score, CBC_ORDER_ANY);
 +      MUTATOR_HOOK(TurretSpawn, ons_TurretSpawn, CBC_ORDER_ANY);
 +      MUTATOR_HOOK(HavocBot_ChooseRole, ons_BotRoles, CBC_ORDER_ANY);
 +      MUTATOR_HOOK(GetTeamCount, ons_GetTeamCount, CBC_ORDER_ANY);
 +      MUTATOR_HOOK(SpectateCopy, ons_SpectateCopy, CBC_ORDER_ANY);
 +      MUTATOR_HOOK(SV_ParseClientCommand, ons_SV_ParseClientCommand, CBC_ORDER_ANY);
 +      MUTATOR_HOOK(PlayerUseKey, ons_PlayerUseKey, CBC_ORDER_ANY);
+       MUTATOR_HOOK(PlayHitsound, ons_PlayHitsound, CBC_ORDER_ANY);
  
        MUTATOR_ONADD
        {
Simple merge
Simple merge
Simple merge