/*************************************************
 * Decompiled by Serious and Edited by SyndiShanX
 * Script: shared\ai\robot_phalanx.gsc
*************************************************/

#using scripts\shared\ai\archetype_utility;
#using scripts\shared\ai_shared;
#using scripts\shared\math_shared;
#using scripts\shared\spawner_shared;

class robotphalanx {
  var scattered_;
  var tier1robots_;
  var tier2robots_;
  var tier3robots_;
  var endposition_;
  var startposition_;
  var phalanxtype_;
  var startrobotcount_;
  var breakingpoint_;
  var currentrobotcount_;

  constructor() {
    tier1robots_ = [];
    tier2robots_ = [];
    tier3robots_ = [];
    startrobotcount_ = 0;
    currentrobotcount_ = 0;
    breakingpoint_ = 0;
    scattered_ = 0;
  }

  destructor() {}

  function scatterphalanx() {
    if(!scattered_) {
      scattered_ = 1;
      _releaserobots(tier1robots_);
      tier1robots_ = [];
      _assignphalanxstance(tier2robots_, "crouch");
      wait(randomfloatrange(5, 7));
      _releaserobots(tier2robots_);
      tier2robots_ = [];
      _assignphalanxstance(tier3robots_, "crouch");
      wait(randomfloatrange(5, 7));
      _releaserobots(tier3robots_);
      tier3robots_ = [];
    }
  }

  function resumefire() {
    _resumefirerobots(tier1robots_);
    _resumefirerobots(tier2robots_);
    _resumefirerobots(tier3robots_);
  }

  function resumeadvance() {
    if(!scattered_) {
      _assignphalanxstance(tier1robots_, "stand");
      wait(1);
      forward = vectornormalize(endposition_ - startposition_);
      _movephalanxtier(tier1robots_, phalanxtype_, "phalanx_tier1", endposition_, forward);
      _movephalanxtier(tier2robots_, phalanxtype_, "phalanx_tier2", endposition_, forward);
      _movephalanxtier(tier3robots_, phalanxtype_, "phalanx_tier3", endposition_, forward);
      _assignphalanxstance(tier1robots_, "crouch");
    }
  }

  function initialize(phalanxtype, origin, destination, breakingpoint, maxtiersize = 10, tieronespawner = undefined, tiertwospawner = undefined, tierthreespawner = undefined) {
    assert(isstring(phalanxtype));
    assert(isint(breakingpoint));
    assert(isvec(origin));
    assert(isvec(destination));
    maxtiersize = math::clamp(maxtiersize, 1, 10);
    forward = vectornormalize(destination - origin);
    tier1robots_ = _createphalanxtier(phalanxtype, "phalanx_tier1", origin, forward, maxtiersize, tieronespawner);
    tier2robots_ = _createphalanxtier(phalanxtype, "phalanx_tier2", origin, forward, maxtiersize, tiertwospawner);
    tier3robots_ = _createphalanxtier(phalanxtype, "phalanx_tier3", origin, forward, maxtiersize, tierthreespawner);
    _assignphalanxstance(tier1robots_, "crouch");
    _movephalanxtier(tier1robots_, phalanxtype, "phalanx_tier1", destination, forward);
    _movephalanxtier(tier2robots_, phalanxtype, "phalanx_tier2", destination, forward);
    _movephalanxtier(tier3robots_, phalanxtype, "phalanx_tier3", destination, forward);
    startrobotcount_ = (tier1robots_.size + tier2robots_.size) + tier3robots_.size;
    breakingpoint_ = breakingpoint;
    startposition_ = origin;
    endposition_ = destination;
    phalanxtype_ = phalanxtype;
    self thread _updatephalanxthread(self);
  }

  function haltadvance() {
    if(!scattered_) {
      _haltadvance(tier1robots_);
      _haltadvance(tier2robots_);
      _haltadvance(tier3robots_);
    }
  }

  function haltfire() {
    _haltfire(tier1robots_);
    _haltfire(tier2robots_);
    _haltfire(tier3robots_);
  }

  function private _updatephalanx() {
    if(scattered_) {
      return false;
    }
    tier1robots_ = _prunedead(tier1robots_);
    tier2robots_ = _prunedead(tier2robots_);
    tier3robots_ = _prunedead(tier3robots_);
    currentrobotcount_ = (tier1robots_.size + tier2robots_.size) + tier2robots_.size;
    if(currentrobotcount_ <= (startrobotcount_ - breakingpoint_)) {
      scatterphalanx();
      return false;
    }
    return true;
  }

