share/hedgewars/Data/Scripts/Multiplayer/Capture_the_Flag.lua
author belphegorr <szabibibi@gmail.com>
Mon, 23 Jul 2012 19:15:59 +0300
changeset 7263 644eabbc9218
parent 5827 a416f1070fdf
child 7838 5c2337f8dbb2
permissions -rw-r--r--
Added a new function: AddNewEvent, which only adds an event to the list if it doesn't already exist. Kept the old one as it might me useful to be able to add an event more than once.

---------------------------------------
-- CAPTURE_THE_FLAG GAMEPLAY MODE 0.5
-- by mikade
---------------------------------------

-- Version History
---------
-- 0.1
---------

-- [conversion from map-dependant CTF_Blizzard to map independant Capture the Flag]
-- added an intial starting stage where flagspawn is decided by the players (weapon set will require a jetpack unless I set)
-- changed the flag from a crate to a visual gear, and all associated methods and checks relating to flags (five hours later, lol)
-- changed starting/respawning positioning to accommodate different map sizes
-- added another circle to mark flag spawn
-- added gameFlag filter
-- changed scoring feedback
-- cleaned up some code

-- removing own flag from spawning point no longer possible
-- destroying flags no longer possible.
-- added basic glowing circle effect to spawn area
-- added expanding circle to fgear itself

-- removed teleporters
-- removed random crate drops (this should be decided by scheme)
-- removed set map criteria like minesNum, turnTime, explosives etc. except for sudden death
-- removed weapon defintions
-- removed placement and respawning methods, hopefully divideTeams will have this covered

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

-- [now with user friendliness]
-- flag is now placed wherever you end up at the end of your first turn, this ensures that it is always placed by turn 3
-- removed a bunch of backup code and no-longer needed variables / methods from CTF_Blizzard days
-- removed an aura that was still mistakenly hanging about
-- added an in-game note about placements
-- added an in-game note about the rules of the game
-- added translation support and loc()'ed everything
-- changed things so the seed is no longer always the same...

-- In this version:
---------
-- 0.3
---------
-- [fufufufu kamikaze fix]
-- added nill checks to make sure the player doesn't generate errors by producing a nil value in hhs[] when he uses kamikaze
-- added a check to make sure the player doesn't kamikaze straight down and make the flag's starting point underwater
-- added a check to make sure the player drops the flag if he has it and he uses kamikaze

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

-- remove user-branding and version numbers
-- removed some stuff that wasn't needed
-- fix piano strike exploit
-- changed delay to allow for better portals
-- changed starting feedback a little
-- increased the radius around the circle indicating the flag thief so that it doesn't obscure his health

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

-- add support for more players
-- allow limited sudden death
-- stop TimeBox ruining my life
-- profit???

-----------------
--SCRIPT BEGINS
-----------------

-- enable awesome translaction support so we can use loc() wherever we want
loadfile(GetDataPath() .. "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 gameStarted = false
local gameTurns = 0

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

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

local numTeams --  store the number of teams in the game
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

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

local fPlaced = {} -- has the flag been placed TRUE/FALSE

local fGear = {}	-- pointer to the visual gears that represent the flag
local fGearX = {}
local fGearY = {}

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 FTTC = 0 -- flag thief tracker counter

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

local fGearRad = 0
local fGearRadMin = 5
local fGearRadMax = 33
local fGearTimer = 0

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

function CheckScore(teamID)

	if teamID == 0 then
		alt = 1
	elseif teamID == 1 then
		alt = 0
	end

	if fCaptures[teamID] == 3 then
		for i = 0, (numhhs-1) do
			if hhs[i] ~= nil then
				if GetHogClan(hhs[i]) == alt then
					SetEffect(hhs[i], heResurrectable, false)
					SetHealth(hhs[i],0)
				end
			end
		end
		if CurrentHedgehog ~= nil then
			ShowMission(loc("GAME OVER!"), loc("Victory for the ") .. GetHogTeamName(CurrentHedgehog), loc("Hooray!"), 0, 0)
		end
	end

end

function DoFlagStuff(gear)

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

	-- player has successfully captured the enemy flag
	if (GetHogClan(CurrentHedgehog) == wtf) and (CurrentHedgehog == fThief[bbq]) and (fIsMissing[wtf] == false) then

		DeleteVisualGear(fGear[wtf])
		fGear[wtf] = nil -- the flag has now disappeared

		fIsMissing[wtf] = false
		fNeedsRespawn[wtf] = true
		fIsMissing[bbq] = false
		fNeedsRespawn[bbq] = true
		fCaptures[wtf] = fCaptures[wtf] +1
		ShowMission(loc("You have SCORED!!"), GetHogTeamName(CurrentHedgehog) .. ": " .. fCaptures[wtf], loc("Opposing Team: ") .. fCaptures[bbq], 0, 0)
		PlaySound(sndVictory)
		fThief[bbq] = nil -- player no longer has the enemy flag
		CheckScore(wtf)

	--if the player is returning the flag
	elseif (GetHogClan(CurrentHedgehog) == wtf) and (fIsMissing[wtf] == true) then

		DeleteVisualGear(fGear[wtf])
		fGear[wtf] = nil -- the flag has now disappeared

		fNeedsRespawn[wtf] = true
		HandleRespawns() -- this will set fIsMissing[wtf] to false :)
		AddCaption(loc("Flag returned!"))

	--if the player is taking the enemy flag
	elseif GetHogClan(CurrentHedgehog) == bbq then

		DeleteVisualGear(fGear[wtf])
		fGear[wtf] = nil -- the flag has now disappeared

		fIsMissing[wtf] = true
		for i = 0,numhhs-1 do
			if CurrentHedgehog ~= nil then
				if CurrentHedgehog == hhs[i] then
					fThief[wtf] = hhs[i]
				end
			end
		end
		AddCaption(loc("Flag captured!"))

	end

