share/hedgewars/Data/Maps/CTF_Blizzard/map.lua
author Wuzzy <Wuzzy2@mail.ru>
Fri, 11 Oct 2019 18:41:56 +0200
changeset 15490 a18f7e4681b8
parent 15088 d59da429d22c
permissions -rw-r--r--
Add flame-less dynamite sprite for drowning dynamite

-------------------------------
-- CTF_BLIZZARD 0.9
--------------------------------

---------
-- 0.2
---------

-- disabled super weapons

-- theme modifications

-- improved hog placement system: teams can now be put
-- in any order and be of any size

---------
-- 0.3
---------

-- In this version:

-- changed starting weapons
-- changed crate drop contents and rate of drops

-- completely removed super weapons and super weapon scripts

-- removed custom respawning
-- removed set respawn points

-- added AIRespawn-esque respawning
-- added simple left vs right respawn points

-- added non-lethal poison to flag carriers as an indicator

-- improved flag mechanics and player-flag feedback
-- flag now instantly respawns if you kill enemy hog and return it,
-- or if the flag falls in water, _BUT_ not if it is blown up

---------
-- 0.4
---------

-- tweaked crate drop rates and crate contents
-- improved the teleporters, they should now be able to handle rope... hopefully
-- updated SetEffect calls to be in line with 0.9.15 definitions
-- added visual gears when hogs respawn
-- added visual gears when hogs teleport
-- added visual gear to track flag and flag carriers
-- removed poisoning of flag carriers
-- removed health adjustments for flag carriers due to aforementioned poisons

---------
-- 0.5
---------

-- added translation support, hopefully
-- added ctf rules
-- added effects to the teleporters
-- added aura round spawning area
-- changed the aura around the flag carrier / flag to an aura and added some support for this
-- changed things so the seed is no longer always the same...

---------
-- 0.6
---------

-- removed branding and version number
-- removed teleport from starting weapons
-- increased captures to 3

------------
-- 0.7
------------

-- hopefully fixed a bug with the teleporters
-- added a fix for crate possibly getting imbedded in land when it was near the water line

------------
-- 0.8
------------

-- fixed version control fail with missing check on onGearDelete

-- changed hog placements code so that they start in the same place for both teams
-- and hogs move in the same order, not backwards to each other.

-----------
-- 0.9
------------

-- add support for more players
-- re-enable sudden death, but set water rise to 0

HedgewarsScriptLoad("/Scripts/Locale.lua")

---------------------------------------------------------------
----------lots of bad variables and things
----------because someone is too lazy
----------to read about tables properly
------------------ "Oh well, they probably have the memory"

local actionReset = 0 -- used in CheckTeleporters()

local roundsCounter = 0	-- used to determine when to spawn more crates
						-- currently every 6 TURNS, should this work
						-- on ROUNDS instead?
local effectTimer = 0
local gameEnded = false

local ropeGear = nil

--------------------------
-- hog and team tracking variales
--------------------------

local numhhs = 0 -- store number of hedgehogs
local hhs = {} -- store hedgehog gears

local teamNameArr = {}	-- store the list of teams
local teamSize = {}	-- store how many hogs per team
local teamIndex = {} -- at what point in the hhs{} does each team begin
local clanTeams = {} -- list of teams per clan

local mostCapturesHogName = nil -- name of hog who holds the record of most flags captured
local mostCapturesHogTeam = nil -- name of team who holds the record of most flags captured
local mostCaptures = 0 -- number of most per-hog captures
local capturesPerHog = {}

-------------------
-- flag variables
-------------------

local fGear = {}	-- pointer to the case gears that represent the flag
local fThief = {}	-- pointer to the hogs who stole the flags
local fIsMissing = {}	-- have the flags been destroyed or captured
local fNeedsRespawn = {}	-- do the flags need to be respawned
local fCaptures = {}	-- the team "scores" how many captures
local fSpawnX = {}		-- spawn X for flags
local fSpawnY = {}		-- spawn Y for flags

local fThiefX = {}
local fThiefY = {}

local fSpawnC = {}
local fCirc = {} -- flag/carrier marker circles
local fCol = {} -- colour of the clans

