share/hedgewars/Data/Scripts/Multiplayer/Battalion.lua
author Wuzzy <Wuzzy2@mail.ru>
Sat, 07 Jul 2018 22:50:08 +0200
changeset 13456 7d69c5605a9d
parent 13035 da691787fcd2
child 13648 690cc84e9fd6
permissions -rwxr-xr-x
Highlander: Localize global variable

--[[
  ########################################################################
  Name:      Battalion
  Made by:   Anachron 
  ########################################################################
]]--

--[[
  
  Readme:
  https://hedgewars.org/wiki/Battalion

  ########################################################################
]]--

--[[
  ########################################################################
  TODO / ideas list
  ########################################################################

  - Make Hogs sorted by rareness for teams with less hogs (more fair)
  - Keep first picked up unused crate utitlity until next round
  - Ship default scheme but let user overwrite it
  - Make SuddenDeathWaterRise dynamic
  - Make SuddenDeathTurns dynamic
  - Add Hog Variants like Crazy Scientist or Astronaut

  ########################################################################
]]--

--[[
  ##############################################################################
  ### GENERAL SCRIPT LOADING AND VARIABLE INITIALISATION                     ###
  ##############################################################################
]]--

HedgewarsScriptLoad("/Scripts/Locale.lua")
HedgewarsScriptLoad("/Scripts/Tracker.lua")
HedgewarsScriptLoad("/Scripts/Params.lua")

-- List of all hog variants with belonging weapons,
--  hitpoints, chances and more
local variants = {}
local varName = ""
local newLine = ""
local gmAny = 0xFFFFFFFF

--[[
  ##############################################################################
  ### VARIANT SETUP                                                          ###
  ##############################################################################
]]--

varName = "Pyromancer"
variants[varName] = {}
variants[varName]["name"] = loc("Pyromancer")
variants[varName]["chance"] = 7
variants[varName]["hat"] = "Gasmask"
variants[varName]["hp"] = 70
variants[varName]["hogLimit"] = 2
variants[varName]["weaponLimit"] = 1
variants[varName]["weapons"] = {amFlamethrower, amMolotov, amWhip}
variants[varName]["helperLimit"] = 1
variants[varName]["helpers"] = {amRope, amParachute}
variants[varName]["special"] = false

varName = "Builder"
variants[varName] = {}
variants[varName]["name"] = loc("Builder")
variants[varName]["chance"] = 10
variants[varName]["hat"] = "constructor"
variants[varName]["hp"] = 100
variants[varName]["hogLimit"] = 1
variants[varName]["weaponLimit"] = 1
variants[varName]["weapons"] = {amDynamite, amWhip, amHammer}
variants[varName]["helperLimit"] = 1
variants[varName]["helpers"] = {amGirder, amBlowTorch}
variants[varName]["special"] = false

varName = "Rifleman"
variants[varName] = {}
variants[varName]["name"] = loc("Rifleman")
variants[varName]["chance"] = 7
variants[varName]["hat"] = "Sniper"
variants[varName]["hp"] = 70
variants[varName]["hogLimit"] = 2
variants[varName]["weaponLimit"] = 1
variants[varName]["weapons"] = {amRCPlane, amShotgun, amSniperRifle}
variants[varName]["helperLimit"] = 1
variants[varName]["helpers"] = {amLowGravity, amParachute}
variants[varName]["special"] = false

varName = "Warrior"
variants[varName] = {}
variants[varName]["name"] = loc("Warrior")
variants[varName]["chance"] = 12
variants[varName]["hat"] = "spartan"
variants[varName]["hp"] = 120
variants[varName]["hogLimit"] = 2
variants[varName]["weaponLimit"] = 1
variants[varName]["weapons"] = {amClusterBomb, amGrenade, amBazooka}
variants[varName]["helperLimit"] = 1
variants[varName]["helpers"] = {amParachute, amRope}
variants[varName]["special"] = false

varName = "Chef"
variants[varName] = {}
variants[varName]["name"] = loc("Chef")
variants[varName]["chance"] = 7
variants[varName]["hat"] = "chef"
variants[varName]["hp"] = 70
variants[varName]["hogLimit"] = 1
variants[varName]["weaponLimit"] = 1
variants[varName]["weapons"] = {amCake, amKnife, amWhip}
variants[varName]["helperLimit"] = 1
variants[varName]["helpers"] = {amRubber, amParachute}
variants[varName]["special"] = false

varName = "Medic"
variants[varName] = {}
variants[varName]["name"] = loc("Medic")
variants[varName]["chance"] = 12
variants[varName]["hat"] = "war_desertmedic"
variants[varName]["hp"] = 120
variants[varName]["hogLimit"] = 1
variants[varName]["weaponLimit"] = 1
variants[varName]["weapons"] = {amResurrector, amMine, amGasBomb}
variants[varName]["helperLimit"] = 1
variants[varName]["helpers"] = {amTeleport, amParachute}
variants[varName]["special"] = false

varName = "Ninja"
variants[varName] = {}
variants[varName]["name"] = loc("Ninja")
variants[varName]["chance"] = 8
variants[varName]["hat"] = "NinjaTriangle"
variants[varName]["hp"] = 80
variants[varName]["hogLimit"] = 2
variants[varName]["weaponLimit"] = 1
variants[varName]["weapons"] = {amSMine, amMine, amFirePunch}
variants[varName]["helperLimit"] = 1
variants[varName]["helpers"] = {amRope, amParachute}
variants[varName]["special"] = false

varName = "Athlete"
variants[varName] = {}
variants[varName]["name"] = loc("Athlete")
variants[varName]["chance"] = 8
variants[varName]["hat"] = "footballhelmet"
variants[varName]["hp"] = 80
variants[varName]["hogLimit"] = 1
variants[varName]["weaponLimit"] = 1
variants[varName]["weapons"] = {amBaseballBat, amFirePunch, amSeduction}
variants[varName]["helperLimit"] = 1
variants[varName]["helpers"] = {amRope, amPickHammer}
variants[varName]["special"] = false

varName = "Scientist"
variants[varName] = {}
variants[varName]["name"] = loc("Scientist")
variants[varName]["chance"] = 7
variants[varName]["hat"] = "doctor"
variants[varName]["hp"] = 80
variants[varName]["hogLimit"] = 1
variants[varName]["weaponLimit"] = 1
variants[varName]["weapons"] = {amPortalGun, amSineGun, amIceGun}
variants[varName]["helperLimit"] = 1
variants[varName]["helpers"] = {amTeleport, amJetpack}  
variants[varName]["special"] = false

varName = "Air General"
variants[varName] = {}
variants[varName]["name"] = loc("Air General")
variants[varName]["chance"] = 5
variants[varName]["hat"] = "war_desertofficer"
variants[varName]["hp"] = 50
variants[varName]["hogLimit"] = 1
variants[varName]["weaponLimit"] = 1
variants[varName]["weapons"] = {amMineStrike, amNapalm, amAirAttack}
variants[varName]["helperLimit"] = 1
variants[varName]["helpers"] = {amRope, amParachute}
variants[varName]["special"] = true

varName = "Hunter"
variants[varName] = {}
variants[varName]["name"] = loc("Hunter")
variants[varName]["chance"] = 10
variants[varName]["hat"] = "Skull"
variants[varName]["hp"] = 100
variants[varName]["hogLimit"] = 1
variants[varName]["weaponLimit"] = 1
variants[varName]["weapons"] = {amBee, amMortar, amDrill}
variants[varName]["helperLimit"] = 1
variants[varName]["helpers"] = {amRope, amParachute}
variants[varName]["special"] = false