end

function CheckFlagProximity()

	for i = 0, 1 do
		if fGear[i] ~= nil then

			g1X = fGearX[i]
			g1Y = fGearY[i]

			g2X, g2Y = GetGearPosition(CurrentHedgehog)

			q = g1X - g2X
			w = g1Y - g2Y
			dist = (q*q) + (w*w)

			if dist < 500 then --1600
				DoFlagStuff(fGear[i])
			end
		end
	end

end


function HandleRespawns()

	for i = 0, 1 do

		if fNeedsRespawn[i] == true then
			fGear[i] = AddVisualGear(fSpawnX[i],fSpawnY[i],vgtCircle,0,true)
			fGearX[i] = fSpawnX[i]
			fGearY[i] = fSpawnY[i]

			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!"))
		end

	end

end


function FlagThiefDead(gear)

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

	if fThief[wtf] ~= nil then
		-- falls into water
		--ShowMission(LAND_HEIGHT,  fThiefY[wtf], (LAND_HEIGHT - fThiefY[wtf]), 0, 0)
		if (LAND_HEIGHT - fThiefY[wtf]) < 15 then
			fIsMissing[wtf] = true
			fNeedsRespawn[wtf] = true
			HandleRespawns()
		else	--normally
			fGearX[wtf]  =  fThiefX[wtf]
			fGearY[wtf]  =  fThiefY[wtf]
			fGear[wtf] = AddVisualGear(fGearX[wtf],fGearY[wtf],vgtCircle,0,true)
		end

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

end

function HandleCircles()

	fGearTimer = fGearTimer + 1
	if fGearTimer == 50 then
		fGearTimer = 0
		fGearRad = fGearRad + 1
		if fGearRad > fGearRadMax then
			fGearRad = fGearRadMin
		end
	end

	for i = 0, 1 do

		--SetVisualGearValues(fSpawnC[i], fSpawnX[i],fSpawnY[i], 20, 200, 0, 0, 100, 50, 3, fCol[i]) -- draw a circ for spawning area

		if fIsMissing[i] == false then -- draw a flag marker at the flag's spawning place
			SetVisualGearValues(fCirc[i], fSpawnX[i],fSpawnY[i], 20, 20, 0, 10, 0, 33, 3, fCol[i])
			if fGear[i] ~= nil then -- draw the flag gear itself
				SetVisualGearValues(fGear[i], fSpawnX[i],fSpawnY[i], 20, 200, 0, 0, 100, fGearRad, 2, fCol[i])
			end
		elseif (fIsMissing[i] == true) and (fNeedsRespawn[i] == false) then
			if fThief[i] ~= nil then -- draw circle round flag carrier			-- 33
				SetVisualGearValues(fCirc[i], fThiefX[i], fThiefY[i], 20, 200, 0, 0, 100, 50, 3, fCol[i])
				--AddCaption("circle marking carrier")
			elseif fThief[i] == nil then -- draw cirle round dropped flag
				--g1X,g1Y,g4,g5,g6,g7,g8,g9,g10,g11 =  GetVisualGearValues(fGear[i])
				--SetVisualGearValues(fCirc[i], g1X, g1Y, 20, 200, 0, 0, 100, 33, 2, fCol[i])
				SetVisualGearValues(fCirc[i], fGearX[i], fGearY[i], 20, 200, 0, 0, 100, 33, 3, fCol[i])
				--AddCaption('dropped circle marker')
				if fGear[i] ~= nil then -- flag gear itself
					--SetVisualGearValues(fGear[i], g1X, g1Y, 20, 200, 0, 0, 100, 10, 4, fCol[i])
					SetVisualGearValues(fGear[i], fGearX[i], fGearY[i], 20, 200, 0, 0, 100, fGearRad, 2, fCol[i])
					--AddCaption('dropped flag itself')
				end
			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])
			--AddCaption("needs respawn = true. flag 'destroyed'?")
		end
	end