local vCircX = {}
local vCircY = {}
local vCircMinA = {}
local vCircMaxA = {}
local vCircType = {}
local vCircPulse = {}
local vCircFuckAll = {}
local vCircRadius = {}
local vCircWidth = {}
local vCircCol = {}


--------------------------------
--zone and teleporter variables
--------------------------------

local leftTel
local rightTel

local zXMin = {}
local zWidth = {}
local zYMin = {}
local zHeight = {}
local zOccupied = {}
local zCount = 0

------------------------
-- zone methods
------------------------
-- see on gameTick also

function ManageTeleporterEffects()
	effectTimer = effectTimer + 1
	if effectTimer > 50 then
		effectTimer = 0

		local i = GetHogClan(CurrentHedgehog)
		if i ~= 0 and i ~= 1 then
			return
		end
		local eX = 10 + zXMin[i] + GetRandom(zWidth[i]-10)
		local eY = 50 + zYMin[i] + GetRandom(zHeight[i]-110)

		-- steam and smoke and DUST look good, smokering looks trippy
		-- smoketrace and eviltrace are not effected by wind?
		-- chunk is a LR falling gear
		local tempE = AddVisualGear(eX, eY, vgtDust, 0, false)
		SetVisualGearValues(tempE, eX, eY, nil, nil, nil, nil, nil, nil, nil, fCol[i])
	end
end

function CreateZone(xMin, yMin, width, height)


	zXMin[zCount] = xMin
	zYMin[zCount] = yMin
	zWidth[zCount] = width
	zHeight[zCount] = height
	zOccupied[zCount] = false
	zCount = zCount + 1

	return (zCount-1)

end

function GearIsInZone(gear, zI)

	if (GetX(gear) > zXMin[zI]) and (GetX(gear) < (zXMin[zI]+zWidth[zI])) and (GetY(gear) > zYMin[zI]) and (GetY(gear) < (zYMin[zI]+zHeight[zI])) then
		zOccupied[zI] = true
	else
		zOccupied[zI] = false
	end

	return zOccupied[zI]

end

------------------------
--flag methods
------------------------

function CheckScore(teamID)

	local alt
	if teamID == 0 then
		alt = 1

	elseif teamID == 1 then
		alt = 0
	end

	if fCaptures[teamID] == 3 and not gameEnded then
		gameEnded = true
		for i = 0, (numhhs-1) do
			if GetHogClan(hhs[i]) == alt then
				SetEffect(hhs[i], heResurrectable, 0)
				SetHealth(hhs[i],0)
			end
		end
		local victoryMsg = string.format(loc("Victory for %s!"), GetHogTeamName(CurrentHedgehog))
		AddCaption(victoryMsg, capcolDefault, capgrpGameState)

		-- Calculate team rankings
		local teamList = {}
		for i=0, TeamsCount-1 do
			local name = GetTeamName(i)
			local clan = GetTeamClan(name)
			if fCaptures[clan] ~= nil then
				table.insert(teamList, { score = fCaptures[clan], name = name, clan = clan })
			end
		end
		local teamRank = function(a, b)
			return a.score > b.score
		end
		table.sort(teamList, teamRank)

		for i=1, #teamList do
			SendStat(siPointType, "!POINTS")
			SendStat(siPlayerKills, tostring(teamList[i].score), teamList[i].name)
		end
		if mostCaptures >= 2 then
			SendStat(siCustomAchievement, string.format(loc("%s (%s) has captured the flag %d times."), mostCapturesHogName, mostCapturesHogTeam, mostCaptures))
		end
	end

end

function HandleRespawns()

	for i = 0, 1 do

		if fNeedsRespawn[i] == true then
			fGear[i] = SpawnFakeAmmoCrate(fSpawnX[i],fSpawnY[i],false,false)
			fNeedsRespawn[i] = false
			fIsMissing[i] = false -- new, this should solve problems of a respawned flag being "returned" when a player tries to score
			AddCaption(loc("Flag respawned!"), capcolDefault, capgrpAmmoinfo)
		end

	end

end