  function private _updatephalanxthread(phalanx) {
    while([[phalanx]] - > _updatephalanx()) {
      wait(1);
    }
  }

  function private _rotatevec(vector, angle) {
    return ((vector[0] * cos(angle)) - (vector[1] * sin(angle)), (vector[0] * sin(angle)) + (vector[1] * cos(angle)), vector[2]);
  }

  function private _resumefirerobots(robots) {
    assert(isarray(robots));
    foreach(robot in robots) {
      _resumefire(robot);
    }
  }

  function private _resumefire(robot) {
    if(isDefined(robot) && isalive(robot)) {
      robot.ignoreall = 0;
    }
  }

  function private _releaserobots(robots) {
    foreach(robot in robots) {
      _resumefire(robot);
      _releaserobot(robot);
      wait(randomfloatrange(0.5, 5));
    }
  }

  function private _releaserobot(robot) {
    if(isDefined(robot) && isalive(robot)) {
      robot clearuseposition();
      robot pathmode("move delayed", 1, randomfloatrange(0.5, 1));
      robot ai::set_behavior_attribute("phalanx", 0);
      wait(0.05);
      robot ai::set_behavior_attribute("move_mode", "normal");
      robot ai::set_behavior_attribute("force_cover", 0);
      robot setavoidancemask("avoid all");
      aiutility::removeaioverridedamagecallback(robot, &_dampenexplosivedamage);
    }
  }

  function private _prunedead(robots) {
    liverobots = [];
    foreach(index, robot in robots) {
      if(isDefined(robot) && isalive(robot)) {
        liverobots[index] = robot;
      }
    }
    return liverobots;
  }

  function private _movephalanxtier(robots, phalanxtype, tier, destination, forward) {
    positions = _getphalanxpositions(phalanxtype, tier);
    angles = vectortoangles(forward);
    assert(robots.size <= positions.size, "");
    foreach(index, robot in robots) {
      if(isDefined(robot) && isalive(robot)) {
        assert(isvec(positions[index]), (((("" + index) + "") + tier) + "") + phalanxtype);
        orientedpos = _rotatevec(positions[index], angles[1] - 90);
        navmeshposition = getclosestpointonnavmesh(destination + orientedpos, 200);
        robot useposition(navmeshposition);
      }
    }
  }

  function private _initializerobot(robot) {
    assert(isactor(robot));
    robot ai::set_behavior_attribute("phalanx", 1);
    robot ai::set_behavior_attribute("move_mode", "marching");
    robot ai::set_behavior_attribute("force_cover", 1);
    robot setavoidancemask("avoid none");
    aiutility::addaioverridedamagecallback(robot, &_dampenexplosivedamage, 1);
  }

  function private _haltfire(robots) {
    assert(isarray(robots));
    foreach(robot in robots) {
      if(isDefined(robot) && isalive(robot)) {
        robot.ignoreall = 1;
      }
    }
  }

  function private _haltadvance(robots) {
    assert(isarray(robots));
    foreach(robot in robots) {
      if(isDefined(robot) && isalive(robot) && robot haspath()) {
        navmeshposition = getclosestpointonnavmesh(robot.origin, 200);
        robot useposition(navmeshposition);
        robot clearpath();
      }
    }
  }

  function private _getphalanxspawner(tier) {
    spawner = getspawnerarray(tier, "targetname");
    assert(spawner.size >= 0, ("" + "") + "");
    assert(spawner.size == 1, ("" + "") + "");
    return spawner[0];
  }