end

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

function CheckDistance(gear1, gear2)

	g1X, g1Y = GetGearPosition(gear1)
	g2X, g2Y = GetGearPosition(gear2)

	g1X = g1X - g2X
	g1Y = g1Y - g2Y
	z = (g1X*g1X) + (g1Y*g1Y)

	dist = z

	return dist

end

function RebuildTeamInfo()


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

	for i = 0, (numhhs-1) do

		z = 0
		unfinished = true
		while(unfinished == true) do

			newTeam = true
			tempHogTeamName = GetHogTeamName(hhs[i]) -- this is the new name

			if tempHogTeamName == teamNameArr[z] then
				newTeam = false
				unfinished = false
			end

			z = z + 1

			if z == TeamsCount then
				unfinished = false
				if newTeam == true then
					teamNameArr[numTeams] = tempHogTeamName
					numTeams = numTeams + 1
				end
			end

		end

	end

	-- find out how many hogs per team, and the index of the first hog in hhs
	for i = 0, numTeams-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
				--add a pointer so this hog appears at i in hhs
			end
		end

	end

end

function StartTheGame()

	gameStarted = true
	AddCaption(loc("Game Started!"))

	for i = 0, 1 do

		-- if someone uses kamikaze downwards, this can happen as the hog won't respawn
		if (LAND_HEIGHT - fSpawnY[i]) < 0 then
			tempG = AddGear(0, 0, gtTarget, 0, 0, 0, 0)
     			FindPlace(tempG, true, 0, LAND_WIDTH, true)
			fSpawnX[i], fSpawnY[i] = GetGearPosition(tempG)
			DeleteGear(tempG)
		end

		fGear[i] = AddVisualGear(fSpawnX[i],fSpawnY[i],vgtCircle,0,true)
		fCirc[i] = AddVisualGear(fSpawnX[i],fSpawnY[i],vgtCircle,0,true)
		fSpawnC[i] = AddVisualGear(fSpawnX[i],fSpawnY[i],vgtCircle,0,true)

		fGearX[i] = fSpawnX[i]
		fGearY[i] = fSpawnY[i]

		fCol[i] = GetClanColor(i)
		fIsMissing[i] = false
		fNeedsRespawn[i] = false
		fCaptures[i] = 0

		--SetVisualGearValues(zxc, 1000,1000, 20, 100, 0,    10,                     1,         100,        5,      GetClanColor(0))

		SetVisualGearValues(fSpawnC[i], fSpawnX[i],fSpawnY[i], 20, 100, 0, 10, 0, 75, 5, fCol[i])

	end

end

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

function onGameInit()

	GameFlags = band(bor(GameFlags, gfDivideTeams), bnot(gfKing + gfForts))
	--SuddenDeathTurns = 999 -- suddendeath is off, effectively
	WaterRise = 0
	Delay = 10

end


function onGameStart()

	--ShowMission(loc(caption), loc(subcaption), loc(goal), 0, 0)
	ShowMission(loc("CAPTURE THE FLAG"), loc("Flags, and their home base will be placed where each team ends their first turn."), "", 0, 0)

	RebuildTeamInfo()

	-- should gfDivideTeams do this automatically?
	--[[for i = 0, (TeamsCount-1) do
		for g = teamIndex[i], (teamIndex[i]+teamSize[i]-1) do
			if GetHogClan(hhs[g]) == 0 then
				FindPlace(hhs[g], false, 0, LAND_WIDTH/2)
			elseif GetHogClan(hhs[g]) == 1 then
				FindPlace(hhs[g], false, LAND_WIDTH/2, LAND_WIDTH)
			end
		end
	end]]

	fPlaced[0] = false
	fPlaced[1] = false

	--zxc = AddVisualGear(fSpawnX[i],fSpawnY[i],vgtCircle,0,true)


	--SetVisualGearValues(zxc, 1000,1000, 20, 255, 1,    10,                     0,         200,        1,      GetClanColor(0))
					--minO,max0 -glowyornot	--pulsate timer	 -- fuckall      -- radius -- width  -- colour