function FlagDeleted(gear)

	local wtf, bbq
	if (gear == fGear[0]) then
		wtf = 0
		bbq = 1
	elseif (gear == fGear[1]) then
		wtf = 1
		bbq = 0
	end

	if CurrentHedgehog ~= nil then

		--if the player picks up the flag
		if band(GetGearMessage(gear), gmDestroy) ~= 0 then

			fGear[wtf] = nil -- the flag has now disappeared and we shouldnt be pointing to it

			-- player has successfully captured the enemy flag
			if (GetHogClan(CurrentHedgehog) == wtf) and (CurrentHedgehog == fThief[bbq]) and (fIsMissing[wtf] == false) then
				PlaySound(sndShotgunReload)
				fIsMissing[wtf] = false
				fNeedsRespawn[wtf] = true
				fIsMissing[bbq] = false
				fNeedsRespawn[bbq] = true
				fCaptures[wtf] = fCaptures[wtf] +1

				AddCaption(string.format(loc("%s has scored!"), GetHogTeamName(CurrentHedgehog)), capcolDefault, capgrpGameState)
				for i=1, #clanTeams[wtf] do
					SetTeamLabel(clanTeams[wtf][i], fCaptures[wtf])
				end

				capturesPerHog[CurrentHedgehog] = capturesPerHog[CurrentHedgehog] + 1
				if capturesPerHog[CurrentHedgehog] > mostCaptures then
					mostCaptures = capturesPerHog[CurrentHedgehog]
					mostCapturesHogName = GetHogName(CurrentHedgehog)
					mostCapturesHogTeam = GetHogTeamName(CurrentHedgehog)
				end

				PlaySound(sndHomerun)
				fThief[bbq] = nil -- player no longer has the enemy flag
				CheckScore(wtf)

			--if the player is returning the flag
			elseif GetHogClan(CurrentHedgehog) == wtf then
				PlaySound(sndShotgunReload)
				fNeedsRespawn[wtf] = true

				-- NEW ADDIITON, does this work? Should make it possible to return your flag and then score in the same turn
				if fIsMissing[wtf] == true then
					HandleRespawns() -- this will set fIsMissing[wtf] to false :)
					AddCaption(loc("Flag returned!"), capcolDefault, capgrpAmmoinfo)
				elseif fIsMissing[wtf] == false then
					AddCaption(loc("That was pointless. The flag will respawn next round."))
				end

			--if the player is taking the enemy flag
			elseif GetHogClan(CurrentHedgehog) == bbq then
				PlaySound(sndShotgunReload)
				fIsMissing[wtf] = true
				for i = 0,numhhs-1 do
					if CurrentHedgehog == hhs[i] then
						fThief[wtf] = hhs[i]
					end
				end

				AddCaption(loc("Flag captured!"))

			else --below line doesnt usually get called
				AddCaption("Hmm... that wasn't supposed to happen...")

			end

		-- if flag has been destroyed
		else

			if GetY(fGear[wtf]) > 2025 then
				fGear[wtf] = nil
				fIsMissing[wtf] = true
				fNeedsRespawn[wtf] = true
				HandleRespawns()
			else
				fGear[wtf] = nil
				fIsMissing[wtf] = true
				fNeedsRespawn[wtf] = true
				AddCaption(loc("Boom!") .. " " .. loc("The flag will respawn next round."))
			end

		end

	-- if flag has been destroyed deep underwater and player is now nil
	-- probably only gets called if the flag thief drowns himself
	-- otherwise the above one will work fine
	else
		fGear[wtf] = nil
		fIsMissing[wtf] = true
		fNeedsRespawn[wtf] = true
		AddCaption(loc("The flag will respawn next round."))
	end

end

function FlagThiefDead(gear)

	local wtf, bbq
	if (gear == fThief[0]) then
		wtf = 0
		bbq = 1
	elseif (gear == fThief[1]) then
		wtf = 1
		bbq = 0
	end

	if fThief[wtf] ~= nil then
		
		if fThiefY[wtf] > 2040 then
			fGear[wtf] = SpawnFakeAmmoCrate(fThiefX[wtf],(fThiefY[wtf]+10),false,false)
		else
			fGear[wtf] = SpawnFakeAmmoCrate(fThiefX[wtf],(fThiefY[wtf]-50),false,false)
		end
		AddCaption(string.format(loc("%s has dropped the flag!"), GetHogName(gear)), capcolDefault, capgrpAmmoinfo)

		AddVisualGear(fThiefX[wtf], fThiefY[wtf], vgtBigExplosion, 0, false)
		fThief[wtf] = nil
	end

