init()
{
  level endon("ex_gameover");

  while(true)
  {
    delay = randomInt(level.ex_artillery_delay_max - level.ex_artillery_delay_min) + level.ex_artillery_delay_min;
    if(delay < 20) delay = 20;
    wait(delay);

    artillery = spawn("script_origin", getTargetPosition());
    // ***** TEST ONLY *****
    //artillery = spawn("script_origin", getMeAsTargetPosition());

    artillery thread goArtillery();
  }
}

goArtillery()
{
  // calculate number of shells
  shellNumber = randomInt(level.ex_artillery_shells_max - level.ex_artillery_shells_min) + level.ex_artillery_shells_min / 2;

  // create artillery start position (surface level)
  artilleryStartPos = getPlayAreaStartPosition();
  artilleryStartPos = (artilleryStartPos[0], artilleryStartPos[1], 0);

  // artillery firing sound
  for(i = 0; i < shellNumber; i++ )
  {
    self thread firingSound();
    wait(.5);
  }

  // alert players
  if(level.ex_artillery_alert)
  {
    thread extreme\_ex_battlechatter::teamchatter("order_cover_generic", "both");
    wait(1);
  }

  // create shell target positions
  shellTargetPos = [];
  for(i = 0; i < shellNumber; i++)
    shellTargetPos[i] = calcShellPos(self.origin);

  self.artilleryGlobalDelay = 0;

  // fire shells
  for(i = 0; i < shellNumber; i++)
    self thread fireShell(artilleryStartPos, shellTargetPos[i]);

  // wait for all shells to explode
  shellImpacts = 0;
  while(shellImpacts < shellNumber)
  {
    self waittill("artillery_shell_impact");
    shellImpacts++;
  }

  self delete();
}

fireShell(shellStartPos, shellTargetPos)
{
  // calculate the height of the artillery spawn point to let shells come in at a certain angle
  //shellAngle = 60;
  //shellDist = distance(shellTargetPos, shellStartPos);
  //shellHeight = int(shellDist * tan(shellAngle));
  //shellStartPos = (shellStartPos[0], shellStartPos[1], shellHeight);
  shellStartPos = (shellTargetPos[0]-100, shellTargetPos[1]-100, level.ex_mapArea_Max[2]);

  self.artilleryGlobalDelay += randomFloatRange( .5, 1.5 );
  wait(self.artilleryGlobalDelay);
  wait(randomFloatRange(1.5, 2.5));

  // show visible artillery shell
  shell = spawn("script_model", shellStartPos);
  shell setModel("xmodel/prop_stuka_bomb");
  shell.angles = vectorToAngles(vectorNormalize(shellTargetPos - shellStartPos));

  // Play incoming sound
  shell playSound("artillery_incoming");

  // calculate time in air (s) based on distance (m) and preferred shell speed (m/s)!
  shellDist = distance(shellTargetPos, shellStartPos);
  shellDist = int(shellDist * 0.0254); // Convert distance to metres
  shellSpeed = 50;
  shellInAir = calcTime(shellDist, shellSpeed);

  // move visible artillery shell (correct target to slam shells into the ground, and for more realistic FX)
  shell moveTo(shellTargetPos + (0,0,-100), shellInAir);

  // wait for shell to hit
  wait(shellInAir);

  shell shellImpact();
  self notify("artillery_shell_impact");
}

shellImpact()
{
  playfx(level.ex_effect["artillery"], self.origin);
  self playSound("artillery_explosion");

  surfaceFx = calcImpactSurface(self.origin);
  if(level.ex_artillery == 1)
    self thread extreme\_ex_utils::scriptedFxRadiusDamage(self, undefined, "MOD_EXPLOSIVE", "artillery_mp", level.ex_artillery_radius, 0, 0, "generic", surfaceFx, true, true, true);

  if(level.ex_artillery == 2)
    self thread extreme\_ex_utils::scriptedFxRadiusDamage(self, undefined, "MOD_EXPLOSIVE", "artillery_mp", level.ex_artillery_radius, 500, 350, "generic", surfaceFx, true, true, true);

  self hide();
  wait(3);
  self delete();
}

