------------------------------------
-- TUMBLER
-- v.0.7.1
------------------------------------
HedgewarsScriptLoad("/Scripts/Locale.lua")
HedgewarsScriptLoad("/Scripts/Tracker.lua")
local fMod = 1000000 -- use this for dev and .16+ games
local leftOn = false
local rightOn = false
local upOn = false
local downOn = false
local preciseOn = false
local wep = {}
local wepAmmo = {}
local wepCol = {}
local wepIndex = 0
local wepCount = 0
local fGears = 0
local mineSpawn
local barrelSpawn
local roundKills = 0
local barrelsEaten = 0
local minesEaten = 0
local moveTimer = 0
local fireTimer = 0
local TimeLeftCounter = 0
local TimeLeft = 0
local stopMovement = false
local tumbleStarted = false
local vTag = {}
------------------------
-- version 0.4
------------------------
-- removed some old code/comments
-- removed both shell and mortar as the primary and secondary weapons
-- the primary weapon is now an explosive(barrel)
-- added support for picking up barrels scattered about the map (backspace)
-- added support for dragging around mines (enter toggles on/off)
-- added support for primary fire being onAttackUp
-- added a trail to indicate when the player has 5s or less left to tumble
-- updated showmission to reflect changed controls and options
------------------------
-- version 0.5
------------------------
-- changed some of the user feedback
-- i can't remember??
-- substituted onAttackUp for onPrecise()
-- brought in line with new velocity changes
------------------------
-- version 0.6
------------------------
-- reduced starting "ammo"
-- randomly spawn new barrels/mines on new turn
-- updated user feedback
-- better locs and coloured addcaptions
-- added tag for turntime
-- removed tractor beam
-- added two new weapons and changed ammo handling
-- health crates now give tumbler time, and wep/utility give flamer ammo
-- explosives AND mines can be picked up to increase their relative ammo
-- replaced "no weapon" selected message that hw serves
-- modified crate frequencies a bit
-- added some simple kill-based achievements, i think
------------------------
-- version 0.7
------------------------
-- a few code optimisations/performance tweaks
-- removed some deprecated code
-- fix a potential spawn bug
-- improved HUD (now shows ammo counts)
-- improved user feedback (less generic messages)
-- colour-coded addcaptions to match hud :)
-- base tumbling time now equals scheme turntime
-- tumbling time extension is now based on the amount of health contained in crate
-- new mines per turn based on minesnum
-- new barrels per turn based on explosives
-- added 2 more achievements: barrel eater and mine eater (like kills, don't do anything atm)
-- slightly increased grab distance for explosives/mines
-- slightly increased flamer velocity
-- slightly decreased flamer volume
-- added a flame vaporiser (based on number of flame gears?)
-- give tumblers an extra 47 health on the start of their tumble to counter the grenade (exp)
-- refocus camera on tumbler on newturn (not on crates, barrels etc)
-- increase delay: yes, yes, eat your hearts out
-- commit log
-- Better HUD
-- Allow more user customization
-- Bugfix for new gear spawns
-- Performance tweaks
-- Variety of small gameplay changes
------------------------
-- version 0.7.1
------------------------
-- redraw HUD on screen resolution change
---------------------------
-- some other ideas/things
---------------------------
--[[
-- add better gameflag handling
-- fix flamer "shots remaining" message on start or choose a standard versus %
-- add more sounds
-- better barrel/minespawn effects
-- separate grab distance for mines/barrels
-- [probably not] make barrels always explode?
-- [probably not] persistent ammo?
-- [probably not] dont hurt tumblers and restore their health at turn end?
]]
----------------------------------------------------------------
----------------------------------------------------------------
local flames = {}
local fGearValues = {}
function runOnflames(func)
for k, gear in ipairs(flames) do
func(gear)
end
end
function trackFGear(gear)
table.insert(flames, gear)
end
function trackFGearDeletion(gear)
fGearValues[gear] = nil
for k, g in ipairs(flames) do
if g == gear then
table.remove(flames, k)
break
end
end
end
function getFGearValue(gear, key)
if fGearValues[gear] ~= nil then
return fGearValues[gear][key]
end
return nil
end
function setFGearValue(gear, key, value)
found = false
for id, values in pairs(fGearValues) do
if id == gear then
values[key] = value
found = true
end
end
if not found then
fGearValues[gear] = { [key] = value }
end
end
function decreaseFGearValue(gear, key)
for id, values in pairs(fGearValues) do
if id == gear then
values[key] = values[key] - 1
end
end
end
function HandleLife(gear)
decreaseFGearValue(gear, "L")
if getFGearValue(gear, "L") == 0 then
AddVisualGear(GetX(gear), GetY(gear), vgtSmoke, 0, false)
DeleteGear(gear)
end
end
----------------------------------------------------------------
----------------------------------------------------------------
function HideTags()
for i = 0, 3 do
SetVisualGearValues(vTag[i],0,0,0,0,0,1,0, 0, 240000, 0xffffff00)
end
end
function DrawTag(i)
zoomL = 1.3
xOffset = 40
if i == 0 then
yOffset = 40
tCol = 0xffba00ff --0xffed09ff --0xffba00ff
tValue = TimeLeft
elseif i == 1 then
zoomL = 1.1
yOffset = 70
tCol = wepCol[0]
tValue = wepAmmo[0]
elseif i == 2 then
zoomL = 1.1
xOffset = 40 + 35
yOffset = 70
tCol = wepCol[1]
tValue = wepAmmo[1]
elseif i == 3 then
zoomL = 1.1
xOffset = 40 + 70
yOffset = 70
tCol = wepCol[2]
tValue = wepAmmo[2]
end
DeleteVisualGear(vTag[i])
vTag[i] = AddVisualGear(0, 0, vgtHealthTag, 0, false)
g1, g2, g3, g4, g5, g6, g7, g8, g9, g10 = GetVisualGearValues(vTag[i])
SetVisualGearValues (
vTag[i], --id
-(ScreenWidth/2) + xOffset, --xoffset
ScreenHeight - yOffset, --yoffset
0, --dx
0, --dy
zoomL, --zoom
1, --~= 0 means align to screen
g7, --frameticks
tValue, --value
240000, --timer
tCol --GetClanColor( GetHogClan(CurrentHedgehog) )
)
end
function GetGearDistance(gear)
g1X, g1Y = GetGearPosition(gear)
g2X, g2Y = GetGearPosition(CurrentHedgehog)
q = g1X - g2X
w = g1Y - g2Y
return( (q*q) + (w*w) )
end
-- add to your ammo ***WHEN YOU PUSH A KEY*** near them
-- yes that was my justification for a non generic method
function CheckProximityToExplosives(gear)
if (GetGearDistance(gear) < 1400) then
if (GetGearType(gear) == gtExplosives) then
wepAmmo[0] = wepAmmo[0] + 1
PlaySound(sndShotgunReload)
DeleteGear(gear)
AddCaption(wep[0] .. " " .. loc("ammo extended!"), wepCol[0], capgrpAmmoinfo )
DrawTag(1)
barrelsEaten = barrelsEaten + 1
if barrelsEaten == 5 then
AddCaption(loc("Achievement Unlocked") .. ": " .. loc("Barrel Eater!"),0xffba00ff,capgrpMessage2)
end
elseif (GetGearType(gear) == gtMine) then
wepAmmo[1] = wepAmmo[1] + 1
PlaySound(sndShotgunReload)
DeleteGear(gear)
AddCaption(wep[1] .. " " .. loc("ammo extended!"), wepCol[1], capgrpAmmoinfo )
DrawTag(2)
minesEaten = minesEaten + 1
if minesEaten == 5 then
AddCaption(loc("Achievement Unlocked") .. ": " .. loc("Mine Eater!"),0xffba00ff,capgrpMessage2)
end
end
else
--AddCaption("There is nothing here...")
end
end
-- check proximity on crates
function CheckProximity(gear)
dist = GetGearDistance(gear)
if (dist < 1600) and (GetGearType(gear) == gtCase) then
if GetHealth(gear) > 0 then
AddCaption(loc("Tumbling Time Extended!"), 0xffba00ff, capgrpMessage2 )
TimeLeft = TimeLeft + HealthCaseAmount --5 --5s
DrawTag(0)
--PlaySound(sndShotgunReload)
else
wepAmmo[2] = wepAmmo[2] + 800
PlaySound(sndShotgunReload)
AddCaption(wep[2] .. " " .. loc("fuel extended!"), wepCol[2], capgrpAmmoinfo )
DrawTag(3)
end
DeleteGear(gear)
end
end
function ChangeWeapon()
wepIndex = wepIndex + 1
if wepIndex == wepCount then
wepIndex = 0
end
AddCaption(wep[wepIndex] .. " " .. loc("selected!"), wepCol[wepIndex],capgrpAmmoinfo )
AddCaption(wepAmmo[wepIndex] .. " " .. loc("shots remaining."), wepCol[wepIndex],capgrpMessage2)
end
---------------
-- action keys
---------------
function onPrecise()
if (CurrentHedgehog ~= nil) and (stopMovement == false) and (tumbleStarted == true) and (wepAmmo[wepIndex] > 0) then
wepAmmo[wepIndex] = wepAmmo[wepIndex] - 1
AddCaption(wepAmmo[wepIndex] .. " " .. loc("shots remaining."), wepCol[wepIndex],capgrpMessage2)
if wep[wepIndex] == loc("Barrel Launcher") then
morte = AddGear(GetX(CurrentHedgehog), GetY(CurrentHedgehog), gtExplosives, 0, 0, 0, 1)
CopyPV(CurrentHedgehog, morte) -- new addition
x,y = GetGearVelocity(morte)
x = x*2
y = y*2
SetGearVelocity(morte, x, y)
DrawTag(1)
elseif wep[wepIndex] == loc("Mine Deployer") then
morte = AddGear(GetX(CurrentHedgehog), GetY(CurrentHedgehog), gtMine, 0, 0, 0, 0)
SetTimer(morte, 1000)
DrawTag(2)
end
end
preciseOn = true
end
function onPreciseUp()
preciseOn = false
end
function onHJump()
-- pick up explosives/mines if nearby them
if (CurrentHedgehog ~= nil) and (stopMovement == false) and (tumbleStarted == true) then
runOnGears(CheckProximityToExplosives)
end
end
function onLJump()
ChangeWeapon()
end
-----------------
-- movement keys
-----------------
function onLeft()
if (CurrentHedgehog ~= nil) and (stopMovement == false) then
leftOn = true
end
end
function onRight()
if (CurrentHedgehog ~= nil) and (stopMovement == false) then
rightOn = true
end
end
function onUp()
if (CurrentHedgehog ~= nil) and (stopMovement == false) then
upOn = true
end
end
function onDown()
if (CurrentHedgehog ~= nil) and (stopMovement == false) then
downOn = true
end
end
function onDownUp()
downOn = false
end
function onUpUp()
upOn = false
end
function onLeftUp()
leftOn = false
end
function onRightUp()
rightOn = false
end
--------------------------
-- other event handlers
--------------------------
function onGameInit()
CaseFreq = 0
HealthCaseProb = 0
Delay = 1000
mineSpawn = MinesNum
if mineSpawn > 4 then
mineSpawn = 4
end
barrelSpawn = Explosives
if barrelSpawn > 4 then
barrelSpawn = 4
end
--MinesNum = 0
--Explosives = 0
for i = 0, 3 do
vTag[0] = AddVisualGear(0, 0, vgtHealthTag, 0, false)
end
HideTags()
wep[0] = loc("Barrel Launcher")
wep[1] = loc("Mine Deployer")
wep[2] = loc("Flamer")
wepCol[0] = 0x78818eff
wepCol[1] = 0xa12a77ff
wepCol[2] = 0xf49318ff
wepCount = 3
end
function onGameStart()
ShowMission (
"TUMBLER",
loc("a Hedgewars mini-game"),
loc("Eliminate the enemy hogs to win.") .. "|" ..
" " .. "|" ..
loc("New Mines Per Turn") .. ": " .. (mineSpawn) .. "|" ..
loc("New Barrels Per Turn") .. ": " .. (barrelSpawn) .. "|" ..
loc("Time Extension") .. ": " .. (HealthCaseAmount) .. loc("sec") .. "|" ..
" " .. "|" ..
loc("Movement: [Up], [Down], [Left], [Right]") .. "|" ..
loc("Fire") .. ": " .. loc("[Left Shift]") .. "|" ..
loc("Change Weapon") .. ": " .. loc("[Enter]") .. "|" ..
loc("Grab Mines/Explosives") .. ": " .. loc("[Backspace]") .. "|" ..
" " .. "|" ..
loc("Health crates extend your time.") .. "|" ..
loc("Ammo is reset at the end of your turn.") .. "|" ..
"", 4, 4000
)
end
function onScreenResize()
-- redraw Tags so that their screen locations are updated
if (CurrentHedgehog ~= nil) and (tumbleStarted == true) then
for i = 0, 3 do
DrawTag(i)
end
end
end
function onNewTurn()
stopMovement = false
tumbleStarted = false
-- randomly create new barrels mines on the map every turn (can be disabled by setting mine/barrels to 0 in scheme)
for i = 0, barrelSpawn-1 do
gear = AddGear(100, 100, gtExplosives, 0, 0, 0, 0)
SetHealth(gear, 100)
if FindPlace(gear, false, 0, LAND_WIDTH, false) ~= nil then
tempE = AddVisualGear(GetX(gear), GetY(gear), vgtBigExplosion, 0, false)
end
end
for i = 0, mineSpawn-1 do
gear = AddGear(100, 100, gtMine, 0, 0, 0, 0)
if FindPlace(gear, false, 0, LAND_WIDTH, false) ~= nil then
tempE = AddVisualGear(GetX(gear), GetY(gear), vgtBigExplosion, 0, false)
end
end
-- randomly spawn time extension crates / flamer fuel on the map
r = GetRandom(100)
if r > 50 then
gear = SpawnHealthCrate(0, 0)
end
r = GetRandom(100)
if r > 70 then
gear = SpawnAmmoCrate(0, 0, amSkip)
end
HideTags()
--reset ammo counts
wepAmmo[0] = 2
wepAmmo[1] = 1
wepAmmo[2] = 50 -- 50000 -- 50
wepIndex = 2
ChangeWeapon()
roundKills = 0
barrelsEaten = 0
minesEaten = 0
FollowGear(CurrentHedgehog)
end
function DisableTumbler()
stopMovement = true
upOn = false
down = false
leftOn = false
rightOn = false
HideTags()
end
function onGameTick()
-- start the player tumbling with a boom once their turn has actually begun
if tumbleStarted == false then
if (TurnTimeLeft > 0) and (TurnTimeLeft ~= TurnTime) then
--AddCaption(loc("Good to go!"))
tumbleStarted = true
TimeLeft = (TurnTime/1000)
AddGear(GetX(CurrentHedgehog), GetY(CurrentHedgehog), gtGrenade, 0, 0, 0, 1)
SetHealth(CurrentHedgehog, GetHealth(CurrentHedgehog) + 47) -- new
for i = 0, 3 do
DrawTag(i)
end
end
end
if (CurrentHedgehog ~= nil) and (tumbleStarted == true) then
runOnGears(CheckProximity) -- crates
-- Calculate and display turn time
TimeLeftCounter = TimeLeftCounter + 1
if TimeLeftCounter == 1000 then
TimeLeftCounter = 0
TimeLeft = TimeLeft - 1
if TimeLeft >= 0 then
DrawTag(0)
end
end
if TimeLeft == 0 then
DisableTumbler()
end
-- handle movement based on IO
moveTimer = moveTimer + 1
if moveTimer == 100 then -- 100
moveTimer = 0
runOnflames(HandleLife)
---------------
-- new trail code
---------------
-- the trail lets you know you have 5s left to pilot, akin to birdy feathers
if (TimeLeft <= 5) and (TimeLeft > 0) then
tempE = AddVisualGear(GetX(CurrentHedgehog), GetY(CurrentHedgehog), vgtSmoke, 0, false)
g1, g2, g3, g4, g5, g6, g7, g8, g9, g10 = GetVisualGearValues(tempE)
SetVisualGearValues(tempE, g1, g2, g3, g4, g5, g6, g7, g8, g9, GetClanColor(GetHogClan(CurrentHedgehog)) )
end
--------------
dx, dy = GetGearVelocity(CurrentHedgehog)
dxlimit = 0.4*fMod
dylimit = 0.4*fMod
if dx > dxlimit then
dx = dxlimit
end
if dy > dylimit then
dy = dylimit
end
if dx < -dxlimit then
dx = -dxlimit
end
if dy < -dylimit then
dy = -dylimit
end
if leftOn == true then
dx = dx - 0.1*fMod
end
if rightOn == true then
dx = dx + 0.1*fMod
end
if upOn == true then
dy = dy - 0.1*fMod
end
if downOn == true then
dy = dy + 0.1*fMod
end
SetGearVelocity(CurrentHedgehog, dx, dy)
end
--
--flamer
--
fireTimer = fireTimer + 1
if fireTimer == 6 then -- 5 --10
fireTimer = 0
if (wep[wepIndex] == loc("Flamer") ) and (preciseOn == true) and (wepAmmo[wepIndex] > 0) and (stopMovement == false) and (tumbleStarted == true) then
wepAmmo[wepIndex] = wepAmmo[wepIndex] - 1
AddCaption(
loc("Flamer") .. ": " ..
(wepAmmo[wepIndex]/800*100) - (wepAmmo[wepIndex]/800*100)%2 .. "%",
wepCol[2],
capgrpMessage2
)
DrawTag(3)
dx, dy = GetGearVelocity(CurrentHedgehog)
shell = AddGear(GetX(CurrentHedgehog), GetY(CurrentHedgehog), gtFlame, 0, 0, 0, 0)
xdev = 1 + GetRandom(25) --15
xdev = xdev / 100
r = GetRandom(2)
if r == 1 then
xdev = xdev*-1
end
ydev = 1 + GetRandom(25) --15
ydev = ydev / 100
r = GetRandom(2)
if r == 1 then
ydev = ydev*-1
end
--*13 --8 *-4
SetGearVelocity(shell, (dx*4.5)+(xdev*fMod), (dy*4.5)+(ydev*fMod)) --10
end
end
--
end
end
function isATrackedGear(gear)
if (GetGearType(gear) == gtExplosives) or
(GetGearType(gear) == gtMine) or
(GetGearType(gear) == gtCase)
then
return(true)
else
return(false)
end
end
--[[function onGearDamage(gear, damage)
if gear == CurrentHedgehog then
-- You are now tumbling
end
end]]
function onGearAdd(gear)
if GetGearType(gear) == gtFlame then
trackFGear(gear)
fGears = fGears +1
if fGears < 80 then
setFGearValue(gear,"L",30)
else
setFGearValue(gear,"L",5) --3
end
elseif isATrackedGear(gear) then
trackGear(gear)
end
end
function onGearDelete(gear)
if GetGearType(gear) == gtFlame then
trackFGearDeletion(gear)
fGears = fGears -1
elseif isATrackedGear(gear) then
trackDeletion(gear)
-- achievements? prototype
elseif GetGearType(gear) == gtHedgehog then
if GetHogTeamName(gear) ~= GetHogTeamName(CurrentHedgehog) then
roundKills = roundKills + 1
if roundKills == 2 then
AddCaption(loc("Double Kill!"),0xffba00ff,capgrpMessage2)
elseif roundKills == 3 then
AddCaption(loc("Killing spree!"),0xffba00ff,capgrpMessage2)
elseif roundKills >= 4 then
AddCaption(loc("Unstoppable!"),0xffba00ff,capgrpMessage2)
end
elseif gear == CurrentHedgehog then
DisableTumbler()
elseif gear ~= CurrentHedgehog then
AddCaption(loc("Friendly Fire!"),0xffba00ff,capgrpMessage2)
end
end
if CurrentHedgehog ~= nil then
FollowGear(CurrentHedgehog)
end
end