end

function HandleCircles()

	for i = 0, 1 do
		if fIsMissing[i] == false then -- draw a circle at the flag's spawning place
			SetVisualGearValues(fCirc[i], fSpawnX[i],fSpawnY[i], vCircMinA[i], vCircMaxA[i], vCircType[i], vCircPulse[i], vCircFuckAll[i], vCircRadius[i], vCircWidth[i], vCircCol[i])
		elseif (fIsMissing[i] == true) and (fNeedsRespawn[i] == false) then
			if fThief[i] ~= nil then -- draw circle round flag carrier
				SetVisualGearValues(fCirc[i], fThiefX[i], fThiefY[i], vCircMinA[i], vCircMaxA[i], vCircType[i], vCircPulse[i], vCircFuckAll[i], vCircRadius[i], vCircWidth[i], vCircCol[i])
			elseif fThief[i] == nil then -- draw cirle round dropped flag
				SetVisualGearValues(fCirc[i], GetX(fGear[i]),GetY(fGear[i]), vCircMinA[i], vCircMaxA[i], vCircType[i], vCircPulse[i], vCircFuckAll[i], vCircRadius[i], vCircWidth[i], vCircCol[i])
			end
		end

		if fNeedsRespawn[i] == true then -- if the flag has been destroyed, no need for a circle
			SetVisualGearValues(fCirc[i], fSpawnX[i],fSpawnY[i], 20, 200, 0, 0, 100, 0, 0, fCol[i])
		end
	end

end

------------------------
-- general methods
------------------------

function CheckTeleporters()

	local teleportActive = false

	if (GearIsInZone(CurrentHedgehog, leftTel) == true) and (GetHogClan(CurrentHedgehog) == 0) then
		teleportActive = true
		destinationX = 1402
		destinationY = 321
	elseif (GearIsInZone(CurrentHedgehog, rightTel) == true) and (GetHogClan(CurrentHedgehog) == 1) then
		teleportActive = true
		destinationX = 2692
		destinationY = 321
	end

	if teleportActive == true then
		if actionReset == 0 then
			if ropeGear ~= nil then
				if GetGearElasticity(ropeGear) ~= 0 then
					SetGearMessage(CurrentHedgehog, gmAttack)
				end
			end
		elseif actionReset == 10 then
			SetGearMessage(CurrentHedgehog, 0)
		elseif actionReset == 20 then
			AddVisualGear(GetX(CurrentHedgehog), GetY(CurrentHedgehog), vgtBigExplosion, 0, false)
			SetGearPosition(CurrentHedgehog,destinationX,destinationY)
			PlaySound(sndWarp)
			AddVisualGear(GetX(CurrentHedgehog), GetY(CurrentHedgehog), vgtBigExplosion, 0, false)
		end

		actionReset = actionReset + 1
		if actionReset >= 30 then
			actionReset = 0
		end

	end

end

function RebuildTeamInfo()


	-- make a list of individual team names
	for i = 0, (TeamsCount-1) do
		teamNameArr[i] = GetTeamName(i)
		teamSize[i] = 0
		teamIndex[i] = 0
		SetTeamLabel(teamNameArr[i], "0")
	end

	-- find out how many hogs per team, and the index of the first hog in hhs
	for i = 0, TeamsCount-1 do

		for z = 0, numhhs-1 do
			if GetHogTeamName(hhs[z]) == teamNameArr[i] then
				if teamSize[i] == 0 then
					teamIndex[i] = z -- should give starting index
				end
				teamSize[i] = teamSize[i] + 1

				local clan = GetHogClan(hhs[z])
				-- Also remember the clan to which the team belongs to
				if not clanTeams[clan] then
					clanTeams[clan] = {}
				end
				table.insert(clanTeams[clan], teamNameArr[i])
			end
		end


	end

end