end


function onNewTurn()

	gameTurns = gameTurns + 1

	if lastTeam ~= GetHogTeamName(CurrentHedgehog) then
		lastTeam = GetHogTeamName(CurrentHedgehog)
	end

	--AddCaption("Handling respawns")
	if gameStarted == true then
		HandleRespawns()
	--new method of placing starting flags
	elseif gameTurns == 1 then
		ShowMission(loc("CAPTURE THE FLAG"), loc("Flags, and their home base will be placed where each team ends their first turn."), "", 0, 0)
	elseif gameTurns == 2 then
		fPlaced[0] = true
		ShowMission(loc("CAPTURE THE FLAG"), loc("RULES OF THE GAME [Press ESC to view]"), loc(" - Return the enemy flag to your base to score | - First team to 3 captures wins | - You may only score when your flag is in your base | - Hogs will drop the flag if killed, or drowned | - Dropped flags may be returned or recaptured | - Hogs respawn when killed"), 0, 0)
	elseif gameTurns == 3 then
		fPlaced[1] = true
		StartTheGame()
	end

end

function onGameTick()

	-- onRessurect calls AFTER you have resurrected,
	-- so keeping track of x,y a few milliseconds before
	-- is useful
	--FTTC = FTTC + 1
	--if FTTC == 100 then
	--	FTTC = 0
		for i = 0,1 do
			if fThief[i] ~= nil then
				fThiefX[i] = GetX(fThief[i])
				fThiefY[i] = GetY(fThief[i])
			end
		end
	--end

	-- things we wanna check often
	if (CurrentHedgehog ~= nil) then
		--AddCaption(LAND_HEIGHT - GetY(CurrentHedgehog))
		--AddCaption(GetX(CurrentHedgehog) .. "; " .. GetY(CurrentHedgehog))
		--CheckTeleporters()

	end

	if gameStarted == true then
		HandleCircles()
		if CurrentHedgehog ~= nil then
			CheckFlagProximity()
		end
	elseif CurrentHedgehog ~= nil then -- if the game hasn't started yet, keep track of where we are gonna put the flags on turn end

		if GetHogClan(CurrentHedgehog) == 0 then
			i = 0
		elseif GetHogClan(CurrentHedgehog) == 1 then
			i = 1
		end

		fSpawnX[i] = GetX(CurrentHedgehog)
		fSpawnY[i] = GetY(CurrentHedgehog)

	end

end

function onGearResurrect(gear)

	--AddCaption("A gear has been resurrected!")

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

	-- should be covered by gfDivideTeams, actually
	-- place hogs belonging to each clan either left or right side of map
	--if GetHogClan(gear) == 0 then
	--	FindPlace(gear, false, 0, LAND_WIDTH/2)
	--elseif GetHogClan(gear) == 1 then
	--	FindPlace(gear, false, LAND_WIDTH/2, LAND_WIDTH)
	--end

	AddVisualGear(GetX(gear), GetY(gear), vgtBigExplosion, 0, false)

end

function InABetterPlaceNow(gear)
	for i = 0, (numhhs-1) do
		if gear == hhs[i] then

			for i = 0,1 do
				if gear == fThief[i] then
					FlagThiefDead(gear)
				end
			end
			hhs[i] = nil
		end
	end
end

function onHogHide(gear)
	 InABetterPlaceNow(gear)
end

function onHogRestore(gear)
	match = false
	for i = 0, (numhhs-1) do
		if (hhs[i] == nil) and (match == false) then
			hhs[i] = gear
			--AddCaption(GetHogName(gear) .. " has reappeared it seems!")
			match = true
		end
	end
end


function onGearAdd(gear)

	if GetGearType(gear) == gtHedgehog then
		hhs[numhhs] = gear
		numhhs = numhhs + 1
		SetEffect(gear, heResurrectable, true)

	elseif GetGearType(gear) == gtPiano then

		for i = 0, 1 do
			if CurrentHedgehog == fThief[i] then
				FlagThiefDead(gear)
			end
		end

	end

end

function onGearDelete(gear)

	if GetGearType(gear) == gtHedgehog then
		InABetterPlaceNow(gear)
	end

end