varName = "King"
variants[varName] = {}
variants[varName]["name"] = loc("King")
variants[varName]["chance"] = 3
variants[varName]["hat"] = "crown"
variants[varName]["hp"] = 60
variants[varName]["hogLimit"] = 1
variants[varName]["weaponLimit"] = 1
variants[varName]["weapons"] = {amWatermelon, amHellishBomb, amBallgun}
variants[varName]["helperLimit"] = 1
variants[varName]["helpers"] = {amRope, amParachute}
variants[varName]["special"] = true

varName = "Knight"
variants[varName] = {}
variants[varName]["name"] = loc("Knight")
variants[varName]["chance"] = 0
variants[varName]["hat"] = "knight"
variants[varName]["hp"] = 80
variants[varName]["hogLimit"] = 1
variants[varName]["weaponLimit"] = 1
variants[varName]["weapons"] = {amShotgun, amBazooka, amMine}
variants[varName]["helperLimit"] = 1
variants[varName]["helpers"] = {amParachute, amRope}
variants[varName]["special"] = true

--[[
  ##############################################################################
  ### GENERAL VARIABLES USED FOR GAMEPLAY                                    ###
  ##############################################################################
]]--

local unused = {amSnowball, amDrillStrike, amTardis}
local lowWeaps = {amKamikaze}
local lowTresh = 25

local counter = {} -- Saves how many hogs of a variant a team has
local group = {} -- Saves randomized variants for all teams
local teamIndex = {} -- Temporary counter for amount of mutated hogs in team
local teamHogs = {} -- Saves a list of all hogs belonging to a team
local hogCount = {} -- Saves how many hogs a team has
local teamNames = {} -- Saves all teams and names
local hogInfo = {} -- Saves all hogs with their original values

local LastHog = nil -- Last Hedgehog
local CurHog = nil -- Current Hedgehog
local LastTeam = nil -- Last Team
local CurTeam = nil -- Current Team

local mode = 'default' -- Which game type to play
local modeExplicit = false -- Whether the mode was set in script param
local luck = 100 -- Multiplier for bonuses like crates
local strength = 1 -- Multiplier for more weapons
local useVariantHats = true -- Whether to overwrite the hog hats to those of their variants
                            -- In King Mode, crowns are always enforced regardless of this setting
local useVariantNames = false -- Whether to overwrite the hog names to those of their variants

local highHasBonusWeps = false -- whether or not a hog got bonus weapons on current turn
local highHasBonusHelp = false -- whether or not a hog got bonus helpers on current turn
local highPickupCount = 1
local highPickupSDCount = 2
local highHelperCount = 1
local highHelperSDCount = 1
local highEnemyKillHPBonus = 10
local highFriendlyKillHPBonus = 15
local highWeapons = {} -- Saves the weapons from kills
local highHelpers = {} -- Saves the helpers from kills
local highSpecialBonus = {amTeleport, amJetpack}
local highSpecialPool = {amExtraDamage, amVampiric}

local kingLinkPerc = 50 -- Percentage of life to share from the team

local pointsWepBase = 5 -- Game start points weapons
local pointsHlpBase = 2 -- Game start points helpers
local pointsKeepPerc = 80 -- Percentage of points to take to next round
local pointsWepTurn = 5 -- Round bonus points weapons
local pointsHlpTurn = 2 -- Round bonus points helpers
local pointsWepMax = 25 -- Maximum points for weapons
local pointsHlpMax = 10 -- Maximum points for helpers
local pointsKeepSDPerc = 60 -- Percentage of points to take to next round on SD
local pointsWepSDTurn = 7 -- Round bonus points weapons on SD
local pointsHlpSDTurn = 3 -- Round bonus points helpers on SD
local pointsWepSDMax = 35 -- Maximum points for weapons on SD
local pointsHlpSDMax = 15 -- Maximum points for helpers on SD

local pointsWeaponVal = {}
pointsWeaponVal[amBazooka] = 5
pointsWeaponVal[amShotgun] = 4
pointsWeaponVal[amFirePunch] = 3
pointsWeaponVal[amMine] = 5
--pointsWeaponVal[amAirAttack] = 10
pointsWeaponVal[amBee] = 6
pointsWeaponVal[amClusterBomb] = 7
pointsWeaponVal[amGrenade] = 5
pointsWeaponVal[amDEagle] = 3
pointsWeaponVal[amWhip] = 3
pointsWeaponVal[amDynamite] = 7
--pointsWeaponVal[amMineStrike] = 14
pointsWeaponVal[amMortar] = 4
pointsWeaponVal[amWatermelon] = 30
pointsWeaponVal[amSniperRifle] = 3
pointsWeaponVal[amBaseballBat] = 3
pointsWeaponVal[amCake] = 7
--pointsWeaponVal[amNapalm] = 11
pointsWeaponVal[amDrill] = 6
pointsWeaponVal[amHellishBomb] = 20
pointsWeaponVal[amSineGun] = 4
--pointsWeaponVal[amKamikaze] = 3
--pointsWeaponVal[amBallgun] = 12
--pointsWeaponVal[amPianoStrike] = 15
pointsWeaponVal[amSnowball] = 2
pointsWeaponVal[amMolotov] = 3
pointsWeaponVal[amFlamethrower] = 4
pointsWeaponVal[amRCPlane] = 7
--pointsWeaponVal[amDrillStrike] = 12
pointsWeaponVal[amGasBomb] = 2
pointsWeaponVal[amHammer] = 3
pointsWeaponVal[amSMine] = 4
pointsWeaponVal[amAirMine] = 3
pointsWeaponVal[amKnife] = 3
pointsWeaponVal[amPortalGun] = 5
--pointsWeaponVal[amIceGun] = 6
pointsWeaponVal[amSeduction] = 2

local pointsHelperVal = {}
pointsHelperVal[amRope] = 5
pointsHelperVal[amParachute] = 2
--pointsHelperVal[amGirder] = 3
pointsHelperVal[amBlowTorch] = 2
pointsHelperVal[amLowGravity] = 3
--pointsHelperVal[amRubber] = 4
pointsHelperVal[amPickHammer] = 2
pointsHelperVal[amTeleport] = 10
pointsHelperVal[amJetpack] = 8

local pointsPerTeam = {}
local pointsToWep = {} -- List of [points] = {ammo1, ammo2}
local pointsToHlp = {} -- List of [points] = {ammo1, ammo2}
local wepPoints = {}
local hlpPoints = {}

local firstTurnOver = false
local suddenDeath = false

local healthCrateChance = 7
local utilCrateChance = 9
local weaponCrateChance = 12

local healthCrateChanceSD = 12
local utilCrateChanceSD = 16
local weaponCrateChanceSD = 21

local emptyCrateChance = 7
local bonusCrateChance = 5
local cratePickupGap = 35

local utilities = {amInvulnerable, amVampiric, amExtraTime, amExtraDamage, amRope, amLandGun}
local autoSelectHelpers = {amRope, amParachute}

local LastWaterLine = 0 -- Saves WaterLine to make sure a water rise wont trigger highland kill

local helpers = {}
helpers[amSkip] = true
helpers[amRope] = true
helpers[amParachute] = true
helpers[amBlowTorch] = true
helpers[amGirder] = true
helpers[amTeleport] = true
helpers[amSwitch] = true
helpers[amJetpack] = true
helpers[amBirdy] = true
helpers[amPortalGun] = true
helpers[amResurrector] = true
helpers[amTardis] = true
helpers[amLandGun] = true
helpers[amRubber] = true
--helpers[amKamikaze] = true

local posCaseAmmo    = 1
local posCaseHealth  = 2
local posCaseUtility = 4
local posCaseDummy   = 8

--[[
  ##############################################################################
  ### GENERAL BONUS LUA FUNCTIONS                                            ###
  ##############################################################################
]]--

