share/hedgewars/Data/Scripts/RopeKnocking.lua
changeset 15490 0039baa9895e
equal deleted inserted replaced
15489:3c0a3c824c49 15490:0039baa9895e
       
     1 HedgewarsScriptLoad("/Scripts/Locale.lua")
       
     2 HedgewarsScriptLoad("/Scripts/Utils.lua")
       
     3 
       
     4 local hhs = {}
       
     5 local deadHogs = {}
       
     6 local missionWon = nil
       
     7 local endTimer = 1000
       
     8 local hogsKilled = 0
       
     9 local totalEnemies = 0
       
    10 local finishTime
       
    11 local playerFail = false
       
    12 local ropeGear = nil
       
    13 local endGameCalled = false
       
    14 local missionEndHandled = false
       
    15 local valkyriesTimer = -1
       
    16 
       
    17 local valkyriesDuration = 20000
       
    18 local timeBonus = 6000
       
    19 local killBonus = 6000
       
    20 local playValkyries = false
       
    21 
       
    22 local extraTime
       
    23 
       
    24 local playerTeamName
       
    25 local missionName = loc("Rope-knocking Challenge")
       
    26 -- Mission type:
       
    27 -- 0 = none (no special handling)
       
    28 -- 1 = challenge (saves mission vars)
       
    29 local missionType = 1
       
    30 
       
    31 local function getKillScore()
       
    32 	return div(hogsKilled * killBonus, totalEnemies)
       
    33 end
       
    34 
       
    35 local function protectEnemies()
       
    36 	-- Protect enemy hogs
       
    37 	for i=1, totalEnemies do
       
    38 		if hhs[i] and GetHealth(hhs[i]) then
       
    39 			SetEffect(hhs[i], heInvulnerable, 1)
       
    40 			SetEffect(hhs[i], heResurrectable, 1)
       
    41 		end
       
    42 	end
       
    43 end
       
    44 
       
    45 local function killStr(killed, total, score)
       
    46 	if total == 16 then
       
    47 		return string.format(loc("You have killed %d of 16 hedgehogs (+%d points)."), killed, score)
       
    48 	else
       
    49 		return string.format(loc("You have killed %d of %d hedgehogs (+%d points)."), killed, total, score)
       
    50 	end
       
    51 end
       
    52 
       
    53 local function gameOver()
       
    54 	StopMusicSound(sndRideOfTheValkyries)
       
    55 	valkyriesTimer = -1
       
    56 	missionWon = false
       
    57 	SendStat(siGameResult, loc("Challenge over!"))
       
    58 	local score = getKillScore()
       
    59 	SendStat(siCustomAchievement, killStr(hogsKilled, totalEnemies, score))
       
    60 	SendStat(siPointType, "!POINTS")
       
    61 	SendStat(siPlayerKills, tostring(score), playerTeamName)
       
    62 	protectEnemies()
       
    63 	if not endGameCalled then
       
    64 		EndGame()
       
    65 		endGameCalled = true
       
    66 	end
       
    67 	if missionType == 1 then
       
    68 		-- Update highscore
       
    69 		updateChallengeRecord("Highscore", score)
       
    70 	end
       
    71 end
       
    72 
       
    73 local function victory(onVictory)
       
    74 	missionWon = true
       
    75 	local e = 0
       
    76 	if extraTime then
       
    77 		e = extraTime
       
    78 	end
       
    79 	local totalTime = TurnTime + e * totalEnemies
       
    80 	local completeTime = (totalTime - finishTime) / 1000
       
    81 	ShowMission(missionName, loc("Challenge completed!"), loc("Congratulations!") .. "|" .. string.format(loc("Completion time: %.2fs"), completeTime), 0, 0)
       
    82 	PlaySound(sndHomerun)
       
    83 	-- Protect player hog
       
    84 	if hhs[0] and GetHealth(hhs[0]) then
       
    85 		SetEffect(hhs[0], heInvulnerable, 1)
       
    86 		SetEffect(hhs[0], heResurrectable, 1)
       
    87 	end
       
    88 	SendStat(siGameResult, loc("Challenge completed!"))
       
    89 	local hogScore = getKillScore()
       
    90 	local timeScore = div(finishTime * timeBonus, totalTime)
       
    91 	local score = hogScore + timeScore
       
    92 	SendStat(siCustomAchievement, killStr(hogsKilled, totalEnemies, hogScore))
       
    93 	SendStat(siCustomAchievement, string.format(loc("You have completed this challenge in %.2f s (+%d points)."), completeTime, timeScore))
       
    94 	SendStat(siPointType, "!POINTS")
       
    95 	SendStat(siPlayerKills, tostring(score), playerTeamName)
       
    96 	SetTeamLabel(playerTeamName, tostring(score))
       
    97 	SetTurnTimeLeft(MAX_TURN_TIME)
       
    98 
       
    99 	if missionType == 1 then
       
   100 		-- Update highscore
       
   101 		updateChallengeRecord("Highscore", score)
       
   102 	end
       
   103 	if onVictory then
       
   104 		onVictory()
       
   105 	end
       
   106 end
       
   107 
       
   108 local function knockTaunt()
       
   109 	local r = math.random(0,23)
       
   110 	local taunt
       
   111 	if r == 0 then taunt =		loc("%s has been knocked out.")
       
   112 	elseif r == 1 then taunt =	loc("%s hit the ground.")
       
   113 	elseif r == 2 then taunt =	loc("%s splatted.")
       
   114 	elseif r == 3 then taunt =	loc("%s was smashed.")
       
   115 	elseif r == 4 then taunt =	loc("%s felt unstable.")
       
   116 	elseif r == 5 then taunt =	loc("%s exploded.")
       
   117 	elseif r == 6 then taunt =	loc("%s fell from a high cliff.")
       
   118 	elseif r == 7 then taunt =	loc("%s goes the way of the lemming.")
       
   119 	elseif r == 8 then taunt =	loc("%s was knocked away.")
       
   120 	elseif r == 9 then taunt =	loc("%s was really unlucky.")
       
   121 	elseif r == 10 then taunt =	loc("%s felt victim to rope-knocking.")
       
   122 	elseif r == 11 then taunt =	loc("%s had no chance.")
       
   123 	elseif r == 12 then taunt =	loc("%s was a good target.")
       
   124 	elseif r == 13 then taunt =	loc("%s spawned at a really bad position.")
       
   125 	elseif r == 14 then taunt =	loc("%s was doomed from the beginning.")
       
   126 	elseif r == 15 then taunt =	loc("%s has fallen victim to gravity.")
       
   127 	elseif r == 16 then taunt =	loc("%s hates Newton.")		-- Isaac Newton
       
   128 	elseif r == 17 then taunt =	loc("%s had it coming.")
       
   129 	elseif r == 18 then taunt =	loc("%s is eliminated!")
       
   130 	elseif r == 19 then taunt =	loc("%s fell too fast.")
       
   131 	elseif r == 20 then taunt =	loc("%s flew like a rock.")
       
   132 	elseif r == 21 then taunt =	loc("%s stumbled.")
       
   133 	elseif r == 22 then taunt =	loc("%s was shoved away.")
       
   134 	elseif r == 23 then taunt =	loc("%s didn't expect that.")
       
   135 	end
       
   136 	return taunt
       
   137 end
       
   138 
       
   139 local function declareEnemyKilled(gear, onVictory)
       
   140 	if deadHogs[gear] or playerFail then
       
   141 		return
       
   142 	end
       
   143 	deadHogs[gear] = true
       
   144 	hogsKilled = hogsKilled + 1
       
   145 
       
   146 	-- Award extra time, if available
       
   147 	if extraTime and extraTime ~= 0 then
       
   148 		SetTurnTimeLeft(TurnTimeLeft + extraTime)
       
   149 		AddCaption(string.format(loc("+%d seconds!"), div(extraTime, 1000)), 0xFFFFFFFF, capgrpMessage2)
       
   150 	end
       
   151 
       
   152 	SetTeamLabel(playerTeamName, tostring(getKillScore()))
       
   153 
       
   154 	if hogsKilled == totalEnemies - 1 then
       
   155 		if playValkyries then
       
   156 			PlayMusicSound(sndRideOfTheValkyries)
       
   157 			valkyriesTimer = valkyriesDuration
       
   158 		end
       
   159 	elseif hogsKilled == totalEnemies then
       
   160 		finishTime = TurnTimeLeft
       
   161 		victory(onVictory)
       
   162 	end
       
   163 end
       
   164 
       
   165 --[[
       
   166 RopeKnocking function!
       
   167 
       
   168 This creates a rope-knocking challenge.
       
   169 The player spawns with one hog and a rope and must kill all other hogs
       
   170 by rope-knocking before the time runs out.
       
   171 The player wins points for each kill and gets a time bonus for killing
       
   172 all enemies.
       
   173 
       
   174 params is a table with all the required parameters.
       
   175 Fields of the params table:
       
   176 
       
   177 	MANDATORY:
       
   178 	- map: Map name
       
   179 	- theme: Theme name
       
   180 	- turnTime: Turn time
       
   181 	- playerTeam: Player team info:
       
   182 		{
       
   183 			x, y: Start position
       
   184 			faceLeft: If true, hog faces left
       
   185 		}
       
   186 	- enemyTeams: Table of enemy team tables. each enemy team table has this format:
       
   187 		{
       
   188 			name: Team name
       
   189 			flag: Flag
       
   190 			hogs: Hogs table:
       
   191 			{
       
   192 				x, y: Position
       
   193 				faceLeft: If true, hog faces left
       
   194 				hat: Hat name
       
   195 				name: Hog name
       
   196 			}
       
   197 		}
       
   198 
       
   199 	OPTIONAL:
       
   200 	- missionName: Mission name
       
   201 	- missionType:
       
   202 		0: None/other: No special handling
       
   203 		1: Challenge: Will save mission variables at end (default)
       
   204 	- killBonus: Score for killing all hogs (one hog scores ca. (killBonus/<number of enemies)) (default: 6000)
       
   205 	- timeBonus: Maximum theoretically possible time bonus (default: 6000)
       
   206 	- gameFlags: List of game flags, if you want to set your own
       
   207 	- extraTime: Extra time awarded for each kill, in milliseconds (default: 0)
       
   208 	- valkyries: If true, play "Ride of the Valkyries" at final enemy (default: false)
       
   209 	- onGameInit: Custom onGameInit callback
       
   210 	- onGameStart: Custom onGameStart callback
       
   211 	- onVictory: Function that is called when the mission is won.
       
   212 
       
   213 	Hint: Use onGameInit and onGameStart to place custom gears and girders
       
   214 	Hint: Use onVictory to save campaign variables if using this in a campaign
       
   215 
       
   216 ]]
       
   217 function RopeKnocking(params)
       
   218 	if params.missionName then
       
   219 		missionName = params.missionName
       
   220 	end
       
   221 	if params.extraTime then
       
   222 		extraTime = params.extraTime
       
   223 	end
       
   224 	if params.valkyries then
       
   225 		playValkyries = params.valkyries
       
   226 	end
       
   227 	if params.missionType then
       
   228 		missionType = params.missionType
       
   229 	end
       
   230 	if params.killBonus then
       
   231 		killBonus = params.killBonus
       
   232 	end
       
   233 	if params.timeBonus then
       
   234 		timeBonus = params.timeBonus
       
   235 	end
       
   236 
       
   237 	_G.onGameInit = function()
       
   238 
       
   239 		if params.gameFlags then
       
   240 			for g=1, #params.gameFlags do
       
   241 				EnableGameFlags(params.gameFlags[g])
       
   242 			end
       
   243 		end
       
   244 
       
   245 		EnableGameFlags(gfBorder, gfSolidLand)
       
   246 
       
   247 		TurnTime = params.turnTime
       
   248 		Delay = 500
       
   249 		Map = params.map
       
   250 		Theme = params.theme
       
   251 
       
   252 		-- Disable Sudden Death
       
   253 		WaterRise = 0
       
   254 		HealthDecrease = 0
       
   255 
       
   256 		CaseFreq = 0
       
   257 		MinesNum = 0
       
   258 		Explosives = 0
       
   259 
       
   260 		-- Player team
       
   261 		playerTeamName = AddMissionTeam(-1)
       
   262 		hhs[0] = AddMissionHog(1)
       
   263 		SetGearPosition(hhs[0], params.playerTeam.x, params.playerTeam.y)
       
   264 		if params.playerTeam.faceLeft == true then
       
   265 			HogTurnLeft(hhs[0], true)
       
   266 		end
       
   267 
       
   268 		-- Enemy teams
       
   269 		for t=1, #params.enemyTeams do
       
   270 			local team = params.enemyTeams[t]
       
   271 			params.enemyTeams[t].name = AddTeam(team.name, -2, "Simple", "Tank", "Default_qau", team.flag)
       
   272 			for h=1, #team.hogs do
       
   273 				local hogData = team.hogs[h]
       
   274 				local name = hogData.name
       
   275 				local hat = hogData.hat
       
   276 				if not hat then
       
   277 					hat = "NoHat"
       
   278 				end
       
   279 				local hog = AddHog(name, 0, 1, hat)
       
   280 				SetGearPosition(hog, hogData.x, hogData.y)
       
   281 				if hogData.faceLeft == true then
       
   282 					HogTurnLeft(hog, true)
       
   283 				end
       
   284 				table.insert(hhs, hog)
       
   285 
       
   286 				totalEnemies = totalEnemies + 1
       
   287 			end
       
   288 		end
       
   289 
       
   290 		if params.onGameInit then
       
   291 			params.onGameInit()
       
   292 		end
       
   293 	end
       
   294 
       
   295 	_G.onGameStart = function()
       
   296 		SendHealthStatsOff()
       
   297 
       
   298 		local timeTxt = ""
       
   299 		local displayTime = 4000
       
   300 		if extraTime and extraTime ~= 0 then
       
   301 			timeTxt = string.format(loc("For each kill you win %d seconds."), div(extraTime, 1000))
       
   302 			displayTime = 5000
       
   303 		end
       
   304 		local recordInfo = getReadableChallengeRecord("Highscore")
       
   305 		if recordInfo == nil then
       
   306 			recordInfo = ""
       
   307 		else
       
   308 			recordInfo = "|" .. recordInfo
       
   309 		end
       
   310 		ShowMission(
       
   311 			missionName,
       
   312 			loc("Challenge"),
       
   313 			loc("Use the rope to knock your enemies to their doom.") .. "|" ..
       
   314 			loc("Finish this challenge as fast as possible to earn bonus points.") .. "|" ..
       
   315 			timeTxt .. recordInfo, -amRope, displayTime)
       
   316 
       
   317 		SetTeamLabel(playerTeamName, "0")
       
   318 
       
   319 		if params.onGameStart then
       
   320 			params.onGameStart()
       
   321 		end
       
   322 	end
       
   323 
       
   324 	_G.onGameTick = function()
       
   325 
       
   326 		if (TurnTimeLeft == 1) and (missionWon == nil) then
       
   327 			PlaySound(sndBoring, CurrentHedgehog)
       
   328 			gameOver()
       
   329 		end
       
   330 
       
   331 		if missionWon ~= nil then
       
   332 
       
   333 			endTimer = endTimer - 1
       
   334 			if endTimer == 1 then
       
   335 				if not endGameCalled then
       
   336 					EndGame()
       
   337 					endGameCalled = true
       
   338 				end
       
   339 			end
       
   340 
       
   341 			if not missionEndHandled then
       
   342 				if missionWon == true then
       
   343 					AddCaption(loc("Victory!"), 0xFFFFFFFF, capgrpGameState)
       
   344 					if missionType == 1 then
       
   345 						SaveMissionVar("Won", "true")
       
   346 					end
       
   347 				else
       
   348 					AddCaption(loc("Challenge over!"), 0xFFFFFFFF, capgrpGameState)
       
   349 				end
       
   350 				missionEndHandled = true
       
   351 			end
       
   352 
       
   353 		end
       
   354 
       
   355 	end
       
   356 
       
   357 	_G.onGameTick20 = function()
       
   358 		if (valkyriesTimer > 0) then
       
   359 			valkyriesTimer = valkyriesTimer - 20
       
   360 			if valkyriesTimer <= 0 then
       
   361 				StopMusicSound(sndRideOfTheValkyries)
       
   362 			end
       
   363 		end
       
   364 		local drown = (hhs[0]) and (band(GetState(hhs[0]), gstDrowning) ~= 0)
       
   365 		if drown and missionWon == nil then
       
   366 			-- Player hog drowns
       
   367 			playerFail = true
       
   368 			return
       
   369 		end
       
   370 		for i=1, totalEnemies do
       
   371 			local hog = hhs[i]
       
   372 			drown = (hog) and (not deadHogs[hog]) and (band(GetState(hhs[i]), gstDrowning) ~= 0)
       
   373 			if drown then
       
   374 				declareEnemyKilled(hog, params.onVictory)
       
   375 			end
       
   376 		end
       
   377 
       
   378 		if ropeGear and not missionWon and band(GetState(ropeGear), gstCollision) ~= 0 then
       
   379 			-- Hide mission on first rope attach
       
   380 			HideMission()
       
   381 		end
       
   382 	end
       
   383 
       
   384 	_G.onGearDamage = function(gear, damage)
       
   385 
       
   386 		if gear == hhs[0] then
       
   387 			-- Player hog hurts itself
       
   388 			playerFail = true
       
   389 			StopMusicSound(sndRideOfTheValkyries)
       
   390 			valkyriesTimer = -1
       
   391 			protectEnemies()
       
   392 		end
       
   393 
       
   394 		if gear ~= hhs[0] and GetGearType(gear) == gtHedgehog and not deadHogs[gear] and missionWon == nil and playerFail == false then
       
   395 			-- Enemy hog took damage
       
   396 			AddVisualGear(GetX(gear), GetY(gear), vgtBigExplosion, 0, false)
       
   397 			DeleteGear(gear)
       
   398 			PlaySound(sndExplosion)
       
   399 			AddCaption(string.format(knockTaunt(), GetHogName(gear)), 0xFFFFFFFF, capgrpMessage)
       
   400 
       
   401 			declareEnemyKilled(gear, params.onVictory)
       
   402 		end
       
   403 
       
   404 	end
       
   405 
       
   406 	_G.onGearAdd = function(gear)
       
   407 		if GetGearType(gear) == gtRope then
       
   408 			ropeGear = gear
       
   409 		end
       
   410 	end
       
   411 
       
   412 	_G.onGearDelete = function(gear)
       
   413 
       
   414 		if (gear == hhs[0]) and (missionWon == nil) then
       
   415 			playerFail = true
       
   416 			gameOver()
       
   417 		end
       
   418 
       
   419 		if GetGearType(gear) == gtHedgehog and gear ~= hhs[0] and not deadHogs[gear] then
       
   420 			declareEnemyKilled(gear, params.onVictory)
       
   421 		end
       
   422 
       
   423 		if GetGearType(gear) == gtRope then
       
   424 			ropeGear = nil
       
   425 		end
       
   426 
       
   427 	end
       
   428 
       
   429 	if params.onAmmoStoreInit then
       
   430 		_G.onAmmoStoreInit = params.onAmmoStoreInit
       
   431 	else
       
   432 		_G.onAmmoStoreInit = function()
       
   433 			SetAmmo(amRope, 9, 0, 0, 0)
       
   434 		end
       
   435 
       
   436 		_G.onNewTurn = function()
       
   437 			SetWeapon(amRope)
       
   438 		end
       
   439 	end
       
   440 
       
   441 end