function HandleCrateDrops()

	roundsCounter = roundsCounter +1

	if roundsCounter == 5 then

		roundsCounter = 0

		r = GetRandom(8)
		if r == 0 then
			SpawnSupplyCrate(0,0,amSwitch)
		elseif r == 1 then
			SpawnSupplyCrate(0,0,amTeleport)
		elseif r == 2 then
			SpawnSupplyCrate(0,0,amJetpack)
		elseif r == 3 then
			SpawnSupplyCrate(0,0,amExtraTime)
		elseif r == 4 then
			SpawnSupplyCrate(0,0,amGirder)
		elseif r == 5 then
			SpawnSupplyCrate(0,0,amDynamite)
		elseif r == 6 then
			SpawnSupplyCrate(0,0,amFlamethrower)
		elseif r == 7 then
			SpawnSupplyCrate(0,0,amPortalGun)
		end
		PlaySound(sndReinforce, CurrentHedgehog)

	end

end

------------------------
-- game methods
------------------------

function onGameInit()

	-- Things we don't modify here will use their default values.
	GameFlags = gfDivideTeams -- Game settings and rules
	TurnTime = 30000 -- The time the player has to move each round (in ms)
	CaseFreq = 0 -- The frequency of crate drops
	MinesNum = 0 -- The number of mines being placed
	Explosives = 0 -- The number of explosives being placed
	-- Disable Sudden Death
	WaterRise = 0
	HealthDecrease = 0
	Map = "Blizzard" -- The map to be played
	Theme = "Snow" -- The theme to be used

	SendHealthStatsOff()
	SendRankingStatsOff()

end


function onGameStart()

	ShowMission(loc("CTF_Blizzard"), loc("Capture The Flag"),
		loc("- Return the enemy flag to your base to score") .."|"..
		string.format(loc("- First clan to score %d captures wins"), 3) .. "|"..
		loc("- You may only score when your flag is in your base") .."|"..
		loc("- Hogs will drop the flag when killed") .."|"..
		loc("- Dropped flags may be returned or recaptured").."|"..
		loc("- Hogs will be revived") .."|"..
		loc("- Touch the sparkles near your base to teleport"), 11, 0)

	-- initialize teleporters
	leftTel = CreateZone(342,1316,42,449)	-- left teleporter (clan 0)
	rightTel = CreateZone(3719,1330,45,449)	-- right teleporter (clan 1)


	--new improved placement schematics aw yeah
	RebuildTeamInfo()
	local team1Placed = 0
	local team2Placed = 0
	for i = 0, (TeamsCount-1) do
		for g = teamIndex[i], (teamIndex[i]+teamSize[i]-1) do
			if GetHogClan(hhs[g]) == 0 then
				SetGearPosition(hhs[g],1403+ ((team1Placed+1)*50),1570)
				team1Placed = team1Placed +1
				if team1Placed > 6 then
					team1Placed = 0
				end
			elseif GetHogClan(hhs[g]) == 1 then
				SetGearPosition(hhs[g],2691- ((team2Placed+1)*50),1570)
				team2Placed = team2Placed +1
				if team2Placed > 6 then
					team2Placed = 0
				end
			end
		end
	end



	--spawn starting ufos and or super weapons
	SpawnSupplyCrate(2048,1858,amJetpack)

	--set flag spawn points and spawn the flags
	fSpawnX[0] = 957
	fSpawnY[0] = 1747
	fSpawnX[1] = 3123
	fSpawnY[1] = 1747

	if ClansCount < 2 then
		return
	end

	for i = 0, 1 do
		fGear[i] = SpawnFakeAmmoCrate(fSpawnX[i],fSpawnY[i],false,false)
		fCirc[i] = AddVisualGear(fSpawnX[i],fSpawnY[i],vgtCircle,0,true)
		fCol[i] = GetClanColor(i)

		fSpawnC[i] = AddVisualGear(fSpawnX[i],fSpawnY[i],vgtCircle,0,true)
		SetVisualGearValues(fSpawnC[i], fSpawnX[i],fSpawnY[i], 10, 200, 1, 10, 0, 300, 5, fCol[i])


		fIsMissing[i] = false
		fNeedsRespawn[i] = false
		fCaptures[i] = 0

		vCircMinA[i] = 20
		vCircMaxA[i] = 255
		vCircType[i] = 1
		vCircPulse[i] = 10
		vCircFuckAll[i] = 0
		vCircRadius[i] = 150
		vCircWidth[i] = 5
		vCircCol[i] = fCol[i]

		SetVisualGearValues(fCirc[i], fSpawnX[i],fSpawnY[i], vCircMinA[i], vCircMaxA[i], vCircType[i], vCircPulse[i], vCircFuckAll[i], vCircRadius[i], vCircWidth[i], vCircCol[i])

	end