function swap(array, index1, index2)
    array[index1], array[index2] = array[index2], array[index1]
end

function shuffle(array)
    local cnt = #array
    while cnt > 1 do
        local index = GetRandom(cnt) +1
        swap(array, index, cnt)
        cnt = cnt - 1
    end
end

function table.clone(org)
  local copy = {}
  for orig_key, orig_value in pairs(org) do
      copy[orig_key] = orig_value
  end
  return copy
end

--[[
  ##############################################################################
  ### WEAPON, UTILITY AND AMMO FUNCTIONS                                     ###
  ##############################################################################
]]--

function clearHogAmmo(hog)
  for val=0, AmmoTypeMax do
    AddAmmo(hog, val, 0)
  end
end

function autoSelectAmmo(hog, var)
  -- Check if hog has any "useful" helper, select helper, if yes
  for key, val in pairs(autoSelectHelpers) do
    if GetAmmoCount(hog, val) > 0 then
      SetWeapon(val)
      return
    end
  end
end

function AddHogAmmo(hog, ammo)
  -- Add weapons of variant
  for key, val in pairs(ammo) do
    AddAmmo(hog, val, GetAmmoCount(hog, val) +1)
  end
end

function GetRandomAmmo(hog, sourceType)
  local var = getHogInfo(hog, 'variant')
  ammo = {}
  local source = ''

  if variants[var] == nil then
    return ammo
  end

  if sourceType == "weapons" then
    source = variants[var][sourceType]
    sourceLimit = variants[var]["weaponLimit"]
  elseif sourceType == "helpers" then
    source = variants[var][sourceType]
    sourceLimit = variants[var]["helperLimit"]
  elseif sourceType == 'poolWeapons' then
    if highWeapons[hog] == nil then
      highWeapons[hog] = {}
    end
    source = highWeapons[hog]
    if suddenDeath == false then
      sourceLimit = highPickupCount
    else
      sourceLimit = highPickupSDCount
    end
  elseif sourceType == 'poolHelpers' then
    if highHelpers[hog] == nil then
      highHelpers[hog] = {}
    end
    source = highHelpers[hog]
    if suddenDeath == false then
      sourceLimit = highHelperCount
    else
      sourceLimit = highHelperSDCount
    end
  else
    return ammo
  end
  
  local varAmmo = {}
  for key, val in pairs(source) do
      varAmmo[key] = val
  end
  
  -- If the amount of random weapons is equally to the amount of weapons possible
  -- We don't need to randomize
  if sourceLimit >= table.getn(source) then
    return varAmmo
  end

  local randIndex = 0
  local i = 0
  while i < sourceLimit and #varAmmo > 0 do
    randIndex = GetRandom(#varAmmo) +1
    ammo[i] = varAmmo[randIndex]

    -- Shift last value to the current index
    varAmmo[randIndex] = varAmmo[#varAmmo]
    -- And remove the last index from the array
    varAmmo[#varAmmo] = nil
    i = i +1
  end

  return ammo
end

function updatePointsLabel(team)
  SetTeamLabel(team, string.format(loc("%d / %d"), pointsPerTeam[team]["weapons"], pointsPerTeam[team]["helpers"]))
end

function addTurnAmmo(hog)
  -- Check if hog is valid
  if hog == nil then
    return
  end

  -- Check if hog is alive
  local hp = GetHealth(hog)
  if hp == nil or hp <= 0 then
    return
  end

  -- Unless its points mode, get weapons normally by variant
  if mode ~= "points" then
    local maxHp = getHogInfo(hog, 'maxHp')
    local hpPer = div(hp * 100, maxHp)

    local wep = getHogInfo(hog, 'weapons')
    local hlp = getHogInfo(hog, 'helpers')

    if wep == nil or table.getn(wep) == 0 then
      hogInfo[hog]['weapons'] = GetRandomAmmo(hog, "weapons")
      wep = getHogInfo(hog, 'weapons')
    end

    if hlp == nil or table.getn(hlp) == 0 then
      hogInfo[hog]['helpers'] = GetRandomAmmo(hog, "helpers")
      hlp = getHogInfo(hog, 'helpers')
    end

    AddHogAmmo(hog, wep)
    AddHogAmmo(hog, hlp)

    if mode == 'highland' then
      local poolWeapons = GetRandomAmmo(hog, 'poolWeapons')
      local poolHelpers = GetRandomAmmo(hog, 'poolHelpers')

      AddHogAmmo(hog, poolWeapons)
      AddHogAmmo(hog, poolHelpers)
    end

    if hpPer < lowTresh or suddenDeath == true then
      AddHogAmmo(hog, lowWeaps)
    end
  -- We are on points mode, so we need to generate weapons based on points
  else
    setupPointsAmmo(hog)
  end

  AddAmmo(hog, amSkip, -1)
end

function setupPointsAmmo(hog)
  local teamName = getHogInfo(hog, 'team')
  local turnWepPoints = pointsPerTeam[teamName]['weapons']
  local turnHlpPoints = pointsPerTeam[teamName]['helpers']
  local weps = {}
  local help = {}

  local wepPointsTmp = table.clone(wepPoints)
  local wepMinPnt = wepPointsTmp[1]
  local wepMaxPnt = wepPointsTmp[#wepPointsTmp]

  --AddCaption("Hog: " .. hog .. " Wep: " .. turnWepPoints .. " - Hlp: " .. turnHlpPoints, GetClanColor(GetHogClan(CurHog)),  capgrpGameState)
  --WriteLnToConsole("BEFORE ## Team: " .. teamName .. " Wep: " .. pointsPerTeam[teamName]['weapons'] .. " - Hlp: " .. pointsPerTeam[teamName]['helpers'])

  while true do
    if turnWepPoints < wepMinPnt then
      break
    end

    if wepPointsTmp[#wepPointsTmp] > turnWepPoints then
      while wepPointsTmp[#wepPointsTmp] > turnWepPoints do
        table.remove(wepPointsTmp)
      end
      wepMaxPnt = turnWepPoints
    end

    local randPoint = wepPointsTmp[GetRandom(#wepPointsTmp) +1]
    local randWepList = pointsToWep[randPoint]
    local randWep = randWepList[GetRandom(#randWepList) +1]

    table.insert(weps, randWep)
    turnWepPoints = turnWepPoints -randPoint
  end

  local hlpPointsTmp = table.clone(hlpPoints)
  local hlpMinPnt = hlpPointsTmp[1]
  local hlpMaxPnt = hlpPointsTmp[#hlpPointsTmp]

  while true do
    if turnHlpPoints < hlpMinPnt then
      break
    end

    if hlpPointsTmp[#hlpPointsTmp] > turnHlpPoints then
      while hlpPointsTmp[#hlpPointsTmp] > turnHlpPoints do
        table.remove(hlpPointsTmp)
      end
      hlpMaxPnt = turnHlpPoints
    end

    local randPoint = hlpPointsTmp[GetRandom(#hlpPointsTmp) +1]
    local randHlpList = pointsToHlp[randPoint]
    local randHlp = randHlpList[GetRandom(#randHlpList) +1]

    table.insert(help, randHlp)
    turnHlpPoints = turnHlpPoints -randPoint
  end

  AddHogAmmo(hog, weps)
  AddHogAmmo(hog, help)

  -- Save remaining points
  pointsPerTeam[teamName]['weaponsRem'] = turnWepPoints
  pointsPerTeam[teamName]['helpersRem'] = turnHlpPoints

  -- Save already collected points so that they wont be "taxed"
  pointsPerTeam[teamName]['weaponsFix'] = pointsPerTeam[teamName]['weapons']
  pointsPerTeam[teamName]['helpersFix'] = pointsPerTeam[teamName]['helpers']

  --WriteLnToConsole("AFTER ## Team: " .. teamName .. " Wep: " .. pointsPerTeam[teamName]['weapons'] .. " - Hlp: " .. pointsPerTeam[teamName]['helpers'])
end

--[[
  ##############################################################################
  ### HOG SETUP  FUNCTIONS                                                   ###
  ##############################################################################
]]--

-- Overwrite hog hat to that of its variant
function SetHogVariantHat(hog)
  local var = getHogInfo(hog, 'variant')
  SetHogHat(hog, variants[var]["hat"])
end

-- Give a crown if the hog is a king.
-- Strip the hog from its crown if
-- it is not a king.
function SetHogVariantHatKingMode(hog)
  local var = getHogInfo(hog, 'variant')
  if var == "King" then
    SetHogHat(hog, variants[var]["hat"])
  elseif GetHogHat(hog) == "crown" then
    SetHogHat(hog, "NoHat")
  end
end

-- Overwrite hog name to that of its variant
function SetHogVariantName(hog)
  local var = getHogInfo(hog, 'variant')
  SetHogName(hog, variants[var]["name"])
end

function GetRandomVariant()
  local maxNum = 0

  for key, val in pairs(variants) do
    maxNum = maxNum + variants[key]["chance"]
  end

  local rand = GetRandom(maxNum)
  local lowBound = 0
  local highBound = 0
  local var = nil

  for key, val in pairs(variants) do
    highBound = lowBound + variants[key]["chance"]
    if rand <= highBound then
      var = key
      break
    end
    lowBound = highBound
  end

  return var
end

function addRandomVariantToTeam(team)
  if counter[team] == nil then
    counter[team] = {}
  end

  while true do
    local var = GetRandomVariant()
    if counter[team][var] == nil and variants[var]["hogLimit"] > 0 then
      counter[team][var] = 1
      break
    elseif counter[team][var] ~= nil and counter[team][var] < variants[var]["hogLimit"] then
      counter[team][var] = counter[team][var] +1
      break
    end
  end

  return var
end

function setTeamHogs(team)
  local maxHog = hogCount[team]

  group[team] = {}
  counter[team] = {}

  if mode == 'king' then
    maxHog = maxHog -1
  end

  for i=1,maxHog do
    table.insert(group[team], group['all'][i])
  end

  if mode == 'king' then
    counter[team]['King'] = 1
    table.insert(group[team], 'King')
  end
end

function countTeamHogs(hog)
  local team = GetHogTeamName(hog)

  if hogCount[team] == nil then
    hogCount[team] = 1
    teamHogs[team] = {}
  else
    hogCount[team] = hogCount[team] +1
  end

  teamHogs[team][hogCount[team]] = hog

  teamNames[team] = 1
end

function setHogVariant(hog)
  local team = getHogInfo(hog, 'team')

  if teamIndex[team] == nil then
    teamIndex[team] = 1
  else
    teamIndex[team] = teamIndex[team] +1
  end

  local hogNum = teamIndex[team]
  local hogVar = group[team][hogNum]

  hogInfo[hog]['variant'] = hogVar
  SetHealth(hog, variants[hogVar]["hp"])
end

function getHogInfo(hog, info)
  if hog == nil then
    WriteLnToChat("ERROR [getHogInfo]: hog is nil!")
    WriteLnToConsole("ERROR [getHogInfo]: hog is nil!")
    return
  end

  if hogInfo[hog] == nil then
    return nil
  end

  return hogInfo[hog][info]
end

function setHogInfo(hog)
  if hog == nil then
    WriteLnToChat("ERROR [setHogInfo]: hog is nil!")
    WriteLnToConsole("ERROR [setHogInfo]: hog is nil!")
    return
  end

  hogInfo[hog] = {}
  hogInfo[hog]['maxHp'] = GetHealth(hog)
  hogInfo[hog]['name'] = GetHogName(hog)
  hogInfo[hog]['hat'] = GetHogHat(hog)
  hogInfo[hog]['team'] = GetHogTeamName(hog)
  hogInfo[hog]['clan'] = GetHogClan(hog)
  hogInfo[hog]['clanColor'] = GetClanColor(hogInfo[hog]['clan'])
end

--[[
  ##############################################################################
  ### CRATE SPAWN AND PICKUP FUNCTIONS                                       ###
  ##############################################################################
]]--

--[[
 : Heals either 10 (95% chance) or 15 (5% chance) hitpoints
 : Plus 10% of the hogs base hitpoints. 
 :
 : Has a 7% chance to be empty.
]]--
function onHealthCratePickup()
  local factor = 2
  local msgColor = getHogInfo(CurHog, 'clanColor')
  local healHp = 0
  PlaySound(sndShotgunReload)

  if GetRandom(100) < emptyCrateChance then
    AddCaption(loc("It's empty!"), msgColor, capgrpMessage)
    return
  elseif GetRandom(100) < bonusCrateChance then
    factor = 3
  end

  local var = getHogInfo(CurHog, 'variant')
  local hogHealth = GetHealth(CurHog)
  healHp = 5 * factor

  -- Add extra 10% of hogs base hp to heal
  healHp = healHp + div(getHogInfo(CurHog, 'maxHp'), 10)

  HealHog(CurHog, healHp)

  SetEffect(CurHog, hePoisoned, 0)
end

--[[
 : Adds either 1 (95% chance) or 2 (5% chance) random weapon(s) based on the hog variant.
 :
 : Has a 7% chance to be empty.
]]--
function onWeaponCratePickup(crate)
  local factor = 1 * strength
  local msgColor = GetClanColor(GetHogClan(CurHog))
  PlaySound(sndShotgunReload)

  if GetRandom(100) < emptyCrateChance then
    if IsHogLocal(CurHog) then
      AddCaption(loc("It's empty!"), msgColor, capgrpMessage)
    end
    return
  elseif GetRandom(100) < bonusCrateChance then
    factor = 2 * strength
  end

  local randIndex
  local randAmmo

  if mode ~= 'points' then
    local var = getHogInfo(CurHog, 'variant')
    randIndex = GetRandom(table.getn(variants[var]["weapons"])) +1
    randAmmo = variants[var]["weapons"][randIndex]
  else
    local possibleWeapons = {}

    for key, val in pairs(pointsWeaponVal) do
      if val > 2 and val < 8 then
        table.insert(possibleWeapons, key)
      end
    end

    randIndex = GetRandom(table.getn(possibleWeapons)) +1
    randAmmo = possibleWeapons[randIndex]
  end

  AddAmmo(CurHog, randAmmo, GetAmmoCount(CurHog, randAmmo) +factor)
  if IsHogLocal(CurHog) then
    AddCaption(string.format(loc("%s (+%d)"), GetAmmoName(randAmmo), factor), msgColor, capgrpMessage)
    local effect = AddVisualGear(GetX(crate), GetY(crate) +cratePickupGap, vgtAmmo, 0, true)
    SetVisualGearValues(effect, nil, nil, nil, nil, nil, randAmmo, nil, nil, nil, msgColor)
  end
end
--[[
 : Adds either 1 (95% chance) or 2 (5% chance) random helper(s) based on the hog variant.
 :
 : Has a 7% chance to be empty.
]]--
function onUtilityCratePickup(crate)
  local factor = 1 * strength
  local msgColor = GetClanColor(GetHogClan(CurHog))
  PlaySound(sndShotgunReload)

  if GetRandom(100) < emptyCrateChance then
    if IsHogLocal(CurHog) then
      AddCaption(loc("It's empty!"), msgColor, capgrpMessage)
    end
    return
  elseif GetRandom(100) < bonusCrateChance then
    factor = 2 * strength
  end

  local randIndex
  local randUtility

  if mode ~= 'points' then
    randIndex = GetRandom(table.getn(utilities)) +1
    randUtility = utilities[randIndex]
  else
    local possibleHelpers = {}

    for key, val in pairs(pointsHelperVal) do
      table.insert(possibleHelpers, key)
    end

    randIndex = GetRandom(table.getn(possibleHelpers)) +1
    randUtility = possibleHelpers[randIndex]
  end
  
  AddAmmo(CurHog, randUtility, GetAmmoCount(CurHog, randUtility) +factor)
  if IsHogLocal(CurHog) then
    AddCaption(string.format(loc("%s (+%d)"), GetAmmoName(randUtility), factor), msgColor, capgrpMessage)
    local effect = AddVisualGear(GetX(crate), GetY(crate) +cratePickupGap, vgtAmmo, 0, true)
    SetVisualGearValues(effect, nil, nil, nil, nil, nil, randUtility, nil, nil, nil, msgColor)
  end
end

function onPickupCrate(crate)
  local pos = GetGearPos(crate)

  -- Check if the crate is fake
  if pos % posCaseDummy >= 1 then
    if pos % posCaseDummy == posCaseAmmo then
      onWeaponCratePickup(crate)
    elseif pos % posCaseDummy == posCaseHealth then
      onHealthCratePickup()
    elseif pos % posCaseDummy == posCaseUtility then
      onUtilityCratePickup(crate)
    end
  end
end

function RandomTurnEvents()
  if GetRandom(100) < weaponCrateChance then
    SpawnFakeAmmoCrate(0, 0, false, false)
    return 5000
  elseif GetRandom(100) < utilCrateChance then
    SpawnFakeUtilityCrate(0, 0, false, false)
    return 5000
  elseif GetRandom(100) < healthCrateChance then
    SpawnFakeHealthCrate(0, 0, false, false)
    return 5000
  end
  return 0
end

--[[
  ##############################################################################
  ### SUDDEN DEATH FUNCTIONS                                                 ###
  ##############################################################################
]]--

function onSuddenDeathDamage(hog)
  local hp = GetHealth(hog)
  local maxHp = getHogInfo(hog, 'maxHp')
  local newHp = 0
  local hpDec = 0
  local hpPer = div(hp * 100, maxHp)

  if hp > 1 then
    local msgColor = GetClanColor(GetHogClan(hog))
    if hpPer <= 25 then
      newHp = hp -2
    elseif hpPer <= 50 then
      newHp = hp -3
    elseif hpPer <= 75 then
      newHp = hp -4
    elseif hpPer <= 100 then
      newHp = hp -5
    elseif hpPer <= 125 then
      newHp = hp -6
    elseif hpPer <= 150 then
      newHp = hp -7
    else
      newHp = div(hp * 93, 100)
    end

    if newHp <= 0 then
      newHp = 1
    end

    hpDec = hp - newHp

    SetHealth(hog, newHp)
    local effect = AddVisualGear(GetX(hog), GetY(hog) +cratePickupGap, vgtHealthTag, hpDec, false)
    SetVisualGearValues(effect, nil, nil, nil, nil, nil, nil, nil, nil, nil, msgColor)
  end
end

function onSuddenDeathTurn()
  runOnGears(onSuddenDeathDamage)
end

function onSuddenDeath()
  suddenDeath = true

  healthCrateChance = healthCrateChanceSD
  utilCrateChance = utilCrateChanceSD
  weaponCrateChance = weaponCrateChanceSD

  if mode == 'highland' then
    highEnemyKillHPBonus = highEnemyKillHPBonus +5
    highFriendlyKillHPBonus = highFriendlyKillHPBonus +10
  end

  if mode ~= 'points' then
    for key, val in pairs(variants) do
      if not variants[key]["special"] then
        variants[key]["weaponLimit"] = variants[key]["weaponLimit"] +1
      end
    end
  end

  if mode ~= 'points' then
    for hog, val in pairs(hogInfo) do
      hogInfo[hog]['weapons'] = {}
      hogInfo[hog]['helpers'] = {}
    end
    
    runOnGears(setupHogTurn)
  end
end

--[[
  ##############################################################################
  ### GEAR TRACKING FUNCTIONS                                                ###
  ##############################################################################
]]--

function onGearAdd(gear)
  local gearType = GetGearType(gear)
  
  if gearType == gtHedgehog then
    trackGear(gear)
  elseif gearType == gtRCPlane then
    SetHealth(gear, 2)
  elseif gearType == gtAirBomb then
    -- Set Boom
    SetGearValues(gear, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 20)
  elseif gearType == gtCake then
    -- Set Boom
    SetGearValues(gear, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 50)
  elseif gearType == gtDEagleShot then
    -- Set Boom
    SetGearValues(gear, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 12)
  end
end

function onHighlandKill(gear)
  local deathVar = getHogInfo(gear, 'variant')
  local killVar = getHogInfo(CurHog, 'variant')
  local bonAmmo = {}
  local deathMaxHP = getHogInfo(gear, 'maxHp')
  local curHP = GetHealth(CurHog)
  local newHP = 0
  local hpDiff = 0
  local addAmmo = false

  -- Killer hog is dead! Don't do anything
  if curHP == nil or curHP <= 0 then
    return
  end

  -- Killer and victim is equal! Don't do anything
  if CurHog == gear then
    return
  end

  -- Hog drowned because of water, not enemy
  if LastWaterLine ~= WaterLine then
    return
  end

  -- Enemy kill! Add weapons to pool and to hog
  if getHogInfo(gear, 'clan') ~= getHogInfo(CurHog, 'clan') then

    -- Initialize weapons if required
    if highWeapons[CurHog] == nil then
      highWeapons[CurHog] = {}
    end

    if highHelpers[CurHog] == nil then
      highHelpers[CurHog] = {}
    end

    -- If not a special hog, use the victims weapons
    if variants[deathVar]['special'] == false then
      bonAmmo = variants[deathVar]['weapons']

      if suddenDeath == true then
        ammoCount = highPickupSDCount
      else
        ammoCount = highPickupCount
      end

      -- Check if hog already got bonus weapons
      if table.getn(highWeapons[CurHog]) == 0 and highHasBonusWeps == false then
        highHasBonusWeps = true
        addAmmo = true
      end

      -- Pass turn bonus weapons to hog pool
      for key, val in pairs(bonAmmo) do
        local idx = table.getn(highWeapons[CurHog]) +1
        highWeapons[CurHog][idx] = val
      end
    -- It's a special hog, use special pool
    else
      bonAmmo = highSpecialBonus

      ammoCount = 1

      -- Check if hog already got bonus helpers
      if table.getn(highWeapons[CurHog]) == 0 and highHasBonusHelp == false then
        highHasBonusHelp = true
        addAmmo = true
      end

      -- Pass turn bonus weapons to hog pool
      for key, val in pairs(highSpecialPool) do
        local idx = table.getn(highHelpers[CurHog]) +1
        highHelpers[CurHog][idx] = val
      end
    end

    if addAmmo then
      local i = 1
      while i <= ammoCount and #bonAmmo > 0 do
        local randAmmo = GetRandom(#bonAmmo) +1
        local randAmmoType = bonAmmo[randAmmo]

        -- Remove the randomized weapon so it cannot be picked up twice
        table.remove(bonAmmo, randAmmo)

        AddAmmo(CurHog, randAmmoType, GetAmmoCount(CurHog, randAmmoType) +1)

        if IsHogLocal(CurHog) then
           local effect = AddVisualGear(GetX(CurHog), GetY(CurHog) + (cratePickupGap * i), vgtAmmo, 0, true)
           SetVisualGearValues(effect, nil, nil, nil, nil, nil, randAmmoType, nil, nil, nil, nil)
        end

        i = i +1
      end
    end

    hpDiff = div(deathMaxHP * highEnemyKillHPBonus, 100)
    newHP = curHP + hpDiff
    HealHog(CurHog, newHP)
  -- Friendly fire! Punish hog by removing weapons and helpers from pool
  -- and reduce health
  else
    highWeapons[CurHog] = {}
    highHelpers[CurHog] = {}

    hpDiff = div(deathMaxHP * highFriendlyKillHPBonus, 100)
    newHP = curHP - hpDiff
    if newHP > 0 then
      SetHealth(CurHog, newHP)
    else
      SetHealth(CurHog, 0)
    end

    local effect = AddVisualGear(GetX(CurHog), GetY(CurHog) - cratePickupGap, vgtHealthTag, hpDiff, false)
    -- Set Tint
    SetVisualGearValues(effect, nil, nil, nil, nil, nil, nil, nil, nil, nil, GetClanColor(GetHogClan(CurHog)))
  end
end

function onKingDeath(KingHog)
  local team = getHogInfo(KingHog, 'team')
  local msgColor = getHogInfo(KingHog, 'clanColor')

  AddCaption(string.format(loc("The king of %s has died!"), team), 0xFFFFFFFF, capgrpGameState)

  -- Kill the rest of the team normally, just like the official King Mode game modifier
  for hog, val in pairs(hogInfo) do
    if getHogInfo(hog, 'team') == team then
      hp = GetHealth(hog)
      if hp ~= nil and hp > 0 then
        SetState(KingHog, gstHHDeath)
        SetHealth(hog, 0)
        SetGearValues(hog, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 0)
      end
    end
  end

  -- We don't use DismissTeam, it causes a lot of problems and nasty side-effects.

end

function onPointsKill(gear)
  local deathVar = getHogInfo(gear, 'variant')
  local killVar = getHogInfo(CurHog, 'variant')
  local deathClan = getHogInfo(gear, 'clan')
  local killClan = getHogInfo(CurHog, 'clan')
  local team = getHogInfo(CurHog, 'team')

  local curHP = GetHealth(CurHog)

  -- Killer hog is dead! Don't do anything
  if curHP == nil or curHP <= 0 then
    return
  end

  -- Hog drowned because of water, not enemy
  if LastWaterLine ~= WaterLine then
    return
  end

  -- Same clan, friendly kill, skip
  if killClan == deathClan then
    return
  end

  pointsPerTeam[team]['weapons'] = pointsPerTeam[team]['weapons'] + 2
  pointsPerTeam[team]['helpers'] = pointsPerTeam[team]['helpers'] + 1
  updatePointsLabel(team)

  local effect = AddVisualGear(GetX(CurHog) - (cratePickupGap / 2), GetY(CurHog), vgtHealthTag, 2, false)
  -- Set Tint
  SetVisualGearValues(effect, nil, nil, nil, nil, nil, nil, nil, nil, nil, 0xFFFFFFFF)

  local effect = AddVisualGear(GetX(CurHog) + (cratePickupGap / 2), GetY(CurHog), vgtHealthTag, 1, false)
  -- Set Tint
  SetVisualGearValues(effect, nil, nil, nil, nil, nil, nil, nil, nil, nil, 0x444444FF)
end

function onGearDelete(gear)
  trackDeletion(gear)

  if GetGearType(gear) == gtCase and band(GetGearMessage(gear), gmDestroy) ~= 0 then
    onPickupCrate(gear)
  end

  if GetGearType(gear) == gtHedgehog then
    if mode ~= 'points' then
      hogInfo[gear]['weapons'] = {}
      hogInfo[gear]['helpers'] = {}
    end

    -- If dying gear is a hog and mode is highland, check for kills
    if mode == 'highland' then
      onHighlandKill(gear)
    -- If current hog is dying and we are on points mode, we need to save the unused weapons/helpers
    elseif mode == 'points' and CurHog == gear then
      savePoints(gear)
    elseif mode == 'points' and CurHog ~= gear then
      onPointsKill(gear)
    end

    if mode == 'king' and getHogInfo(gear, 'variant') == 'King' then
      onKingDeath(gear)
    end
  end
end

--[[
  ##############################################################################
  ### TURN BASED FUNCTIONS                                                   ###
  ##############################################################################
]]--

function calcKingHP(doEffects)
  if doEffects == nil then
     doEffects = true
  end
  local teamKings = {}
  local teamHealth = {}

  for hog, val in pairs(hogInfo) do
    local hp = GetHealth(hog)

    if hp ~= nil and hp > 0 then
      local team = getHogInfo(hog, 'team')

      if teamHealth[team] == nil then
        teamHealth[team] = 0
      end

      if getHogInfo(hog, 'variant') == 'King' then
        teamKings[team] = hog
      else
        teamHealth[team] = teamHealth[team] + hp
      end
    end
  end

  for team, hog in pairs(teamKings) do
    local hp = GetHealth(hog)
    local newHP = div(teamHealth[team] * kingLinkPerc, 100)

    -- Set hitpoints to 1 if no other hog is alive or only has 1 hitpoint
    if newHP <= 0 then
      newHP = 1
    end

    local diff = math.abs(newHP - hp)

    -- Change HP and do some nice effects
    if newHP ~= hp then
        if not doEffects then
            SetHealth(hog, newHP)
        else
            if newHP > hp then
                HealHog(hog, diff, false)
            elseif newHP < hp then
                SetHealth(hog, newHP)
                if doEffects then
                    local effect = AddVisualGear(GetX(hog), GetY(hog) - cratePickupGap, vgtHealthTag, diff, false)
                    -- Set Tint
                    SetVisualGearValues(effect, nil, nil, nil, nil, nil, nil, nil, nil, nil, GetClanColor(GetHogClan(hog)))
                end
            end
        end
    end
  end
end

function setupHogTurn(hog)
  clearHogAmmo(hog)
  addTurnAmmo(hog)
end

function onEndTurn()
  if not firstTurnOver then
    firstTurnOver = true
  end
  local anyHog = nil
  for team, val in pairs(teamNames) do
    -- Count amount of alive hogs in team
    local c = 0
    for idx, hog in pairs(teamHogs[team]) do
      if GetHealth(hog) ~= nil then
        anyHog = hog
        c = c + 1
      end
    end

    -- Only one hog left, unfreeze the hog
    if c == 1 then
      if GetHealth(anyHog) ~= nil then
        SetEffect(anyHog, heFrozen, 0)
      end
    end
  end

  -- When we are on points mode count remaining weapon/helper points
  if mode == 'points' and GetHealth(CurHog) ~= nil then
    savePoints(CurHog)
  end

  -- Run random turn events
  RandomTurnEvents()
end

function savePoints(hog)
  local team = getHogInfo(hog, 'team')
  local hogWepPoints = 0
  local hogHlpPoints = 0

  for ammoType=0, AmmoTypeMax do
    local ammoCount = GetAmmoCount(hog, ammoType)

    if pointsWeaponVal[ammoType] ~= nil then
      hogWepPoints = hogWepPoints + (pointsWeaponVal[ammoType] * ammoCount)
    elseif pointsHelperVal[ammoType] ~= nil then
      hogHlpPoints = hogHlpPoints + (pointsHelperVal[ammoType] * ammoCount)
    end
  end

  local wepWoTax = pointsPerTeam[team]['weaponsFix']
  local hlpWoTax = pointsPerTeam[team]['helpersFix']
  local wepToTax = 0
  local hlpToTax = 0

  if hogWepPoints <= wepWoTax then
    wepWoTax = hogWepPoints
  else
    wepToTax = hogWepPoints - wepWoTax
  end

  if hogHlpPoints <= hlpWoTax then
    hlpWoTax = hogHlpPoints
  else
    hlpToTax = hogHlpPoints - hlpWoTax
  end

  if suddenDeath == false then
    pointsPerTeam[team]['weapons'] = pointsPerTeam[team]['weaponsRem'] + wepWoTax + div(wepToTax * pointsKeepPerc, 100)
    pointsPerTeam[team]['helpers'] = pointsPerTeam[team]['helpersRem'] + hlpWoTax + div(hlpToTax * pointsKeepPerc, 100)
  else
    pointsPerTeam[team]['weapons'] = pointsPerTeam[team]['weaponsRem'] + wepWoTax + div(wepToTax * pointsKeepSDPerc, 100)
    pointsPerTeam[team]['helpers'] = pointsPerTeam[team]['helpersRem'] + hlpWoTax + div(hlpToTax * pointsKeepSDPerc, 100)
  end

  updatePointsLabel(team)

  local effect = AddVisualGear(GetX(hog) - (cratePickupGap / 2), GetY(hog), vgtHealthTag, pointsPerTeam[team]['weapons'], false)
  -- Set Tint
  SetVisualGearValues(effect, nil, nil, nil, nil, nil, nil, nil, nil, nil, 0xFFFFFFFF)

  local effect = AddVisualGear(GetX(hog) + (cratePickupGap / 2), GetY(hog), vgtHealthTag, pointsPerTeam[team]['helpers'], false)
  -- Set Tint
  SetVisualGearValues(effect, nil, nil, nil, nil, nil, nil, nil, nil, nil, 0x444444FF)
end

function onPointsTurn()
  local hogWepPoints = 0
  local hogHlpPoints = 0

  if suddenDeath == false then
    pointsPerTeam[LastTeam]['weapons'] = pointsPerTeam[LastTeam]['weapons'] + pointsWepTurn
    pointsPerTeam[LastTeam]['helpers'] = pointsPerTeam[LastTeam]['helpers'] + pointsHlpTurn

    if pointsPerTeam[LastTeam]['weapons'] > pointsWepMax then
      pointsPerTeam[LastTeam]['weapons'] = pointsWepMax
    end

    if pointsPerTeam[LastTeam]['helpers'] > pointsHlpMax then
      pointsPerTeam[LastTeam]['helpers'] = pointsHlpMax
    end
  else
    pointsPerTeam[LastTeam]['weapons'] = pointsPerTeam[LastTeam]['weapons'] + pointsWepSDTurn
    pointsPerTeam[LastTeam]['helpers'] = pointsPerTeam[LastTeam]['helpers'] + pointsHlpSDTurn

    if pointsPerTeam[LastTeam]['weapons'] > pointsWepSDMax then
      pointsPerTeam[LastTeam]['weapons'] = pointsWepSDMax
    end

    if pointsPerTeam[LastTeam]['helpers'] > pointsHlpSDMax then
      pointsPerTeam[LastTeam]['helpers'] = pointsHlpSDMax
    end
  end

  -- Take the first alive hog from LastTeam and setup new weapons and helpers
  -- Since the weapons and helpers are shared the whole team, this is sufficent
  for idx, teamHog in pairs(teamHogs[LastTeam]) do
    if GetHealth(teamHog) ~= nil then
      clearHogAmmo(teamHog)
      addTurnAmmo(teamHog)
      break
    end
  end
end

function onNewTurn()
  LastHog = CurHog
  LastTeam = CurTeam
  CurHog = CurrentHedgehog
  CurTeam = getHogInfo(CurHog, 'team')

  if suddenDeath == true then
    onSuddenDeathTurn()
  else
    local RoundsTillSD = (SuddenDeathTurns+2) - (TotalRounds+1)
    -- Show SD reminder every couple of turns, and in the first turn
    if (not firstTurnOver) or (RoundsTillSD <= 6) or (RoundsTillSD <= 25 and RoundsTillSD % 5 == 0) or (RoundsTillSD % 10 == 0) then
        AddCaption(string.format(loc("Rounds until Sudden Death: %d"), RoundsTillSD), 0xFFFFFFFF, capgrpGameState)
    end
  end

  -- Generate new weapons for last hog if it's still alive
  if LastHog ~= nil and LastHog ~= CurHog then
    if mode == 'points' then
      onPointsTurn()
    else
      hogInfo[LastHog]['weapons'] = {}
      hogInfo[LastHog]['helpers'] = {}
      setupHogTurn(LastHog)
    end
  end

  -- Recalculate the kings hp if required
  if mode == 'king' then
    calcKingHP()
  end

  if mode == 'highland' then
    highHasBonusWeps = false
    highHasBonusHelp = false
  end

  -- Set LastWaterLine to the current water line
  LastWaterLine = WaterLine
end

--[[
  ##############################################################################
  ### GAME START FUNCTIONS                                                   ###
  ##############################################################################
]]--

function onAmmoStoreInit()
  for val=0, AmmoTypeMax do
    SetAmmo(val, 0, 0, 0, 0)
  end
end

function onParameters()
  parseParams()

  if params['mode'] ~= nil then
    mode = params['mode']
    if mode == "default" or mode == "king" or mode == "points" or mode == "highland" then
       modeExplicit = true
    end
  end

  if params['mutatenames'] ~= nil then
    useVariantNames = params['mutatenames']
  end
  if params['mutate'] ~= nil then
    useVariantHats = params['mutate']
  end

  if params['strength'] ~= nil and tonumber(params['strength']) > 0 then
    strength = tonumber(params['strength'])
    -- Highland
    if mode == 'highland' then
      highPickupCount = highPickupCount * strength
      highPickupSDCount = highPickupSDCount * strength
      highHelperCount = highHelperCount * strength
      highHelperSDCount = highHelperSDCount * strength
    -- Points
    elseif mode == 'points' then
      pointsWepBase = pointsWepBase * strength
      pointsHlpBase = pointsHlpBase * strength
      pointsWepTurn = pointsWepTurn * strength
      pointsHlpTurn = pointsHlpTurn * strength
      pointsWepMax = pointsWepMax * strength
      pointsHlpMax = pointsHlpMax * strength
      pointsWepSDTurn = pointsWepSDTurn * strength
      pointsHlpSDTurn = pointsHlpSDTurn * strength
      pointsWepSDMax = pointsWepSDMax * strength
      pointsHlpSDMax = pointsHlpSDMax * strength
    -- Either king or normal mode, change variants
    else
      for name, data in pairs(variants) do
        variants[name]["weaponLimit"] = variants[name]["weaponLimit"] * strength
        variants[name]["helperLimit"] = variants[name]["helperLimit"] * strength
      end
    end
  end

  if params['luck'] ~= nil and tonumber(params['luck']) > 0 then
    luck = tonumber(params['luck'])

    healthCrateChance = div(healthCrateChance * luck, 100)
    utilCrateChance = div(utilCrateChance * luck, 100)
    weaponCrateChance = div(weaponCrateChance * luck, 100)

    healthCrateChanceSD = div(healthCrateChanceSD * luck, 100)
    utilCrateChanceSD = div(utilCrateChanceSD * luck, 100)
    weaponCrateChanceSD = div(weaponCrateChanceSD * luck, 100)

    emptyCrateChance = div(emptyCrateChance * 100, luck)
    bonusCrateChance = div(bonusCrateChance * luck, 100)
  end
end

function onGameStart()
  -- If we are not on points mode, we start randomizing everything
  if mode ~= 'points' then
    if GetGameFlag(gfBorder) or MapHasBorder() then
      variants["Air General"] = nil
      variants['Athlete'] = nil
    end

    if mode == 'king' then
      variants['King']['chance'] = 0
    end

    for i=1,8 do
      addRandomVariantToTeam("all")
    end

    -- Translate randomized team to a flat group
    group['all'] = {}
    for key, val in pairs(counter["all"]) do
      for i=1, counter["all"][key] do
        table.insert(group['all'], key)
      end
    end

    -- Shuffle group for more randomness
    shuffle(group['all'])
  -- We are in points mode, setup other weapons
  elseif mode == 'points' then
    -- Translate [ammo] -> points to [points] -> {ammo1, ammo2}
    for ammoType, ammoPoints in pairs(pointsWeaponVal) do
      if pointsToWep[ammoPoints] == nil then
        pointsToWep[ammoPoints] = {}
      end

      table.insert(pointsToWep[ammoPoints], ammoType)
    end

    for ammoType, ammoPoints in pairs(pointsHelperVal) do
      if pointsToHlp[ammoPoints] == nil then
        pointsToHlp[ammoPoints] = {}
      end

      table.insert(pointsToHlp[ammoPoints], ammoType)
    end

    for points, ammoList in pairs(pointsToWep) do
      table.insert(wepPoints, points)
    end

    for points, ammoList in pairs(pointsToHlp) do
      table.insert(hlpPoints, points)
    end

    table.sort(wepPoints)
    table.sort(hlpPoints)
  end

  -- Initial Hog Setup
  runOnGears(countTeamHogs)

  for key, val in pairs(teamNames) do
    if mode == 'points' then
      pointsPerTeam[key] = {}
      pointsPerTeam[key]['weapons'] = pointsWepBase
      pointsPerTeam[key]['helpers'] = pointsHlpBase
      updatePointsLabel(key)
    else
      setTeamHogs(key)
    end
  end

  runOnGears(setHogInfo)
  
  if mode ~= 'points' then
    runOnGears(setHogVariant)
    runOnGears(setupHogTurn)
    if useVariantNames ~= false and useVariantNames ~= 'false' then
      runOnGears(SetHogVariantName)
    end
    if useVariantHats ~= false and useVariantHats ~= 'false' then
        runOnGears(SetHogVariantHat)
    elseif mode == 'king' then
        -- If variant hats are disabled but we're in King Mode,
        -- we still change *some* hats to make sure only kings
        -- wear crows. Otherwise, you don't know who's the king!
        runOnGears(SetHogVariantHatKingMode)
    end
  end

  if mode == 'points' then
    for key, val in pairs(teamNames) do
      clearHogAmmo(teamHogs[key][1])
      addTurnAmmo(teamHogs[key][1])
    end
  end

  if mode == 'king' then
    calcKingHP(false)
  end

  local txt = ''
  local icon = 0

  if mode ~= 'points' then
    txt = txt .. loc("Variants: Hogs will be randomized from 12 different variants") .. "|"
    txt = txt .. loc("Weapons: Hogs will get 1 out of 3 weapons randomly each turn") .. "|"
    txt = txt .. loc("Helpers: Hogs will get 1 out of 2 helpers randomly each turn") .. "|"
    txt = txt .. loc("Crates: Crates drop randomly with chance of being empty") .. "|"
    txt = txt .. loc("Last Resort: Having less than 25% base health gives kamikaze") .. "|"
    txt = txt .. loc("Modifiers: Unlimited attacks, per-hog ammo") .. "|"
  else
    txt = txt .. loc("Crates: Crates drop randomly and may be empty") .. "|"
    txt = txt .. loc("Modifiers: Unlimited attacks, shared clan ammo") .. "|"
  end

  if luck ~= 100 then
    txt = txt .. string.format(loc("Luck: %d%% (modifier for crates)"), luck) .. "|"
  end

  if strength > 1 then
    txt = txt .. string.format(loc("Strength: %d (multiplier for ammo)"), strength) .. "|"
  end

  if mode == 'highland' then
    txt = txt .. " |"
    txt = txt .. loc("--- Highland Mode ---").."|"
    txt = txt .. string.format(loc("Enemy kills: Collect victim's weapons and +%d%% of its base health"), highEnemyKillHPBonus).."|"
    txt = txt .. string.format(loc("Friendly kills: Clear killer's pool and -%d%% of its base health"), highFriendlyKillHPBonus).."|"
    txt = txt .. string.format(loc("Turns: Hogs get %d random weapon(s) from their pool"), highPickupCount).."|"
    txt = txt .. loc("Hint: Kills won't transfer a hog's pool to the killer's pool").."|"
    txt = txt .. loc("Specials: Kings and air generals drop helpers, not weapons").."|"
    icon = 1 -- Target
  elseif mode == 'king' then
    txt = txt .. " |"
    txt = txt .. loc("--- King Mode ---").."|"
    txt = txt .. loc("Protect the King: When the king dies, so does the team").."|"
    txt = txt .. string.format(loc("Turns: King's health is set to %d%% of the team health"), kingLinkPerc).."|"
    icon = 0 -- Golden Crown
  elseif mode == 'points' then
    txt = txt .. " |"
    txt = txt .. loc("--- Points Mode ---").."|"
    txt = txt .. loc("Variants: Kings and air generals are disabled").."|"
    txt = txt .. string.format(loc("Weapons: Each team starts with %d weapon points"), pointsWepBase).."|"
    txt = txt .. string.format(loc("Helpers: Each team starts with %d helper points"), pointsHlpBase).."|"
    txt = txt .. string.format(loc("Turns: Refill %d weapon and %d helper points|and randomize weapons and helpers based on team points"), pointsWepTurn, pointsHlpTurn).."|"
    icon = 4 -- Golden Star
  else
    icon = -amGrenade -- Grenade
  end

  txt = txt .. " |"
  txt = txt .. loc("--- Sudden Death ---").."|"
  txt = txt .. loc("Weapons: Nearly every hog variant gets 1 kamikaze").."|"
  txt = txt .. loc("Crates: Crates drop more often with a higher chance of bonus ammo").."|"
  txt = txt .. loc("Water: Rises by 37 per turn").."|"
  txt = txt .. loc("Health: Hogs lose up to 7% base health per turn").."|"

  -- Add hint if mode was not set in script parameter, or set incorrectly
  if not modeExplicit then
    txt = txt .. " |"
    txt = txt .. loc("--- Hint ---").."|"
    txt = txt .. loc("Modes: Activate “highland”, “king” or “points” mode by putting mode=<name>|into the script parameter").."|"
  end

  if mode == 'highland' then
    txt = txt .. string.format(loc("Highland: Hogs get %d random weapons from their pool"), highPickupSDCount) .. "|"
  end

  SetAmmoDescriptionAppendix(amRCPlane, loc("The RC plane only carries 2 weak bombs."))
  SetAmmoDescriptionAppendix(amAirAttack, loc("The air bombs are weaker than usual."))
  SetAmmoDescriptionAppendix(amCake, loc("The explosion is weaker than usual."))
  SetAmmoDescriptionAppendix(amDEagle, loc("Base damage has been modified to 12 per shot."))

  ShowMission(loc("Battalion"), loc("Less tools, more fun"), txt, icon, 0)
end

function onGameInit()
  --[[ CONFIGURATEABLE FOR PLAYERS ]]--
  --[[ ONCE IT HAS BEEN ADDED TO HW ]]--

  --[[ REQUIRED CONFIGURATIONS ]]--

  WaterRise = 37 -- Water rises by 37
  HealthDecrease = 0 -- No health decrease by game, script with 7%
  CaseFreq = 0 -- don't spawn crates

  -- Removed gfResetWeps to see weapons next turn
  EnableGameFlags(gfInfAttack)
  DisableGameFlags(gfResetWeps)

  if mode ~= 'points' then
    EnableGameFlags(gfPerHogAmmo)
  else
    DisableGameFlags(gfPerHogAmmo)
  end
end