share/hedgewars/Data/Scripts/Multiplayer/Space_Invasion.lua
changeset 13710 0da36902e5b6
parent 13709 e2b1abb4fba5
child 13711 d5f12b1d180d
--- a/share/hedgewars/Data/Scripts/Multiplayer/Space_Invasion.lua	Tue Aug 28 03:45:44 2018 +0200
+++ b/share/hedgewars/Data/Scripts/Multiplayer/Space_Invasion.lua	Tue Aug 28 05:46:33 2018 +0200
@@ -89,8 +89,6 @@
 
 -- add support for other world edges (they are currently disabled)
 
--- maybe add a check for a tie, IMPOSSIBRU THERE ARE NO TIES
-
 -- if more weapons are added, replace primshotsfired all over the place
 
 -- look for derp and let invaders shoot again
@@ -148,13 +146,10 @@
 SI.preciseOn = false
 SI.roundLimit = 3		-- can be overridden by script parameter "rounds"
 SI.roundNumber = 0
-SI.firstClan = 10
+SI.lastRound = -1
 SI.gameOver = false
 SI.gameBegun = false
 
-SI.bestClan = 65535
-SI.bestScore = 0
-
 -- for script parameters
 -- NOTE: If you change this, also change the default “Space Invasion” game scheme
 SI.startBarrels = 5		-- "barrels"
@@ -172,8 +167,8 @@
 SI.numhhs = 0
 SI.hhs = {}
 
-SI.numTeams = 0
 SI.teamNameArr = {}
+SI.teamNameArrReverse = {}
 SI.teamClan = {}
 SI.teamSize = {}
 SI.teamIndex = {}
@@ -390,33 +385,12 @@
 		SI.teamCircsKilled[i] = 0
 		SI.teamSurfer[i] = false
 	end
-	SI.numTeams = 0
-
-	for i = 0, (SI.numhhs-1) do
-
-		local z = 0
-		local unfinished = true
-		while(unfinished == true) do
-
-			local newTeam = true
-			local tempHogTeamName = GetHogTeamName(SI.hhs[i]) -- this is the new name
-
-			if tempHogTeamName == SI.teamNameArr[z] then
-				newTeam = false
-				unfinished = false
-			end
-
-			z = z + 1
-
-			if z == (TeamsCount-1) then
-				unfinished = false
-				if newTeam == true then
-					SI.teamNameArr[SI.numTeams] = tempHogTeamName
-					SI.numTeams = SI.numTeams + 1
-				end
-			end
-
-		end
+
+	for i=0, TeamsCount-1 do
+
+		local name = GetTeamName(i)
+		SI.teamNameArr[i] = name
+		SI.teamNameArrReverse[name] = i
 
 	end
 
@@ -500,6 +474,11 @@
 	return newAward
 end
 
+-- Update scoreboard and check victory state.
+-- Returns 2 bools:
+-- 1: true if game over
+-- 2: true if game's not over but we're playing now in tie-breaking phase
+
 function CommentOnScore()
 	local teamStats = {}
 	for i = 0,(TeamsCount-1) do
@@ -521,7 +500,7 @@
 			local comment = teamStats[i].name .. " |" ..
 			string.format(loc("Score: %d"), teamStats[i].score) .. "|" ..
 			string.format(loc("Kills: %d"), teamStats[i].kills)
-			if i < TeamsCount then	
+			if i < TeamsCount then
 				comment = comment .. "| |"
 			end
 			table.insert(teamComment, comment)
@@ -533,20 +512,40 @@
 		table.insert(teamComment, comment)
 	end
 
+	local roundLimitHit = SI.roundNumber >= SI.roundLimit
+	local tie = teamStats[1].score == teamStats[2].score
+	local lGameOver = roundLimitHit and (not tie)
+
 	local entireC = ""
+
 	for i = TeamsCount,1,-1 do
 		entireC = entireC .. teamComment[i]
 	end
+
 	local statusText, scoreText
-	if SI.roundNumber >= SI.roundLimit then
+	-- Game is over
+	if lGameOver then
 		statusText = loc("Game over!")
 		scoreText = loc("Final team scores:")
-	else
+	-- Round is over and game is not yet complete
+	elseif not roundLimitHit then
 		AddCaption(string.format(loc("Rounds complete: %d/%d"), SI.roundNumber, SI.roundLimit), capcolDefault, capgrpMessage)
-		return
+		return lGameOver, false
+	-- Teams are tied for the lead at the end
+	elseif roundLimitHit and tie then
+		local tieBreakingRound = SI.roundNumber - SI.roundLimit + 1
+		local msg
+		if tieBreakingRound == 1 then
+			msg = loc("Teams are tied! Continue playing rounds until we have a winner!")
+		else
+			msg = string.format(loc("Tie-breaking round %d"), tieBreakingRound)
+		end
+		AddCaption(msg, capcolDefault, capgrpMessage)
+		return lGameOver, true
 	end
+
 	local displayTime
-	if SI.roundNumber >= SI.roundLimit then
+	if lGameOver then
 		displayTime = 20000
 	else
 		displayTime = 1
@@ -556,7 +555,7 @@
 			string.format(loc("Rounds complete: %d/%d"), SI.roundNumber, SI.roundLimit) .. "| " .. "|" ..
 			scoreText .. " |" ..entireC, 4, displayTime)
 