end


function onNewTurn()

	HandleRespawns()

end

function onCaseDrop()

	HandleCrateDrops()

end

function onGameTick()

	-- onRessurect calls AFTER you have resurrected,
	-- so keeping track of x,y a few milliseconds before
	-- is useful

	for i = 0,1 do
		if fThief[i] ~= nil then
			fThiefX[i] = GetX(fThief[i])
			fThiefY[i] = GetY(fThief[i])
		end
	end

	-- things we wanna check often
	if (CurrentHedgehog ~= nil) then
		CheckTeleporters()
	end

	HandleCircles()
	if (CurrentHedgehog ~= nil) then
		ManageTeleporterEffects()
	end

end


function onAmmoStoreInit()

	SetAmmo(amDrill,9,0,0,0)
	SetAmmo(amMortar,9,0,0,0)

	SetAmmo(amGrenade,9,0,0,0)
	SetAmmo(amClusterBomb,4,0,0,0)

	SetAmmo(amShotgun, 9, 0, 0, 0)
	SetAmmo(amFlamethrower, 1, 0, 0, 1)

	SetAmmo(amFirePunch, 9, 0, 0, 0)
	SetAmmo(amBaseballBat, 2, 0, 0, 0)

	SetAmmo(amDynamite,2,0,0,1)
	SetAmmo(amSMine,4,0,0,0)

	SetAmmo(amBlowTorch, 9, 0, 0, 0)
	SetAmmo(amPickHammer, 9, 0, 0, 0)
	SetAmmo(amGirder, 2, 0, 0, 2)
	SetAmmo(amPortalGun, 2, 0, 0, 2)

	SetAmmo(amParachute, 9, 0, 0, 0)
	SetAmmo(amRope, 9, 0, 0, 0)
	SetAmmo(amTeleport, 0, 0, 0, 1)
	SetAmmo(amJetpack, 1, 0, 0, 1)

	SetAmmo(amSwitch, 2, 0, 0, 1)
	SetAmmo(amExtraTime,1,0,0,1)
	SetAmmo(amLowGravity,1,0,0,0)
	SetAmmo(amSkip, 9, 0, 0, 0)

end


function onGearResurrect(gear, vGear)

	-- mark the flag thief as dead if he needed a respawn
	for i = 0,1 do
		if gear == fThief[i] then
			FlagThiefDead(gear)
		end
	end

	-- place hogs belonging to each clan either left or right side of map
	if GetHogClan(gear) == 0 then
		FindPlace(gear, false, 0, 2048)
	elseif GetHogClan(gear) == 1 then
		FindPlace(gear, false, 2048, LAND_WIDTH)
	end

	if vGear then
		SetVisualGearValues(vGear, GetX(gear), GetY(gear))
	end

end

local excessHogsWarning = false

function onGearAdd(gear)

	if GetGearType(gear) == gtHedgehog then

		capturesPerHog[gear] = 0
		if GetHogClan(gear) > 1 then
			DeleteGear(gear)
			if not excessHogsWarning then
				WriteLnToChat(loc("Only two clans allowed! Excess hedgehogs will be removed."))
				excessHogsWarning = true
			end
		else
			hhs[numhhs] = gear
			numhhs = numhhs + 1
			SetEffect(gear, heResurrectable, 1)
		end

	elseif GetGearType(gear) == gtRope then
		ropeGear = gear
	end

end

function onGearDelete(gear)

	if (gear == fGear[0]) or (gear == fGear[1]) then
		FlagDeleted(gear)
	end

	if GetGearType(gear) == gtRope then
		ropeGear = nil
	end

	if GetGearType(gear) == gtHedgehog then
		for i = 0, (numhhs-1) do
			if gear == hhs[i] then
				
				for k = 0,1 do
					if gear == fThief[k] then
						FlagThiefDead(gear)
					end
				end				
				hhs[i] = nil	
			end		
		end
	end

end