  function private _getphalanxpositions(phalanxtype, tier) {
    switch (phalanxtype) {
      case "phanalx_wedge": {
        switch (tier) {
          case "phalanx_tier1": {
            return array((0, 0, 0), (-64, -48, 0), (64, -48, 0), (-128, -96, 0), (128, -96, 0));
          }
          case "phalanx_tier2": {
            return array((-32, -96, 0), (32, -96, 0));
          }
          case "phalanx_tier3": {
            return array();
          }
        }
        jump loc_00000928;
      }
      case "phalanx_diagonal_left": {
        switch (tier) {
          case "phalanx_tier1": {
            return array((0, 0, 0), (-48, -64, 0), (-96, -128, 0), (-144, -192, 0));
          }
          case "phalanx_tier2": {
            return array(vectorscale((1, 0, 0), 64), (16, -64, 0), (-48, -128, 0), (-112, -192, 0));
          }
          case "phalanx_tier3": {
            return array();
          }
        }
        loc_00000928:
          jump loc_000009E8;
      }
      case "phalanx_diagonal_right": {
        switch (tier) {
          case "phalanx_tier1": {
            return array((0, 0, 0), (48, -64, 0), (96, -128, 0), (144, -192, 0));
          }
          case "phalanx_tier2": {
            return array(vectorscale((-1, 0, 0), 64), (-16, -64, 0), (48, -128, 0), (112, -192, 0));
          }
          case "phalanx_tier3": {
            return array();
          }
        }
        loc_000009E8:
          jump loc_00000AA0;
      }
      case "phalanx_forward": {
        switch (tier) {
          case "phalanx_tier1": {
            return array((0, 0, 0), vectorscale((1, 0, 0), 64), vectorscale((1, 0, 0), 128), vectorscale((1, 0, 0), 192));
          }
          case "phalanx_tier2": {
            return array((-32, -64, 0), (32, -64, 0), (96, -64, 0), (160, -64, 0));
          }
          case "phalanx_tier3": {
            return array();
          }
        }
        loc_00000AA0:
          jump loc_00000B60;
      }
      case "phalanx_column": {
        switch (tier) {
          case "phalanx_tier1": {
            return array((0, 0, 0), vectorscale((-1, 0, 0), 64), vectorscale((0, -1, 0), 64), vectorscale((-1, -1, 0), 64));
          }
          case "phalanx_tier2": {
            return array(vectorscale((0, -1, 0), 128), (-64, -128, 0), vectorscale((0, -1, 0), 192), (-64, -192, 0));
          }
          case "phalanx_tier3": {
            return array();
          }
        }
        loc_00000B60:
          jump loc_00000BE8;
      }
      case "phalanx_column_right": {
        switch (tier) {
          case "phalanx_tier1": {
            return array((0, 0, 0), vectorscale((0, -1, 0), 64), vectorscale((0, -1, 0), 128), vectorscale((0, -1, 0), 192));
          }
          case "phalanx_tier2": {
            return array();
          }
          case "phalanx_tier3": {
            return array();
          }
        }
        loc_00000BE8:
          break;
      }
      default: {
        assert(("" + phalanxtype) + "");
      }
    }
    assert(("" + tier) + "");
  }

  function private _dampenexplosivedamage(inflictor, attacker, damage, flags, meansofdamage, weapon, point, dir, hitloc, offsettime, boneindex, modelindex) {
    entity = self;
    isexplosive = isinarray(array("MOD_GRENADE", "MOD_GRENADE_SPLASH", "MOD_PROJECTILE", "MOD_PROJECTILE_SPLASH", "MOD_EXPLOSIVE"), meansofdamage);
    if(isexplosive && isDefined(inflictor) && isDefined(inflictor.weapon)) {
      weapon = inflictor.weapon;
      distancetoentity = distance(entity.origin, inflictor.origin);
      fractiondistance = 1;
      if(weapon.explosionradius > 0) {
        fractiondistance = (weapon.explosionradius - distancetoentity) / weapon.explosionradius;
      }
      return int(max(damage * fractiondistance, 1));
    }
    return damage;
  }

  function private _createphalanxtier(phalanxtype, tier, phalanxposition, forward, maxtiersize, spawner = undefined) {
    robots = [];
    if(!isspawner(spawner)) {
      spawner = _getphalanxspawner(tier);
    }
    positions = _getphalanxpositions(phalanxtype, tier);
    angles = vectortoangles(forward);
    foreach(index, position in positions) {
      if(index >= maxtiersize) {
        break;
      }
      orientedpos = _rotatevec(position, angles[1] - 90);
      navmeshposition = getclosestpointonnavmesh(phalanxposition + orientedpos, 200);
      if(!spawner.spawnflags & 64) {
        spawner.count++;
      }
      robot = spawner spawner::spawn(1, "", navmeshposition, angles);
      if(isalive(robot)) {
        _initializerobot(robot);
        wait(0.05);
        robots[robots.size] = robot;
      }
    }
    return robots;
  }

  function private _assignphalanxstance(robots, stance) {
    assert(isarray(robots));
    foreach(robot in robots) {
      if(isDefined(robot) && isalive(robot)) {
        robot ai::set_behavior_attribute("phalanx_force_stance", stance);
      }
    }
  }
}