-	if SI.roundNumber >= SI.roundLimit then
+	if lGameOver then
 		local winnerTeam = teamStats[1].name
 		for i = 0, (SI.numhhs-1) do
 			if GetHogTeamName(SI.hhs[i]) == winnerTeam then
@@ -583,7 +582,17 @@
 --[[ Award some awards (just for fun, its for the stats screen only
 and has no effect on the score or game outcome. ]]
 		local awardsGiven = 0
-	
+
+		if SI.roundNumber == SI.roundLimit + 1 then
+			SendStat(siCustomAchievement,
+			loc("The teams were tied, so an additional round has been played to determine the winner."))
+			awardsGiven = awardsGiven + 1
+		elseif SI.roundNumber > SI.roundLimit then
+			SendStat(siCustomAchievement,
+			string.format(loc("The teams were tied, so %d additional rounds have been played to determine the winner."),
+			SI.roundNumber - SI.roundLimit))
+			awardsGiven = awardsGiven + 1
+		end
 		if SI.awardTotalKills >= 30 then
 			awardsGiven = awardsGiven + 1
 			SendStat(siCustomAchievement,
@@ -683,30 +692,36 @@
 			elseif r == 3 then text = loc("Nothing of interest has happened.")
 			elseif r == 4 then text = loc("There are no snarky comments this time.")
 			end
-		
+
 			SendStat(siCustomAchievement, text)
 		end
 	end
+
+	return lGameOver, false
 end
 
 function onNewRound()
+	SI.lastRound = TotalRounds
 	SI.roundNumber = SI.roundNumber + 1
 
-	CommentOnScore()
-
-	-- end game if its at round limit
-	if SI.roundNumber >= SI.roundLimit then
-
+	local lGameOver, lTied = CommentOnScore()
+	local bestScore = 0
+	local bestClan = -1
+
+	-- Game has been determined to be over, so end it
+	if lGameOver then
+
+		-- Get winning score
 		for i = 0, (TeamsCount-1) do
-			if SI.teamScore[i] > SI.bestScore then
-				SI.bestScore = SI.teamScore[i]
-				SI.bestClan = SI.teamClan[i]
+			if SI.teamScore[i] > bestScore then
+				bestScore = SI.teamScore[i]
+				bestClan = SI.teamClan[i]
 			end
 		end
 
 		-- Kill off all the losers
 		for i = 0, (SI.numhhs-1) do
-			if GetHogClan(SI.hhs[i]) ~= SI.bestClan then
+			if GetHogClan(SI.hhs[i]) ~= bestClan then
 				SetEffect(SI.hhs[i], heResurrectable, 0)
 				SetHealth(SI.hhs[i],0)
 			end
@@ -717,13 +732,66 @@
 		EndTurn(true)
 		SI.TimeLeft = 0
 		SendStat(siGraphTitle, loc("Score graph"))
+
+	-- Round limit passed and teams are tied!
+	elseif lTied then
+		-- Enter (or continue) tie-breaking phase...
+
+		-- Rules in case of a tie:
+		-- 1) All teams that are not tied for the lead are killed (they can't play anymore, but they will keep their score and be ranked normally)
+		-- 2) Another round is played with the remaining teams
+		-- 3) After this round, scores are checked again to determine a winner. If there's a tie again, this procedure is repeated
+
+		-- Get leading teams
+		for i = 0, (TeamsCount-1) do
+			if SI.teamScore[i] > bestScore then
+				bestScore = SI.teamScore[i]
+			end
+		end
+
+		local tiedForTheLead = {}
+		for i = 0, (TeamsCount-1) do
+			if SI.teamScore[i] == bestScore then
+				tiedForTheLead[i] = true
+			end
+		end
+
+		local wasCurrent = false
+		-- Kill teams not in the top
+		for i = 0, (SI.numhhs-1) do
+			local hog = SI.hhs[i]
+			if GetHealth(hog) then -- check if hog is still alive
+				local team = SI.teamNameArrReverse[GetHogTeamName(hog)]
+				if team and tiedForTheLead[team] ~= true then
+					-- hilarious loser face
+					SetState(hog, bor(GetState(hog), gstLoser))
+					-- die!
+					SetEffect(hog, heResurrectable, 0)
+					SetHealth(hog, 0)
+					-- Note the death might not trigger immediately since we
+					-- zero the health at the beginning of a turn rather than
+					-- the end of one.
+					-- It's just a minor visual thing, not a big deal.
+					if hog == CurrentHedgehog then
+						wasCurrent = true
+					end
+				end
+			end
+		end
+
+		-- if current hedgehog was among the loser, end the turn
+		if wasCurrent then
+			EndTurn(true)
+		end
+
+		-- From that point on, the game just continues normally ...
 	end
 end
 
 -- gaudy racer
 function CheckForNewRound()
 
-	if GetHogClan(CurrentHedgehog) == SI.firstClan then
+	if TotalRounds > 0 and TotalRounds > SI.lastRound then
 		onNewRound()
 	end
 
@@ -1108,7 +1176,6 @@
 	if (SI.gameOver == false) and (SI.gameBegun == false) then
 		SI.gameBegun = true
 		SI.roundNumber = 0 -- 0
-		SI.firstClan = GetHogClan(CurrentHedgehog)
 	end
 
 	if SI.gameOver == true then
@@ -1303,7 +1370,7 @@
 							value = SI.shotsHit, 
 						}
 					end
-		
+
 				end
 
 				-- other awards