--[[-----------------------------------------------------
-- CONSTRUCTION MODE --
---------------------------------------------------------
A Hedgewars gameplay mode by mikade.
Special thanks to all who helped test and offered suggestions.
Additional thanks to sheepluva/nemo for adding some extra hooks.
---------------------
-- STRUCTURES LIST --
---------------------
* Healing Station: Heals hogs to 150 health
* Teleportation Node: Allows teleporting to any other teleporter nodes
* Bio-filter: Explodes enemy hogs
* Respawner: If you have one of these, any slain hogs are resurrected here
* Generator: Generates power (used to buy stuff)
* Support Station: Allows purchasing crates
* Construction Station: Allows purchasing girders, rubber, mines, sticky mines, barrels
* Reflector Shield: Reflect projectiles
* Weapon Filter: Removes all equipement of enemy hogs passing through this area
---------------------------------------------------------
-- SCRIPT PARAMETER
---------------------------------------------------------
The script parameter can be used to configure the energy
of the game. It is a comma-seperated list of key=value pairs, where each
key is a word and each value is an integer between 0 and 4294967295.
Possible keys:
* initialenergy: Amount of energy that each team starts with (default: 550)
Note: Must be smaller than or equal to maxenergy
* energyperround: Amount of energy that each team gets per round (default: 50)
* maxenergy: Maximum amount of energy each team can hold (default: 1000)
* cratesperround: Maximum number of crates you can place per round (default: 5)
For the previous 2 keys, you can use the value “inf” for an unlimited amount.
Example: “initialenergy=750, maxenergy=2000” starts thee game with 750 energy
and sets the maximum energy to 2000.
Example: “craterperround=inf” disables the crate placement limit.
---------------------------------------------------------
-- Ideas list --
---------------------------------------------------------
* To make the weapon filter more attractive, make it vaporize flying saucers
and also rope, and maybe incoming gears
* Make healing thing also cure poison?
* Maybe make poison more virulent and dangerous
]]
HedgewarsScriptLoad("/Scripts/Locale.lua")
HedgewarsScriptLoad("/Scripts/Tracker.lua")
HedgewarsScriptLoad("/Scripts/Params.lua")
-- Structures stuff
local strucID = {}
local strucGear = {}
local strucClan = {}
local strucType = {}
local strucCost = {}
local strucHealth = {}
local strucCirc = {}
local strucCircCol = {}
local strucCircRadius = {}
local strucAltDisplay = {}
-- Clan stuff
local clanPower = {} -- current power for each clan. Used to build stuff
local clanPowerTag = nil -- visual gear ID of displayed clan power
local clanUsedExtraTime = {} -- has used extra time in this round?
local clanCratesSpawned = {} -- number of crates spawned in this round
local clanFirstTurn = {}
local clanBoundsSX = {}
local clanBoundsSY = {}
local clanBoundsEX = {}
local clanBoundsEY = {}
-- For tracking previous mode selection per-team
local teamLStructIndex = {}
local teamLObjectMode = {}
local teamLCrateMode = {}
local teamLMineIndex = {}
local teamLWeapIndex = {}
local teamLUtilIndex = {}
-- Wall stuff
local wallsVisible = false
local wX = {}
local wY = {}
local wWidth = {}
local wHeight = {}
local wCol = {}
local wMargin = 20
local borderEffectTimer = 0 -- timer for border clan sparkles
-- Other stuff
local placedExpense = 0 -- Cost of current selected thing
local curWep = amNothing -- current weapon, used to reduce # of calls to GetCurAmmoType()
local fortMode = false -- is using a fort map?
local tempID_CheckProximity = nil -- temporary structure variable for CheckProximity
local cGear = nil -- detects placement of girders and objects (using airattack)
local uniqueStructureID = 0 -- Counter and ID for structures. Is incremented each time a structure spawns
-- Colors
local colorSupportStation = 0xFFFF00FF
local colorConstructionStation = 0xFFFFFFFF
local colorTeleportationNode = 0x0000FFFF
local colorHealingStation = 0xFF808040 -- Just a slight glow
local colorBioFilter = 0xFF0000FF
local colorReflectorShield = 0xFFAE00FF
local colorWeaponFilter = 0xA800FFFF
local colorHealingStationParticle = 0x00FF0080
local colorGeneratorParticle = 0xFFFF00FF
local colorMessageError = 0xFFFFFFFF
-- Fake ammo types, for the overwritten weapons in Construction Mode
local amCMStructurePlacer = amAirAttack
local amCMCratePlacer = amMineStrike
local amCMObjectPlacer = amNapalm
-- Config variables (script parameter)
-- NOTE: If you change this, also change the default game scheme “Construction Mode”
local conf_initialEnergy = 550
local conf_energyPerRound = 50
local conf_maxEnergy = 1000
local conf_cratesPerRound = 5
-----------------------
-- CRATE DEFINITIONS --
-----------------------
-- format:
-- { ammoType, cost }
local costFactor = 20
-- WEAPON CRATES
-- Weapons which shouldn't be aded:
-- Air attack, napalm, drillstrike: Overwritten weapons for the Construction Mode tools
-- Mine strike: Is currently broken
-- Piano strike: Hog is resurrected by respawner. Not strictly prohibited, however.
local atkArray = {
{amBazooka, 2*costFactor},
--{amBee, 4*costFactor},
{amMortar, 1*costFactor},
{amDrill, 3*costFactor},
{amSnowball, 3*costFactor},
{amDuck, 2*costFactor},
{amGrenade, 2*costFactor},
{amClusterBomb, 3*costFactor},
{amWatermelon, 25*costFactor},
{amHellishBomb, 25*costFactor},
{amMolotov, 3*costFactor},
{amGasBomb, 3*costFactor},
{amShotgun, 2*costFactor},
{amDEagle, 2*costFactor},
{amSniperRifle, 3*costFactor},
--{amSineGun, 6*costFactor},
{amFlamethrower, 4*costFactor},
{amIceGun, 15*costFactor},
{amMinigun, 13*costFactor},
{amFirePunch, 3*costFactor},
{amWhip, 1*costFactor},
{amBaseballBat, 7*costFactor},
--{amKamikaze, 1*costFactor},
{amSeduction, 1*costFactor},
{amHammer, 1*costFactor},
{amMine, 1*costFactor},
{amDynamite, 9*costFactor},
{amCake, 25*costFactor},
{amBallgun, 40*costFactor},
--{amRCPlane, 25*costFactor},
{amSMine, 5*costFactor},
--{amPiano, 40*costFactor},
{amPickHammer, 2*costFactor},
{amBlowTorch, 4*costFactor},
{amKnife, 2*costFactor},
{amBirdy, 7*costFactor},
}
-- UTILITY CRATES --
-- Utilities which shouldn't be added:
-- * Teleport: We have teleportation node
-- * Switch: Infinite in default Construction Mode weapons scheme
-- * Girder, rubber: Requires construction station
-- * Resurrector: We have the resurrector structure for this
-- Utilities which might be weird for this mode:
-- * Tardis: Randomly teleports hog, maybe even into enemy clan's area
local utilArray = {
{amLandGun, 5*costFactor},
{amRope, 7*costFactor},
{amParachute, 2*costFactor},
{amJetpack, 8*costFactor},
{amPortalGun, 15*costFactor},
{amInvulnerable, 5*costFactor},
{amLaserSight, 2*costFactor},
{amVampiric, 6*costFactor},
{amLowGravity, 4*costFactor},
{amExtraDamage, 6*costFactor},
{amExtraTime, 8*costFactor}
}
----------------------------
-- Placement stuff
----------------------------
-- primary placement categories
local cIndex = 1 -- category index
local cat = {
loc_noop("Girder Placement Mode"),
loc_noop("Rubber Placement Mode"),
loc_noop("Mine Placement Mode"),
loc_noop("Sticky Mine Placement Mode"),
loc_noop("Barrel Placement Mode"),
loc_noop("Weapon Crate Placement Mode"),
loc_noop("Utility Crate Placement Mode"),
loc_noop("Health Crate Placement Mode"),
loc_noop("Structure Placement Mode"),
}
-- Frames in sprTarget for the structure images
local catFrames = {
["Respawner"] = 1,
["Generator"] = 2,
["Healing Station"] = 3,
["Support Station"] = 4,
["Weapon Filter"] = 5,
["Teleportation Node"] = 6,
["Bio-Filter"] = 7,
["Construction Station"] = 8,
["Reflector Shield"] = 9,
}
local catReverse = {}
for c=1, #cat do
catReverse[cat[c]] = c
end
-- Track girders in proximity of CurrentHedgehog
local sProx = {
["Girder Placement Mode"] = false,
["Rubber Placement Mode"] = false,
["Mine Placement Mode"] = false,
["Sticky Mine Placement Mode"] = false,
["Barrel Placement Mode"] = false,
["Weapon Crate Placement Mode"] = false,
["Utility Crate Placement Mode"] = false,
["Health Crate Placement Mode"] = false,
["Structure Placement Mode"] = false,
["Teleportation Mode"] = false,
}
local pMode = {} -- pMode contains custom subsets of the main categories
local pIndex = 1
local currentGirderRotation = 1 -- current girder rotation, we actually need this as HW remembers what rotation you last used
-- Returns true if ammoType is an ammo type with a special meaning
-- in Construction Mode.
function IsConstructionModeAmmo(ammoType)
return ammoType == amCMStructurePlacer or
ammoType == amCMObjectPlacer or
ammoType == amCMCratePlacer or
ammoType == amGirder or
ammoType == amRubber or
ammoType == amTeleport
end
function RenderClanPower()
for i=0, TeamsCount-1 do
local name = GetTeamName(i)
SetTeamLabel(name, clanPower[GetTeamClan(name)])
end
DrawClanPowerTag()
end
function DrawClanPowerTag()
local zoomL = 1.1
local xOffset = 45
local yOffset = 70
local tValue = clanPower[GetHogClan(CurrentHedgehog)]
local tCol = GetClanColor(GetHogClan(CurrentHedgehog))
DeleteVisualGear(clanPowerTag)
clanPowerTag = AddVisualGear(-div(ScreenWidth, 2) + xOffset, ScreenHeight - yOffset, vgtHealthTag, tValue, false)
SetVisualGearValues(
clanPowerTag, -- id
nil, -- x offset (set above)
nil, -- y offset (set above)
0, -- dx
0, -- dy
zoomL, -- zoom
1, -- ~= 0 means align to screen
nil, -- frameticks
nil, -- value (set above)
240000, -- timer
tCol -- color
)
end
function DeleteClanPowerTag()
DeleteVisualGear(clanPowerTag)
clanPowerTag = nil
end
function XYisInRect(px, py, psx, psy, pex, pey)
if (px > psx) and (px < pex) and (py > psy) and (py < pey) then
return(true)
else
return(false)
end
end
function AddWall(zXMin, zYMin, zWidth, zHeight, zCol)
table.insert(wX, zXMin)
table.insert(wY, zYMin)
table.insert(wWidth, zWidth)
table.insert(wHeight, zHeight)
table.insert(wCol, zCol)
end
function BorderSpark(zXMin,zYMin, zWidth, zHeight, bCol)
local eX = zXMin + GetRandom(zWidth+10)
local eY = zYMin + GetRandom(zHeight+10)
local tempE = AddVisualGear(eX, eY, vgtDust, 0, false)
if tempE ~= 0 then
SetVisualGearValues(tempE, eX, eY, nil, nil, nil, nil, nil, 1, nil, bCol)
end
end
function HandleBorderEffects()
borderEffectTimer = borderEffectTimer + 1
if borderEffectTimer > 15 then
borderEffectTimer = 1
for i = 1, #wX do
BorderSpark(wX[i],wY[i],wWidth[i],wHeight[i], wCol[i])
end
end
end
----
-- old reflecting stuff from like 3 years ago lol
---
function gearCanBeDeflected(gear)
if (GetGearType(gear) == gtShell) or
(GetGearType(gear) == gtGrenade) or
(GetGearType(gear) == gtAirBomb) or
(GetGearType(gear) == gtClusterBomb) or
(GetGearType(gear) == gtCluster) or
(GetGearType(gear) == gtGasBomb) or
(GetGearType(gear) == gtMine) or
(GetGearType(gear) == gtMortar) or
(GetGearType(gear) == gtHellishBomb) or
(GetGearType(gear) == gtWatermelon) or
(GetGearType(gear) == gtMelonPiece) or
(GetGearType(gear) == gtEgg) or
(GetGearType(gear) == gtDrill) or
(GetGearType(gear) == gtBall) or
(GetGearType(gear) == gtExplosives) or
(GetGearType(gear) == gtFlame) or
(GetGearType(gear) == gtPortal) or
(GetGearType(gear) == gtDynamite) or
(GetGearType(gear) == gtSMine) or
(GetGearType(gear) == gtKnife) or
(GetGearType(gear) == gtJetpack) or
(GetGearType(gear) == gtBirdy) or
(GetGearType(gear) == gtSnowball) or
(GetGearType(gear) == gtMolotov)
then
return(true)
else
return(false)
end
end
function getThreatDamage(gear)
local dmg
--- damage amounts for weapons
if (GetGearType(gear) == gtGrenade) or
(GetGearType(gear) == gtClusterBomb) or
(GetGearType(gear) == gtGasBomb) or
(GetGearType(gear) == gtKnife) or
(GetGearType(gear) == gtEgg) or
(GetGearType(gear) == gtMolotov) or
(GetGearType(gear) == gtHellishBomb) or
(GetGearType(gear) == gtWatermelon) or
(GetGearType(gear) == gtSMine) then
dmg = 30
elseif (GetGearType(gear) == gtMelonPiece) then
dmg = 40
elseif (GetGearType(gear) == gtAirBomb) or
(GetGearType(gear) == gtDrill) or
(GetGearType(gear) == gtMine) or
(GetGearType(gear) == gtCluster) then
dmg = 20
elseif (GetGearType(gear) == gtFlame) or
(GetGearType(gear) == gtPortal) or
(GetGearType(gear) == gtDynamite) then
dmg = 0
elseif (GetGearType(gear) == gtBall) then
dmg = 1
else -- normal shell, snowball etc
dmg = 65
end
return(dmg)
end
function setGearReflectionValues(gear)
local dmg = getThreatDamage(gear)
setGearValue(gear,"damage",dmg)
setGearValue(gear,"deflects",0)
if (CurrentHedgehog ~= nil) then
setGearValue(gear,"owner",GetHogClan(CurrentHedgehog)) -- NEW NEEDS CHANGE?
else
setGearValue(gear,"owner",10)
end
end
function isATrackedGear(gear)
if (GetGearType(gear) == gtHedgehog) or
(GetGearType(gear) == gtTarget) or
(GetGearType(gear) == gtCase)
then
return(true)
else
return(false)
end
end
function AddStruc(pX,pY, pType, pClan)
uniqueStructureID = uniqueStructureID + 1
local tempG = AddGear(0, 0, gtTarget, 0, 0, 0, 0)
SetGearPosition(tempG, pX, pY)
setGearValue(tempG, "uniqueStructureID", uniqueStructureID)
local tempCirc = AddVisualGear(0,0,vgtCircle,0,true)
if pType ~= "Respawner" and pType ~= "Generator" then
SetVisualGearValues(tempCirc, 0, 0, 100, 255, 1, 100, 0, 500, 1, 0xFFFFFF00)
table.insert(strucCirc, tempCirc)
else
table.insert(strucCirc, false)
end
table.insert(strucID, uniqueStructureID)
table.insert(strucType, pType)
table.insert(strucGear,tempG)
table.insert(strucClan,pClan)
table.insert(strucCost,2)
local frameID = 0
local visualSprite = sprTarget
local madness = AddVisualGear(GetX(tempG), GetY(tempG), vgtStraightShot, 1, true,1)
if pType == "Reflector Shield" then
table.insert(strucHealth,255)
else
table.insert(strucHealth,1)
end
if pType == "Bio-Filter" then
table.insert(strucCircCol, colorBioFilter)
table.insert(strucCircRadius,1000)
elseif pType == "Healing Station" then
table.insert(strucCircCol, colorHealingStation)
table.insert(strucCircRadius,500)
elseif pType == "Respawner" then
table.insert(strucCircCol, 0)
table.insert(strucCircRadius,0)
runOnHogs(EnableHogResurrectionForThisClan)
elseif pType == "Teleportation Node" then
table.insert(strucCircCol, colorTeleportationNode)
table.insert(strucCircRadius,350)
elseif pType == "Generator" then
table.insert(strucCircCol, 0)
table.insert(strucCircRadius,0)
setGearValue(tempG, "power", 0)
elseif pType == "Support Station" then
table.insert(strucCircCol, colorSupportStation)
table.insert(strucCircRadius,500)
elseif pType == "Construction Station" then
table.insert(strucCircCol, colorConstructionStation)
table.insert(strucCircRadius,500)
elseif pType == "Reflector Shield" then
table.insert(strucCircCol, colorReflectorShield)
table.insert(strucCircRadius,750)
elseif pType == "Weapon Filter" then
table.insert(strucCircCol, colorWeaponFilter)
table.insert(strucCircRadius,750)
end
if catFrames[pType] then
frameID = catFrames[pType]
end
SetVisualGearValues(madness, nil, nil, 0, 0, nil, frameID, nil, visualSprite, nil, nil)
SetState(tempG, bor(GetState(tempG),gstInvisible) )
table.insert(strucAltDisplay, madness)
end
-- this is basically onStructureDelete
-- we may need to expand it for non-gear structures later
function CheckGearForStructureLink(gear)
local respawnerDestroyed = false
for i = 1, #strucID do
if strucID[i] == getGearValue(gear,"uniqueStructureID") then
if strucType[i] == "Respawner" then
respawnerDestroyed = true
end
table.remove(strucID,i)
table.remove(strucGear,i)
table.remove(strucClan,i)
table.remove(strucType,i)
table.remove(strucCost,i)
table.remove(strucHealth,i)
DeleteVisualGear(strucCirc[i])
table.remove(strucCirc,i)
table.remove(strucCircCol,i)
table.remove(strucCircRadius,i)
if strucAltDisplay[i] ~= 1 then
DeleteVisualGear(strucAltDisplay[i])
end
table.remove(strucAltDisplay,i)
end
end
if respawnerDestroyed == true then
runOnHogs(RecalibrateRespawn)
end
end
-- called when we add a new respawner
function EnableHogResurrectionForThisClan(gear)
if GetHogClan(gear) == GetHogClan(CurrentHedgehog) then
SetEffect(gear, heResurrectable, 1)
end
end
-- this is called when a respawner blows up
function RecalibrateRespawn(gear)
local respawnerList = {}
for i = 1, #strucID do
if (strucType[i] == "Respawner") and (strucClan[i] == GetHogClan(gear)) then
table.insert(respawnerList, i)
end
end
if #respawnerList >= 1 then
SetEffect(gear, heResurrectable, 1)
else
SetEffect(gear, heResurrectable, 0)
end
end
--resposition dead hogs at a respawner if they own one
function FindRespawner(gear)
local respawnerList = {}
for i = 1, #strucID do
if (strucType[i] == "Respawner") and (strucClan[i] == GetHogClan(gear)) then
table.insert(respawnerList, i)
end
end
if #respawnerList >= 1 then
local i = GetRandom(#respawnerList)+1
SetGearPosition(gear,GetX(strucGear[respawnerList[i]]),GetY(strucGear[respawnerList[i]])-25)
AddVisualGear(GetX(gear), GetY(gear), vgtExplosion, 0, false)
local msgs = {
loc("The respawner respawns %s"),
loc("%s died … and lives again!"),
loc("%s gets an extra life"),
loc("%s is now a zombie hedgehog"),
loc("%s has been rescued from death"),
}
local r = math.random(1, #msgs)
AddCaption(string.format(msgs[r], GetHogName(gear)))
else -- (this should never happen, but just in case)
SetEffect(gear, heResurrectable, 0)
DeleteGear(gear)
end
end
function CheckTeleport(gear, tX, tY)
local teleportOriginSuccessful = false
local teleportDestinationSuccessful = false
for i = 1, #strucID do
if (strucType[i] == "Teleportation Node") and (strucClan[i] == GetHogClan(CurrentHedgehog)) then
local dist = GetDistFromGearToXY(CurrentHedgehog,GetX(strucGear[i]), GetY(strucGear[i]))
local NR
NR = (48/100*strucCircRadius[i])/2
if dist <= NR*NR then
teleportOriginSuccessful = true
end
dist = GetDistFromXYtoXY(tX,tY,GetX(strucGear[i]), GetY(strucGear[i]))
NR = (48/100*strucCircRadius[i])/2
if dist <= NR*NR then
teleportDestinationSuccessful = true
end
end
end
if ((teleportDestinationSuccessful == false) or (teleportOriginSuccessful == false)) then
if IsHogLocal(CurrentHedgehog) then
AddCaption(loc("Teleport unsuccessful. Please teleport within a clan teleporter's sphere of influence."), colorMessageError, capgrpMessage)
end
SetGearTarget(gear, GetX(CurrentHedgehog), GetY(CurrentHedgehog))
end
end
--Check for proximity of gears to structures, and make structures behave accordingly
function CheckProximity(gear)
local sID = tempID_CheckProximity
local dist = GetDistFromGearToXY(gear, GetX(strucGear[sID]), GetY(strucGear[sID]))
if not dist then
return
end
-- calculate my real radius if I am an aura
local NR
NR = (48/100*strucCircRadius[sID])/2
-- we're in business
if dist <= NR*NR then
-- heal clan hogs
if strucType[sID] == "Healing Station" then
if GetGearType(gear) == gtHedgehog then
if GetHogClan(gear) == strucClan[sID] then
local hogLife = GetHealth(gear)
-- Heal hog by 1 HP, up to 150 HP total
if hogLife < 150 then
if ((hogLife + 1) % 5) == 0 then
-- Health anim every 5 HP
HealHog(gear, 1, false)
else
SetHealth(gear, hogLife+1)
end
end
-- Maybe find better animation?
local tempE = AddVisualGear(GetX(strucGear[sID]), GetY(strucGear[sID]), vgtSmoke, 0, false)
SetVisualGearValues(tempE, nil, nil, nil, nil, nil, nil, nil, nil, nil, colorHealingStationParticle)
end
end
-- explode enemy clan hogs
elseif strucType[sID] == "Bio-Filter" then
if GetGearType(gear) == gtHedgehog then
if (GetHogClan(gear) ~= strucClan[sID]) and (GetHealth(gear) > 0) then
AddGear(GetX(gear), GetY(gear), gtGrenade, 0, 0, 0, 1)
end
end
-- were those weapons in your pocket, or were you just happy to see me?
elseif strucType[sID] == "Weapon Filter" then
if GetGearType(gear) == gtHedgehog then
if (GetHogClan(gear) ~= strucClan[sID]) then
-- Vaporize (almost) all of the hog's ammo
local ammosDestroyed = 0
for wpnIndex = 0, AmmoTypeMax do
if (not IsConstructionModeAmmo(wpnIndex)) and wpnIndex ~= amSkip and wpnIndex ~= amNothing then
local count = GetAmmoCount(gear, wpnIndex)
-- Infinite ammos are spared
if count ~= 100 then
ammosDestroyed = ammosDestroyed + count
AddAmmo(gear, wpnIndex, 0)
end
end
end
if ammosDestroyed > 0 then
-- Vaporize effects
if gear == CurrentHedgehog then
local r = math.random(1, 2)
if r == 1 then
PlaySound(sndNutter, gear)
else
PlaySound(sndOops, gear)
end
end
PlaySound(sndVaporize)
for i=1, 5 do
AddVisualGear(GetX(gear), GetY(gear), vgtSmoke, 0, false)
end
local msgs = {
loc("%s lost all the weapons"),
loc("The ammo of %s has been vaporized"),
loc("%s fell victim to a weapon filter"),
loc("%s is suddenly low on ammo"),
loc("%s is now as poor as a church mouse"),
}
local r = math.random(1, #msgs)
AddCaption(string.format(msgs[r], GetHogName(gear)), 0xFFFFFFFF, capgrpAmmoinfo)
end
end
end
-- BOUNCE! POGO! POGO! POGO! POGO!
elseif strucType[sID] == "Reflector Shield" then
-- add check for whose projectile it is
if gearCanBeDeflected(gear) == true then
local gOwner = getGearValue(gear,"owner")
local gDeflects = getGearValue(gear,"deflects")
local gDmg = getGearValue(gear,"damage")
if gDeflects >= 3 then
DeleteGear(gear)
AddVisualGear(GetX(gear), GetY(gear), vgtSmoke, 0, false)
PlaySound(sndVaporize)
elseif gOwner ~= strucClan[sID] then
--whether to vaporize gears or bounce them
if gDmg ~= 0 then
local dx, dy = GetGearVelocity(gear)
if (dx == 0) and (dy == 0) then
-- static mine, explosive, etc encountered
-- do nothing
else
--let's bounce something!
dx = dx*(-1)
dy = dy*(-1)
SetGearVelocity(gear,dx,dy)
setGearValue(gear,"deflects",(gDeflects+1))
AddVisualGear(GetX(gear), GetY(gear), vgtExplosion, 0, false)
PlaySound(sndExplosion)
strucHealth[sID] = strucHealth[sID] - gDmg
if strucCirc[sID] then
strucCircCol[sID] = strucCircCol[sID] - gDmg
end
if strucHealth[sID] <= 0 then
AddVisualGear(GetX(strucGear[sID]), GetY(strucGear[sID]), vgtExplosion, 0, false)
DeleteGear(strucGear[sID])
PlaySound(sndExplosion)
end
end
else
DeleteGear(gear)
AddVisualGear(GetX(gear), GetY(gear), vgtSmoke, 0, false)
PlaySound(sndVaporize)
end
end
end
--mark as within range of a teleporter node
elseif strucType[sID] == "Teleportation Node" then
if GetGearType(gear) == gtHedgehog then
if GetHogClan(gear) == strucClan[sID] then
sProx["Teleportation Mode"] = true
end
end
-- mark as within range of construction station
-- and thus allow menu access to placement modes
-- for girders, mines, sticky mines and barrels
elseif strucType[sID] == "Construction Station" then
if GetGearType(gear) == gtHedgehog then
if GetHogClan(gear) == strucClan[sID] then
AddVisualGear(GetX(strucGear[sID]), GetY(strucGear[sID]), vgtSmoke, 0, false)
sProx["Girder Placement Mode"] = true
sProx["Rubber Placement Mode"] = true
sProx["Mine Placement Mode"] = true
sProx["Sticky Mine Placement Mode"] = true
sProx["Barrel Placement Mode"] = true
end
end
-- mark as within stupport station range
-- and thus allow menu access to placement modes
-- for weapon, utility, and med crates
elseif strucType[sID] == "Support Station" then
if GetGearType(gear) == gtHedgehog then
if GetHogClan(gear) == strucClan[sID] then
AddVisualGear(GetX(strucGear[sID]), GetY(strucGear[sID]), vgtSmoke, 0, false)
sProx["Health Crate Placement Mode"] = true
sProx["Weapon Crate Placement Mode"] = true
sProx["Utility Crate Placement Mode"] = true
end
end
end
end
end
-- used to check if we need to run through all hogs or just currenthedgehog
function isAStructureThatAppliesToMultipleGears(pID)
if strucType[pID] == "Healing Station" or
strucType[pID] == "Reflector Shield" or
strucType[pID] == "Weapon Filter" or
strucType[pID] == "Bio-Filter"
then
return(true)
else
return(false)
end
end
function HandleStructures()
if GameTime % 100 == 0 then
for k, _ in pairs(sProx) do
if k ~= "Structure Placement Mode" then
sProx[k] = false
end
end
end
for i = 1, #strucID do
if strucCirc[i] then
SetVisualGearValues(strucCirc[i], GetX(strucGear[i]), GetY(strucGear[i]), nil, nil, nil, nil, nil, strucCircRadius[i], nil, strucCircCol[i])
end
tempID_CheckProximity = i
SetVisualGearValues(strucAltDisplay[i], GetX(strucGear[i]), GetY(strucGear[i]), 0, 0, nil, nil, 800000, sprTarget)
if GameTime % 100 == 0 then
-- Check For proximity of stuff to our structures
if isAStructureThatAppliesToMultipleGears(i) then
runOnGears(CheckProximity)
else -- only check prox on CurrentHedgehog
if CurrentHedgehog ~= nil then
CheckProximity(CurrentHedgehog)
end
end
if strucType[i] == "Generator" then
for z = 0, ClansCount-1 do
if z == strucClan[i] then
increaseGearValue(strucGear[i],"power")
if getGearValue(strucGear[i],"power") == 10 then
setGearValue(strucGear[i],"power",0)
-- Add 1 energy (if not at max. already)
if not (conf_maxEnergy ~= "inf" and clanPower[z] + 1 > conf_maxEnergy) then
clanPower[z] = clanPower[z] + 1
-- Spawn one particle per energy added
local particle = AddVisualGear(GetX(strucGear[i]), GetY(strucGear[i])-16, vgtStraightShot, sprStar, false)
SetVisualGearValues(particle, nil, nil, math.random(-100, 100)*0.00005, 0.02, math.random(360), 0, 900, nil, 0, colorGeneratorParticle)
else
SetVisualGearValues(strucAltDisplay[i], GetX(strucGear[i]), GetY(strucGear[i]), 0, 0, nil, nil, 800000, sprTarget)
end
end
end
end
end
end
end
-- Add and remove ammo based on structure proximity
if GameTime % 100 == 0 and CurrentHedgehog ~= nil then
if sProx["Girder Placement Mode"] then
AddAmmo(CurrentHedgehog, amGirder, 100)
else
AddAmmo(CurrentHedgehog, amGirder, 0)
end
if sProx["Rubber Placement Mode"] then
AddAmmo(CurrentHedgehog, amRubber, 100)
else
AddAmmo(CurrentHedgehog, amRubber, 0)
end
if sProx["Mine Placement Mode"] or sProx["Sticky Mine Placement Mode"] or sProx["Barrel Placement Mode"] then
AddAmmo(CurrentHedgehog, amCMObjectPlacer, 100)
else
AddAmmo(CurrentHedgehog, amCMObjectPlacer, 0)
end
if sProx["Teleportation Mode"] then
AddAmmo(CurrentHedgehog, amTeleport, 100)
else
AddAmmo(CurrentHedgehog, amTeleport, 0)
end
if sProx["Weapon Crate Placement Mode"] or sProx["Utility Crate Placement Mode"] or sProx["Health Crate Placement Mode"] then
AddAmmo(CurrentHedgehog, amCMCratePlacer, 100)
else
AddAmmo(CurrentHedgehog, amCMCratePlacer, 0)
end
end
end
------------------------
-- SOME GENERAL METHODS
------------------------
function GetDistFromGearToXY(gear, g2X, g2Y)
local g1X, g1Y = GetGearPosition(gear)
if not g1X then
return nil
end
local q = g1X - g2X
local w = g1Y - g2Y
return ( (q*q) + (w*w) )
end
function GetDistFromXYtoXY(a, b, c, d)
local q = a - c
local w = b - d
return ( (q*q) + (w*w) )
end
-- essentially called when user clicks the mouse
-- with girders or an airattack
function PlaceObject(x,y)
if (clanUsedExtraTime[GetHogClan(CurrentHedgehog)] == true) and (cat[cIndex] == "Utility Crate Placement Mode") and (utilArray[pIndex][1] == amExtraTime) then
if IsHogLocal(CurrentHedgehog) then
AddCaption(loc("You may only place 1 Extra Time crate per turn."), colorMessageError, capgrpVolume)
end
PlaySound(sndDenied)
elseif (conf_cratesPerRound ~= "inf" and clanCratesSpawned[GetHogClan(CurrentHedgehog)] >= conf_cratesPerRound) and ( (cat[cIndex] == "Health Crate Placement Mode") or (cat[cIndex] == "Utility Crate Placement Mode") or (cat[cIndex] == "Weapon Crate Placement Mode") ) then
if IsHogLocal(CurrentHedgehog) then
AddCaption(string.format(loc("You may only place %d crates per round."), conf_cratesPerRound), colorMessageError, capgrpVolume)
end
PlaySound(sndDenied)
elseif (XYisInRect(x,y, clanBoundsSX[GetHogClan(CurrentHedgehog)],clanBoundsSY[GetHogClan(CurrentHedgehog)],clanBoundsEX[GetHogClan(CurrentHedgehog)],clanBoundsEY[GetHogClan(CurrentHedgehog)]) == true)
and (clanPower[GetHogClan(CurrentHedgehog)] >= placedExpense)
then
-- For checking if the actual placement succeeded
local placed = false
local gear
if cat[cIndex] == "Girder Placement Mode" then
placed = PlaceGirder(x, y, currentGirderRotation)
elseif cat[cIndex] == "Rubber Placement Mode" then
placed = PlaceRubber(x, y, currentGirderRotation)
elseif cat[cIndex] == "Health Crate Placement Mode" then
gear = SpawnHealthCrate(x,y)
if gear ~= nil then
placed = true
SetHealth(gear, pMode[pIndex])
clanCratesSpawned[GetHogClan(CurrentHedgehog)] = clanCratesSpawned[GetHogClan(CurrentHedgehog)] +1
end
elseif cat[cIndex] == "Weapon Crate Placement Mode" then
gear = SpawnAmmoCrate(x, y, atkArray[pIndex][1])
if gear ~= nil then
placed = true
clanCratesSpawned[GetHogClan(CurrentHedgehog)] = clanCratesSpawned[GetHogClan(CurrentHedgehog)] +1
end
elseif cat[cIndex] == "Utility Crate Placement Mode" then
gear = SpawnUtilityCrate(x, y, utilArray[pIndex][1])
if gear ~= nil then
placed = true
if utilArray[pIndex][1] == amExtraTime then
clanUsedExtraTime[GetHogClan(CurrentHedgehog)] = true
end
clanCratesSpawned[GetHogClan(CurrentHedgehog)] = clanCratesSpawned[GetHogClan(CurrentHedgehog)] +1
end
elseif cat[cIndex] == "Barrel Placement Mode" then
gear = AddGear(x, y, gtExplosives, 0, 0, 0, 0)
if gear ~= nil then
placed = true
SetHealth(gear, pMode[pIndex])
end
elseif cat[cIndex] == "Mine Placement Mode" then
gear = AddGear(x, y, gtMine, 0, 0, 0, 0)
if gear ~= nil then
placed = true
SetTimer(gear, pMode[pIndex])
end
elseif cat[cIndex] == "Sticky Mine Placement Mode" then
gear = AddGear(x, y, gtSMine, 0, 0, 0, 0)
placed = gear ~= nil
elseif cat[cIndex] == "Structure Placement Mode" then
AddStruc(x,y, pMode[pIndex],GetHogClan(CurrentHedgehog))
placed = true
end
if placed then
-- Pay the price
clanPower[GetHogClan(CurrentHedgehog)] = clanPower[GetHogClan(CurrentHedgehog)] - placedExpense
RenderClanPower()
else
if IsHogLocal(CurrentHedgehog) then
AddCaption(loc("Invalid Placement"), colorMessageError, capgrpVolume)
end
PlaySound(sndDenied)
end
else
if (clanPower[GetHogClan(CurrentHedgehog)] >= placedExpense) then
if IsHogLocal(CurrentHedgehog) then
AddCaption(loc("Invalid Placement"), colorMessageError, capgrpVolume)
end
else
if IsHogLocal(CurrentHedgehog) then
AddCaption(loc("Insufficient Power"), colorMessageError, capgrpVolume)
end
end
PlaySound(sndDenied)
end
end
-- called when user changes primary selection
-- either via up/down keys
-- or selecting girder/airattack
function RedefineSubset()
pIndex = 1
pMode = {}
if (CurrentHedgehog == nil or band(GetState(CurrentHedgehog), gstHHDriven) == 0) then
return false
end
local team = GetHogTeamName(CurrentHedgehog)
if cat[cIndex] == "Girder Placement Mode" then
pIndex = currentGirderRotation
pMode = {amGirder}
elseif cat[cIndex] == "Rubber Placement Mode" then
pIndex = currentGirderRotation
pMode = {amRubber}
elseif cat[cIndex] == "Barrel Placement Mode" then
pMode = {60}
teamLObjectMode[team] = cat[cIndex]
elseif cat[cIndex] == "Health Crate Placement Mode" then
pMode = {HealthCaseAmount}
teamLCrateMode[team] = cat[cIndex]
elseif cat[cIndex] == "Weapon Crate Placement Mode" then
for i = 1, #atkArray do
pMode[i] = atkArray[i][1]
end
teamLCrateMode[team] = cat[cIndex]
pIndex = teamLWeapIndex[team]
elseif cat[cIndex] == "Utility Crate Placement Mode" then
for i = 1, #utilArray do
pMode[i] = utilArray[i][1]
end
teamLCrateMode[team] = cat[cIndex]
pIndex = teamLUtilIndex[team]
elseif cat[cIndex] == "Mine Placement Mode" then
pMode = {0,1000,2000,3000,4000,5000}
teamLObjectMode[team] = cat[cIndex]
pIndex = teamLMineIndex[team]
elseif cat[cIndex] == "Sticky Mine Placement Mode" then
pMode = {amSMine}
teamLObjectMode[team] = cat[cIndex]
elseif cat[cIndex] == "Structure Placement Mode" then
pMode = {
loc_noop("Support Station"),
loc_noop("Construction Station"),
loc_noop("Healing Station"),
loc_noop("Teleportation Node"),
loc_noop("Weapon Filter"),
loc_noop("Bio-Filter"),
loc_noop("Reflector Shield"),
loc_noop("Respawner"),
loc_noop("Generator"),
}
pIndex = teamLStructIndex[team]
end
return true
end
-- Updates the handling of the main construction mode tools:
-- Structure Placer, Crate Placer, Object Placer.
-- This handles the internal category state,
-- the HUD display and the clans outline.
function HandleConstructionModeTools()
-- Update display selection criteria
if (CurrentHedgehog ~= nil and band(GetState(CurrentHedgehog), gstHHDriven) ~= 0) then
curWep = GetCurAmmoType()
local updated = false
local team = GetHogTeamName(CurrentHedgehog)
if (curWep == amGirder) then
cIndex = 1
RedefineSubset()
updated = true
elseif (curWep == amRubber) then
cIndex = 2
RedefineSubset()
updated = true
elseif (curWep == amCMStructurePlacer) then
cIndex = 9
RedefineSubset()
updateCost()
updated = true
elseif (curWep == amCMCratePlacer) then
cIndex = catReverse[teamLCrateMode[team]]
RedefineSubset()
updateCost()
updated = true
elseif (curWep == amCMObjectPlacer) then
cIndex = catReverse[teamLObjectMode[team]]
RedefineSubset()
updateCost()
updated = true
end
if updated then
AddCaption(loc(cat[cIndex]), GetClanColor(GetHogClan(CurrentHedgehog)), capgrpMessage)
showModeMessage()
wallsVisible = true
else
wallsVisible = false
end
else
curWep = amNothing
wallsVisible = false
end
end
local cursorIcon = nil
local ammoIcon = nil
local ammoIconBorder = nil
-- Handle cursor stuff. This displays a sprite under the cursor so you can see what you're going to place.
function HandleCursor()
if curWep == amCMStructurePlacer or curWep == amCMObjectPlacer or curWep == amCMCratePlacer then
local dFrame = 0
local dSprite
local yOffset = 0
if (cat[cIndex] == "Structure Placement Mode") then
dSprite = sprTarget
dFrame = catFrames[pMode[pIndex]]
elseif (cat[cIndex] == "Mine Placement Mode") then
dSprite = sprCustom2 -- sprMineOff
elseif (cat[cIndex] == "Sticky Mine Placement Mode") then
dSprite = sprCustom3 -- sprSMineOff
elseif (cat[cIndex] == "Barrel Placement Mode") then
dSprite = sprExplosives
elseif (cat[cIndex] == "Health Crate Placement Mode") then
dSprite = sprFAid
elseif (cat[cIndex] == "Weapon Crate Placement Mode") then
dSprite = sprCase
elseif (cat[cIndex] == "Utility Crate Placement Mode") then
dSprite = sprUtility
else
dSprite = sprArrow
end
-- Display the gear to be spawned under the cursor
if not cursorIcon then
cursorIcon = AddVisualGear(CursorX, CursorY, vgtStraightShot, dSprite, true, 3)
end
SetVisualGearValues(cursorIcon, CursorX, CursorY, 0, 0, 0, dFrame, 1000, dSprite, 1000)
-- Render ammo icon for weapon and utility crate.
-- But hide this from prying eyes of your enemies online!
if IsHogLocal(CurrentHedgehog) then
local ammoFrame
if (cat[cIndex] == "Weapon Crate Placement Mode") or (cat[cIndex] == "Utility Crate Placement Mode") then
local tArr
if (cat[cIndex] == "Weapon Crate Placement Mode") then
tArr = atkArray
else
tArr = utilArray
end
-- Get ammo icon
ammoFrame = tArr[pIndex][1] - 1
end
if ammoFrame then
local xDisplacement = 42
local yDisplacement = 42
local x = CursorX + yDisplacement
local y = CursorY + yDisplacement
-- Border around ammo icon
if not ammoIconBorder then
ammoIconBorder = AddVisualGear(x, y, vgtStraightShot, sprCustom1, true, 3)
end
SetVisualGearValues(ammoIconBorder, x, y, 0, 0, 0, 0, 1000, nil, 1000)
-- Ammo icon
if not ammoIcon then
ammoIcon = AddVisualGear(x, y, vgtStraightShot, sprAMAmmos, true, 3)
end
SetVisualGearValues(ammoIcon, x, y, 0, 0, 0, ammoFrame, 1000, nil, 1000)
else
-- Cleanup vgears if not placing ammo crates
if ammoIcon then
DeleteVisualGear(ammoIcon)
end
if ammoIconBorder then
DeleteVisualGear(ammoIconBorder)
end
end
end
else
-- Cleanup vgears
if cursorIcon then
DeleteVisualGear(cursorIcon)
end
if ammoIcon then
DeleteVisualGear(ammoIcon)
end
if ammoIconBorder then
DeleteVisualGear(ammoIconBorder)
end
end
end
function onVisualGearDelete(vg)
if vg ~= nil then
if vg == cursorIcon then
cursorIcon = nil
elseif vg == ammoIcon then
ammoIcon = nil
elseif vg == ammoIconBorder then
ammoIconBorder = nil
end
end
end
-- called in onGameTick()
function HandleConstructionMode()
HandleStructures()
if CurrentHedgehog ~= nil then
if wallsVisible == true then
HandleBorderEffects()
end
HandleCursor()
if GameTime % 100 == 0 then
-- Force-update the construction mode tools every 100ms.
-- This makes sure the announcer messages don't disappear
-- while the tool is selected.
if (band(GetState(CurrentHedgehog), gstHHDriven) ~= 0) then
RenderClanPower()
curWep = GetCurAmmoType()
HandleConstructionModeTools()
else
DeleteClanPowerTag()
curWep = amNothing
end
end
end
-- some kind of target detected, tell me your story
if cGear ~= nil then
local x,y = GetGearTarget(cGear)
if GetGearType(cGear) == gtAirAttack then
DeleteGear(cGear)
PlaceObject(x, y)
elseif GetGearType(cGear) == gtTeleport then
CheckTeleport(cGear, x, y)
cGear = nil
elseif GetGearType(cGear) == gtGirder then
currentGirderRotation = GetState(cGear)
PlaceObject(x, y)
end
end
end
---------------------------------------------------------------
-- Cycle through selection subsets (by changing pIndex, pMode)
-- i.e health of barrels, medikits,
-- timer of mines
-- contents of crates
-- gears to reposition etc.
---------------------------------------------------------------
function updateCost()
if CurrentHedgehog == nil or band(GetState(CurrentHedgehog), gstHHDriven) == 0 then return end
-- Fallback cost
placedExpense = 1
if pMode[pIndex] == "Healing Station" then
placedExpense = 50
elseif pMode[pIndex] == "Weapon Filter" then
placedExpense = 50
elseif pMode[pIndex] == "Bio-Filter" then
placedExpense = 100
elseif pMode[pIndex] == "Respawner" then
placedExpense = 300
elseif pMode[pIndex] == "Teleportation Node" then
placedExpense = 30
elseif pMode[pIndex] == "Support Station" then
placedExpense = 50
elseif pMode[pIndex] == "Construction Station" then
placedExpense = 50
elseif pMode[pIndex] == "Generator" then
placedExpense = 300
elseif pMode[pIndex] == "Reflector Shield" then
placedExpense = 200
elseif cat[cIndex] == "Weapon Crate Placement Mode" then
placedExpense = atkArray[pIndex][2]
elseif cat[cIndex] == "Utility Crate Placement Mode" then
placedExpense = utilArray[pIndex][2]
elseif cat[cIndex] == "Health Crate Placement Mode" then
placedExpense = 5
elseif cat[cIndex] == "Mine Placement Mode" then
placedExpense = 15
elseif cat[cIndex] == "Sticky Mine Placement Mode" then
placedExpense = 20
elseif cat[cIndex] == "Barrel Placement Mode" then
placedExpense = 10
elseif cat[cIndex] == "Girder Placement Mode" then
placedExpense = 1
elseif cat[cIndex] == "Rubber Placement Mode" then
placedExpense = 3
end
-- Hide cost from spectators.
-- Also, this information is hidden cuz it could be used to infer e.g. crate contents.
if IsHogLocal(CurrentHedgehog) then
AddCaption(string.format(loc("Cost: %d"), placedExpense), GetClanColor(GetHogClan(CurrentHedgehog)), capgrpAmmostate)
end
end
-- Should be called when the index of the mode was changed by the player.
-- E.g. new weapon crate contents or structure type
function updateIndex()
if (curWep == amGirder) or (curWep == amRubber) or (curWep == amCMStructurePlacer) or (curWep == amCMCratePlacer) or (curWep == amCMObjectPlacer) then
showModeMessage()
updateCost()
end
-- Update team variables so the previous state can be restored later
if CurrentHedgehog == nil or band(GetState(CurrentHedgehog), gstHHDriven) == 0 then return end
local val = pMode[pIndex]
local team = GetHogTeamName(CurrentHedgehog)
if cat[cIndex] == "Structure Placement Mode" then
teamLStructIndex[team] = pIndex
elseif cat[cIndex] == "Mine Placement Mode" then
teamLMineIndex[team] = pIndex
elseif cat[cIndex] == "Weapon Crate Placement Mode" then
teamLWeapIndex[team] = pIndex
elseif cat[cIndex] == "Utility Crate Placement Mode" then
teamLUtilIndex[team] = pIndex
end
end
function showModeMessage()
if CurrentHedgehog == nil or band(GetState(CurrentHedgehog), gstHHDriven) == 0 then return end
local val = pMode[pIndex]
local str
if cat[cIndex] == "Mine Placement Mode" then
-- timer in seconds
str = string.format(loc("%d sec"), div(val, 1000))
elseif cat[cIndex] == "Structure Placement Mode" then
str = loc(val)
elseif cat[cIndex] == "Girder Placement Mode" then
str = GetAmmoName(amGirder)
elseif cat[cIndex] == "Rubber Placement Mode" then
str = GetAmmoName(amRubber)
elseif cat[cIndex] == "Sticky Mine Placement Mode" then
str = GetAmmoName(amSMine)
elseif cat[cIndex] == "Weapon Crate Placement Mode"
or cat[cIndex] == "Utility Crate Placement Mode" then
str = GetAmmoName(val)
elseif cat[cIndex] == "Health Crate Placement Mode" then
str = tostring(val)
else
str = tostring(val)
end
-- Hide the mode message from prying enemy eyes except for the structure placer.
-- So stuff like crate contents or mine timers are secret.
if cat[cIndex] == "Structure Placement Mode" or IsHogLocal(CurrentHedgehog) then
AddCaption(str, GetClanColor(GetHogClan(CurrentHedgehog)), capgrpMessage2)
end
end
function rotateMode(pDir)
curWep = GetCurAmmoType()
local foundMatch = false
while(foundMatch == false) do
cIndex = cIndex + pDir
if (cIndex == 1) or (cIndex == 2) then -- we no longer hit girder by normal means
cIndex = #cat
elseif cIndex > #cat then
cIndex = 3 -- we no longer hit girder by normal means
end
if (GetCurAmmoType() == amCMCratePlacer) then
if (cat[cIndex] == "Health Crate Placement Mode") or
(cat[cIndex] == "Weapon Crate Placement Mode") or
(cat[cIndex] == "Utility Crate Placement Mode") then
foundMatch = true
end
elseif (GetCurAmmoType() == amCMObjectPlacer) then
if (cat[cIndex] == "Mine Placement Mode") or
(cat[cIndex] == "Sticky Mine Placement Mode") or
(cat[cIndex] == "Barrel Placement Mode") then
foundMatch = true
end
elseif (GetCurAmmoType() == amCMStructurePlacer) then
if cat[cIndex] == "Structure Placement Mode" then
foundMatch = true
end
end
end
if foundMatch == true then
RedefineSubset()
--updateCost()
HandleConstructionModeTools()
end
end
---------------------
-- PLAYER CONTROLS --
---------------------
-- [Timer X]: Used as shortcut key for faster selection of stuff
function onTimer(key)
curWep = GetCurAmmoType()
if (curWep == amCMStructurePlacer) then
-- Select structure directly in structure placer
-- [Timer X] selects structures 1-5
-- [Precise]+[Timer X] selects structures 6-10
local structureID = key
local precise = band(GetGearMessage(CurrentHedgehog), gmPrecise) ~= 0
if precise then
structureID = structureID + 5
end
-- Check for valid pIndex
if structureID <= #pMode then
pIndex = structureID
updateIndex()
end
elseif (curWep == amCMObjectPlacer) then
-- [Timer X]: Set mine time 1-5
if cat[cIndex] == "Mine Placement Mode" then
local index = key + 1
if key <= #pMode then
pIndex = index
updateIndex()
end
end
end
end
-- [Switch]: Set mine time to 0 (only in mine placement mode)
function onSwitch()
curWep = GetCurAmmoType()
if (curWep == amCMObjectPlacer) then
pIndex = 1
updateIndex()
end
end
-- [Left]/[Right]: Change submode (e.g. structure type) of any Construction Mode tool or rotate girder/rubber
function onLeft()
curWep = GetCurAmmoType()
if (curWep == amGirder) or (curWep == amRubber) or (curWep == amCMStructurePlacer) or (curWep == amCMCratePlacer) or (curWep == amCMObjectPlacer) then
pIndex = pIndex - 1
if pIndex == 0 then
pIndex = #pMode
end
updateIndex()
end
end
function onRight()
curWep = GetCurAmmoType()
if (curWep == amGirder) or (curWep == amRubber) or (curWep == amCMStructurePlacer) or (curWep == amCMCratePlacer) or (curWep == amCMObjectPlacer) then
pIndex = pIndex + 1
if pIndex > #pMode then
pIndex = 1
end
updateIndex()
end
end
-- [Up]/[Down]
-- Cycle through the primary categories
-- (by changing cIndex) i.e. mine, sticky mine,
-- barrels, health/weapon/utility crate.
function onUp()
curWep = GetCurAmmoType()
if ( (curWep == amCMCratePlacer) or (curWep == amCMObjectPlacer) ) then
if CurrentHedgehog ~= nil and band(GetState(CurrentHedgehog), gstHHDriven) ~= 0 then
rotateMode(-1)
end
end
end
function onDown()
curWep = GetCurAmmoType()
if ( (curWep == amCMCratePlacer) or (curWep == amCMObjectPlacer) ) then
if CurrentHedgehog ~= nil and band(GetState(CurrentHedgehog), gstHHDriven) ~= 0 then
rotateMode(1)
end
end
end
-- [Set weapon]/[Slot X]: Just update internal stuff
onSetWeapon = HandleConstructionModeTools()
onSlot = onSetWeapon
----------------------------
-- standard event handlers
----------------------------
-- Parses a positive integer
function parseInt(str, default, infinityPermitted)
if str == "inf" and infinityPermitted then
return "inf"
end
if str == nil then return default end
local s = string.match(str, "(%d*)")
if s ~= nil then
return math.min(4294967295, math.max(0, tonumber(s)))
else
return nil
end
end
-- Parse parameters
function onParameters()
parseParams()
conf_initialEnergy = parseInt(params["initialenergy"], conf_initialEnergy)
conf_energyPerRound = parseInt(params["energyperround"], conf_energyPerRound)
conf_maxEnergy = parseInt(params["maxenergy"], conf_maxEnergy, true)
conf_cratesPerRound = parseInt(params["cratesperround"], conf_cratesPerRound, true)
end
function onGameInit()
Explosives = 0
MinesNum = 0
EnableGameFlags(gfInfAttack)
-- This is a hack to make sure all girder/rubber placement is handled by Construction Mode to overwrite the default behaviour
SetMaxBuildDistance(1)
fortMode = MapGen == mgForts
-- if there are forts, let engine place the hogs on them
if fortMode then
EnableGameFlags(gfDivideTeams)
end
RedefineSubset()
end
function initialSetup(gear)
-- Engine already placed hogs in fort mode
if not fortMode then
FindPlace(gear, false, clanBoundsSX[GetHogClan(gear)], clanBoundsEX[GetHogClan(gear)],true)
end
-- Add core ammo
AddAmmo(gear, amCMStructurePlacer, 100)
AddAmmo(gear, amSkip, 100)
-- Remove special Construction Mode stuff.
-- This stuff is added and removed dynamically based on
-- proximity to structures.
AddAmmo(gear, amCMObjectPlacer, 0)
AddAmmo(gear, amCMCratePlacer, 0)
AddAmmo(gear, amGirder, 0)
AddAmmo(gear, amRubber, 0)
AddAmmo(gear, amTeleport, 0)
-- Drill strike is broken, so we force-remove it
AddAmmo(gear, amDrillStrike, 0)
-- Everything else is set by the weapon scheme.
-- Infinite switch is recommended.
end
function onGameStart()
trackTeams()
ShowMission (
loc("CONSTRUCTION MODE"),
loc("A Hedgewars mini-game"),
loc("Build a fortress and destroy your enemy.") .. "|" ..
loc("There are a variety of structures available to aid you.") .. "|" ..
loc("Use the structure placer to place structures.")
, -amCMStructurePlacer, 5000
)
SetAmmoTexts(amCMStructurePlacer, loc("Structure Placer"), loc("Construction Mode tool"), loc("Build one of multiple different structures|to aid you in victory, at the cost of energy.") .. "| |" ..
loc("Support Station: Allows placement of crates.") .. "|"..
loc("Construction Station: Allows placement of| girders, rubber, mines, sticky mines| and barrels.") .. "|" ..
loc("Healing Station: Heals nearby hogs.") .. "|" ..
loc("Teleportation Node: Allows teleportation| between other nodes.") .. "|" ..
loc("Weapon Filter: Dematerializes all ammo| carried by enemies entering it.") .. "|" ..
loc("Bio-Filter: Aggressively removes enemies.") .. "|" ..
loc("Reflector Shield: Reflects enemy projectiles.") .. "|" ..
loc("Respawner: Resurrects dead hogs.") .. "|" ..
loc("Generator: Generates energy.") .. "|" ..
" |" ..
loc("Left/right: Choose structure type").."|"..
loc("1-5, Precise + 1-4: Choose structure type").."|"..
loc("Cursor: Build structure"))
local txt_crateLimit = ""
if conf_cratesPerRound ~= "inf" then
txt_crateLimit = string.format(loc("You may only place %d crates per round."), conf_cratesPerRound) .. "|"
end
SetAmmoTexts(amCMCratePlacer, loc("Crate Placer"), loc("Construction Mode tool"),
loc("This allows you to create a crate anywhere|within your clan's area of influence,|at the cost of energy.") .. "|" ..
txt_crateLimit ..
loc("Up/down: Choose crate type") .. "|" ..
loc("Left/right: Choose crate contents") .. "|" ..
loc("|Cursor: Place crate"))
SetAmmoTexts(amCMObjectPlacer, loc("Object Placer"), loc("Construction Mode tool"),
loc("This allows you to create and place mines,|sticky mines and barrels anywhere within your|clan's area of influence at the cost of energy.").."|"..
loc("Up/down: Choose object type|1-5/Switch/Left/Right: Choose mine timer|Cursor: Place object")
)
SetAmmoDescriptionAppendix(amTeleport, loc("It only works in teleportation nodes of your own clan."))
local sCirc = AddVisualGear(0,0,vgtCircle,0,true)
SetVisualGearValues(sCirc, 0, 0, 100, 255, 1, 10, 0, 40, 3, 0x00000000)
for i = 0, ClansCount-1 do
clanPower[i] = math.min(conf_initialEnergy, conf_maxEnergy)
clanUsedExtraTime[i] = false
clanCratesSpawned[i] = 0
clanFirstTurn[i] = true
end
for i = 0, TeamsCount-1 do
local team = GetTeamName(i)
teamLStructIndex[team] = 1
teamLObjectMode[team] = "Mine Placement Mode"
teamLCrateMode[team] = "Weapon Crate Placement Mode"
teamLMineIndex[team] = 1
teamLWeapIndex[team] = 1
teamLUtilIndex[team] = 1
SetTeamLabel(team, tostring(clanPower[GetTeamClan(team)]))
end
local tMapWidth = RightX - LeftX
local tMapHeight = WaterLine - TopY
local clanInterval = div(tMapWidth,ClansCount)
-- define construction areas for each clan
-- if there are forts-based spawn locations, adjust areas around them
for i = 0, ClansCount-1 do
local slot
if fortMode then
slot = div(GetX(getFirstHogOfClan(i))-LeftX,clanInterval)
else
slot = i
end
local color = GetClanColor(i)
clanBoundsSX[i] = LeftX+(clanInterval*slot)+20
clanBoundsSY[i] = TopY
clanBoundsEX[i] = LeftX+(clanInterval*slot)+clanInterval-20
clanBoundsEY[i] = WaterLine
--top and bottom
AddWall(LeftX+(clanInterval*slot),TopY,clanInterval,wMargin,color)
AddWall(LeftX+(clanInterval*slot),WaterLine-25,clanInterval,wMargin,color)
--add a wall to the left and right
AddWall(LeftX+(clanInterval*slot)+20,TopY,wMargin,WaterLine,color)
AddWall(LeftX+(clanInterval*slot)+clanInterval-20,TopY,wMargin,WaterLine,color)
end
runOnHogs(initialSetup)
end
function onNewTurn()
curWep = GetCurAmmoType()
HandleConstructionModeTools()
local clan = GetHogClan(CurrentHedgehog)
if clanFirstTurn[clan] then
clanFirstTurn[clan] = false
else
clanPower[clan] = clanPower[clan] + conf_energyPerRound
if conf_maxEnergy ~= "inf" and clanPower[clan] > conf_maxEnergy then
clanPower[clan] = conf_maxEnergy
end
end
clanUsedExtraTime[clan] = false
clanCratesSpawned[clan] = 0
RenderClanPower()
end
function onEndTurn()
curWep = amNothing
HandleConstructionModeTools()
DeleteClanPowerTag()
end
function onGameTick()
HandleConstructionMode()
end
function onScreenResize()
-- redraw Tags so that their screen locations are updated
if (CurrentHedgehog ~= nil) then
RenderClanPower()
end
end
function onGearResurrect(gear)
if GetGearType(gear) == gtHedgehog then
AddVisualGear(GetX(gear), GetY(gear), vgtExplosion, 0, false)
FindRespawner(gear)
end
end
-- track hedgehogs and placement gears
function onGearAdd(gear)
local gt = GetGearType(gear)
if (gt == gtAirAttack) or (gt == gtTeleport) or (gt == gtGirder) then
cGear = gear
end
if isATrackedGear(gear) then
trackGear(gear)
elseif gearCanBeDeflected(gear) then
trackGear(gear)
setGearReflectionValues(gear)
end
end
function onGearDelete(gear)
if GetGearType(gear) == gtTarget then
CheckGearForStructureLink(gear)
end
if (GetGearType(gear) == gtAirAttack) or (GetGearType(gear) == gtTeleport) or (GetGearType(gear) == gtGirder) then
cGear = nil
end
if (isATrackedGear(gear) or gearCanBeDeflected(gear)) then
trackDeletion(gear)
end
end