calcTime(distvalue, speedvalue)
{
  distunit = 1;  // Metres
  speedunit = 1; // Metres per second
  timeinsec = (distvalue * distunit) / (speedvalue * speedunit);
  return timeinsec;
}

firingSound()
{
  players = getentarray("player", "classname");
  for(i = 0; i < players.size; i++)
    players[i] playLocalSound("artillery_fire");
}

getPlayAreaStartPosition(side)
{
  x = level.ex_playArea_Min[0];
  y = level.ex_playArea_Min[1];
  z = level.ex_playArea_Max[2];

  if(!isDefined(side)) side = randomInt(4);

  switch(side)
  {
    // North side of map area
    case 1: {
      x = level.ex_playArea_Max[0];
      y = randomInt(level.ex_playArea_Length);
      z = level.ex_playArea_Max[2];
      break;
    }
    // East side of map area
    case 2: {
      x = randomInt(level.ex_playArea_Width);
      y = level.ex_playArea_Min[1];
      z = level.ex_playArea_Max[2];
      break;
    }
    // South side of map area
    case 3: {
      x = randomInt(level.ex_playArea_Width);
      y = level.ex_playArea_Max[1];
      z = level.ex_playArea_Max[2];
      break;
    }
    // West side of map area
    default: {
      x = level.ex_playArea_Min[0];
      y = randomInt(level.ex_playArea_Length);
      z = level.ex_playArea_Max[2];
      break;
    }
  }

  return (x, y, z);
}

getMapAreaStartPosition(side)
{
  x = level.ex_mapArea_Min[0];
  y = level.ex_mapArea_Min[1];
  z = level.ex_mapArea_Max[2];

  if(!isDefined(side)) side = randomInt(4);

  switch(side)
  {
    // North side of map area
    case 1: {
      x = level.ex_mapArea_Max[0];
      y = randomInt(level.ex_mapArea_Length);
      z = level.ex_mapArea_Max[2];
      break;
    }
    // East side of map area
    case 2: {
      x = randomInt(level.ex_mapArea_Width);
      y = level.ex_mapArea_Min[1];
      z = level.ex_mapArea_Max[2];
      break;
    }
    // South side of map area
    case 3: {
      x = randomInt(level.ex_mapArea_Width);
      y = level.ex_mapArea_Max[1];
      z = level.ex_mapArea_Max[2];
      break;
    }
    // West side of map area
    default: {
      x = level.ex_mapArea_Min[0];
      y = randomInt(level.ex_mapArea_Length);
      z = level.ex_mapArea_Max[2];
      break;
    }
  }

  return (x, y, z);
}

getTargetPosition()
{
  x = level.ex_playArea_Min[0] + randomInt(level.ex_playArea_Width);
  y = level.ex_playArea_Min[1] + randomInt(level.ex_playArea_Length);
  z = level.ex_playArea_Min[2];

  return (x, y, z);
}

getMeAsTargetPosition()
{
  targetPos = getTargetPosition();
  players = getEntArray("player", "classname");
  for(i = 0; i < players.size; i++)
    if(players[i].name == "yourplayername") targetPos = players[i].origin;

  return targetPos;
}

calcShellPos(targetPos)
{
  shellPos = undefined;
  iterations = 0;

  while(!isDefined(shellPos) && iterations < 5)
  {
    shellPos = targetPos;
    angle = randomFloat(360);
    radius = randomFloat(level.ex_rank_artillery_radius);
    randomOffset = (cos(angle) * radius, sin(angle) * radius, 0);
    shellPos += randomOffset;
    startOrigin = shellPos + (0, 0, 800);
    endOrigin = shellPos + (0, 0, -2048);

    trace = bulletTrace( startOrigin, endOrigin, true, undefined );
    if(trace["fraction"] < 1.0) shellPos = trace["position"];
      else shellPos = undefined;

    iterations++;
  }
  return shellPos;
}

calcImpactSurface(targetPos)
{
  startOrigin = targetPos + (0, 0, 800);
  endOrigin = targetPos + (0, 0, -2048);

  trace = bulletTrace(startOrigin, endOrigin, true, undefined);
  if(trace["fraction"] < 1.0) surface = trace["surfacetype"];
    else surface = "dirt";

  return surface;
}
