share/hedgewars/Data/Scripts/Multiplayer/Tumbler.lua
author nemo
Sat, 20 Dec 2014 16:20:37 -0500
changeset 10696 173069dd7b17
parent 8043 da083f8d95e6
child 12056 05a968518a56
permissions -rw-r--r--
Make amGirder invisible in construction mode since it allows placing anywhere in fortress. Print "insufficient power" if placement was blocked due to that, to avoid confusing noobs.

------------------------------------
-- 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