share/hedgewars/Data/Scripts/SimpleMission.lua
changeset 13054 2d0f3e12fcad
parent 13053 8b42562dcada
child 13055 5ceb8b0632c4
equal deleted inserted replaced
13053:8b42562dcada 13054:2d0f3e12fcad
   124 	- missionTitle:		The name of the mission (highly recommended)
   124 	- missionTitle:		The name of the mission (highly recommended)
   125 	- goalText:		A short string explaining the goal of the mission (use this if you set custom goals).
   125 	- goalText:		A short string explaining the goal of the mission (use this if you set custom goals).
   126 
   126 
   127 	GOAL TYPES:
   127 	GOAL TYPES:
   128 	- type			name of goal type
   128 	- type			name of goal type
       
   129 	- failText		Optional. For non-goals, this text will be shown in the stats if mission fails due to this non-goal
       
   130 				being completed. For goals which fail, this text will be displayed at failure. Note that
       
   131 				all normal goals have sensible default fail texts.
   129 	- type="destroy"	Gear must be destroyed
   132 	- type="destroy"	Gear must be destroyed
   130 		- id		Gear to destroy
   133 		- id		Gear to destroy
   131 	- type="teamDefeat"	Team must be defeated
   134 	- type="teamDefeat"	Team must be defeated
   132 		- teamName	Name of team to defeat
   135 		- teamName	Name of team to defeat
   133 	- type="collect"	Crate must be collected
   136 	- type="collect"	Crate must be collected
   290 				SendStat(siCustomAchievement, customAchievements[a])
   293 				SendStat(siCustomAchievement, customAchievements[a])
   291 			end
   294 			end
   292 		end
   295 		end
   293 	end
   296 	end
   294 
   297 
       
   298 	_G.sm.criticalGearFailText = function(gearSmid)
       
   299 		local gear = _G.sm.goalGears[gearSmid]
       
   300 		if GetGearType(gear) == gtHedgehog then
       
   301 			return string.format(loc("%s is dead, who was critical to this mission!"), GetHogName(gear))
       
   302 		else
       
   303 			return loc("We have lost an object which was critical to this mission.")
       
   304 		end
       
   305 	end
       
   306 
   295 	_G.sm.checkGoal = function(goal)
   307 	_G.sm.checkGoal = function(goal)
   296 		if goal.type == "destroy" then
   308 		if goal.type == "destroy" then
   297 			return getGearValue(_G.sm.goalGears[goal.id], "sm_destroyed")
   309 			return getGearValue(_G.sm.goalGears[goal.id], "sm_destroyed")
   298 		elseif goal.type == "collect" then
   310 		elseif goal.type == "collect" then
   299 			local collector = getGearValue(_G.sm.goalGears[goal.id], "sm_collected")
   311 			local collector = getGearValue(_G.sm.goalGears[goal.id], "sm_collected")
   301 				if not goal.collectors then
   313 				if not goal.collectors then
   302 					if GetHogClan(collector) == _G.sm.playerClan then
   314 					if GetHogClan(collector) == _G.sm.playerClan then
   303 						return true
   315 						return true
   304 					else
   316 					else
   305 						-- Fail if the crate was collected by enemy
   317 						-- Fail if the crate was collected by enemy
   306 						return "fail"
   318 						return "fail", loc("The enemy has taken a crate which we really needed!")
   307 					end
   319 					end
   308 				else
   320 				else
   309 					for c=1, #goal.collectors do
   321 					for c=1, #goal.collectors do
   310 						if _G.sm.goalGears[goal.collectors[c]] == collector then
   322 						if _G.sm.goalGears[goal.collectors[c]] == collector then
   311 							return true
   323 							return true
   312 						end
   324 						end
   313 					end
   325 					end
   314 					-- Fail if the crate was collected by someone who was not supposed to get it
   326 					-- Fail if the crate was collected by someone who was not supposed to get it
   315 					return "fail"
   327 					return "fail", loc("The wrong hedgehog has taken the crate.")
   316 				end
   328 				end
   317 			else
   329 			else
   318 				-- Fail goal if crate was destroyed
   330 				-- Fail goal if crate was destroyed
   319 				if getGearValue(_G.sm.goalGears[goal.id], "sm_destroyed") then
   331 				if getGearValue(_G.sm.goalGears[goal.id], "sm_destroyed") then
   320 					return "fail"
   332 					return "fail", loc("A crate critical to this mission has been destroyed.")
   321 				end
   333 				end
   322 				return false
   334 				return false
   323 			end
   335 			end
   324 		elseif goal.type == "turns" then
   336 		elseif goal.type == "turns" then
   325 			return sm.gameTurns >= goal.turns
   337 			return sm.gameTurns >= goal.turns
   326 		elseif goal.type == "rounds" then
   338 		elseif goal.type == "rounds" then
   327 			return (TotalRounds) >= goal.rounds
   339 			return (TotalRounds) >= goal.rounds
   328 		elseif goal.type == "inZone" then
   340 		elseif goal.type == "inZone" then
   329 			if getGearValue(_G.sm.goalGears[goal.id], "sm_destroyed") then
   341 			if getGearValue(_G.sm.goalGears[goal.id], "sm_destroyed") then
   330 				return "fail"
   342 				return "fail", criticalGearFailText(goal.id)
   331 			end
   343 			end
   332 			local gX, gY = GetGearPosition(_G.sm.goalGears[goal.id])
   344 			local gX, gY = GetGearPosition(_G.sm.goalGears[goal.id])
   333 			-- 4 sub-goals, each optional
   345 			-- 4 sub-goals, each optional
   334 			local g1 = (not goal.xMin) or gX >= goal.xMin
   346 			local g1 = (not goal.xMin) or gX >= goal.xMin
   335 			local g2 = (not goal.xMax) or gX <= goal.xMax
   347 			local g2 = (not goal.xMax) or gX <= goal.xMax
   339 		elseif goal.type == "distGearPos" or goal.type == "distGearGear" then
   351 		elseif goal.type == "distGearPos" or goal.type == "distGearGear" then
   340 			local gX, tY, tX, tY
   352 			local gX, tY, tX, tY
   341 			if goal.type == "distGearPos" then
   353 			if goal.type == "distGearPos" then
   342 				if getGearValue(_G.sm.goalGears[goal.id], "sm_destroyed") then
   354 				if getGearValue(_G.sm.goalGears[goal.id], "sm_destroyed") then
   343 					-- Fail if gear was destroyed
   355 					-- Fail if gear was destroyed
   344 					return "fail"
   356 					return "fail", criticalGearFailText(goal.id)
   345 				end
   357 				end
   346 				gX, gY = GetGearPosition(_G.sm.goalGears[goal.id])
   358 				gX, gY = GetGearPosition(_G.sm.goalGears[goal.id])
   347 				tX, tY = goal.x, goal.y
   359 				tX, tY = goal.x, goal.y
   348 			elseif goal.type == "distGearGear" then
   360 			elseif goal.type == "distGearGear" then
   349 				if getGearValue(_G.sm.goalGears[goal.id1], "sm_destroyed") or getGearValue(_G.sm.goalGears[goal.id2], "sm_destroyed") then
   361 				-- Fail if one of the gears was destroyed
   350 					-- Fail if one of the gears was destroyed
   362 				if getGearValue(_G.sm.goalGears[goal.id1], "sm_destroyed") then
   351 					return "fail"
   363 					return "fail", criticalGearFailText(goal.id1)
       
   364 				elseif getGearValue(_G.sm.goalGears[goal.id2], "sm_destroyed") then
       
   365 					return "fail", criticalGearFailText(goal.id2)
   352 				end
   366 				end
   353 				gX, gY = GetGearPosition(_G.sm.goalGears[goal.id1])
   367 				gX, gY = GetGearPosition(_G.sm.goalGears[goal.id1])
   354 				tX, tY = GetGearPosition(_G.sm.goalGears[goal.id2])
   368 				tX, tY = GetGearPosition(_G.sm.goalGears[goal.id2])
   355 			end
   369 			end
   356 
   370 
   366 			return false
   380 			return false
   367 		elseif goal.type == "suddenDeath" then
   381 		elseif goal.type == "suddenDeath" then
   368 			return sm.isInSuddenDeath
   382 			return sm.isInSuddenDeath
   369 		elseif goal.type == "damage" then
   383 		elseif goal.type == "damage" then
   370 			local damage = goal.damage or 1
   384 			local damage = goal.damage or 1
   371 			local tookEnoughDamage = getGearValue(_G.sm.goalGears[goal.id], "sm_maxDamage") >= damage
   385 			local gear = _G.sm.goalGears[goal.id]
   372 			if getGearValue(_G.sm.goalGears[goal.id], "sm_destroyed") then
   386 			local tookEnoughDamage = getGearValue(gear, "sm_maxDamage") >= damage
       
   387 			if getGearValue(gear, "sm_destroyed") then
   373 				-- Fail if gear was destroyed without taking enough damage first
   388 				-- Fail if gear was destroyed without taking enough damage first
   374 				if not tookEnoughDamage and goal.canDestroy == false then
   389 				if not tookEnoughDamage and goal.canDestroy == false then
   375 					return "fail"
   390 					if GetGearType(gear) == gtHedgehog then
       
   391 						return "fail", string.format(loc("%s has been killed before taking enough damage first."), GetHogName(gear))
       
   392 					else
       
   393 						return "fail", loc("An object has been destroyed before it took enough damage.")
       
   394 					end
   376 				else
   395 				else
   377 				-- By default, succeed if gear was destroyed
   396 				-- By default, succeed if gear was destroyed
   378 					return true
   397 					return true
   379 				end
   398 				end
   380 			end
   399 			end
   381 			return tookEnoughDamage
   400 			return tookEnoughDamage
   382 		elseif goal.type == "drown" then
   401 		elseif goal.type == "drown" then
   383 			local drowned = getGearValue(_G.sm.goalGears[goal.id], "sm_drowned")
   402 			local drowned = getGearValue(_G.sm.goalGears[goal.id], "sm_drowned")
   384 			-- Fail if gear was destroyed by something other than drowning
   403 			-- Fail if gear was destroyed by something other than drowning
   385 			if not drowned and getGearValue(_G.sm.goalGears[goal.id], "sm_destroyed") then
   404 			if not drowned and getGearValue(_G.sm.goalGears[goal.id], "sm_destroyed") then
   386 				return "fail"
   405 				return "fail", criticalGearFailText(goal.id)
   387 			end
   406 			end
   388 			return drowned
   407 			return drowned
   389 		elseif goal.type == "poison" then
   408 		elseif goal.type == "poison" then
   390 			if getGearValue(_G.sm.goalGears[goal.id], "sm_destroyed") then
   409 			if getGearValue(_G.sm.goalGears[goal.id], "sm_destroyed") then
   391 				return "fail"
   410 				return "fail", criticalGearFailText(goal.id)
   392 			end
   411 			end
   393 			return GetEffect(_G.sm.goalGears[goal.id], hePoisoned) >= 1
   412 			return GetEffect(_G.sm.goalGears[goal.id], hePoisoned) >= 1
   394 		elseif goal.type == "freeze" then
   413 		elseif goal.type == "freeze" then
   395 			if getGearValue(_G.sm.goalGears[goal.id], "sm_destroyed") then
   414 			if getGearValue(_G.sm.goalGears[goal.id], "sm_destroyed") then
   396 				return "fail"
   415 				return "fail", criticalGearFailText(goal.id)
   397 			end
   416 			end
   398 			return GetEffect(_G.sm.goalGears[goal.id], heFrozen) >= 256
   417 			return GetEffect(_G.sm.goalGears[goal.id], heFrozen) >= 256
   399 		elseif goal.type == "cure" then
   418 		elseif goal.type == "cure" then
   400 			if getGearValue(_G.sm.goalGears[goal.id], "sm_destroyed") then
   419 			if getGearValue(_G.sm.goalGears[goal.id], "sm_destroyed") then
   401 				return "fail"
   420 				return "fail", criticalGearFailText(goal.id)
   402 			end
   421 			end
   403 			return GetEffect(_G.sm.goalGears[goal.id], hePoisoned) == 0
   422 			return GetEffect(_G.sm.goalGears[goal.id], hePoisoned) == 0
   404 		elseif goal.type == "melt" then
   423 		elseif goal.type == "melt" then
   405 			if getGearValue(_G.sm.goalGears[goal.id], "sm_destroyed") then
   424 			if getGearValue(_G.sm.goalGears[goal.id], "sm_destroyed") then
   406 				return "fail"
   425 				return "fail", criticalGearFailText(goal.id)
   407 			end
   426 			end
   408 			return GetEffect(_G.sm.goalGears[goal.id], heFrozen) == 0
   427 			return GetEffect(_G.sm.goalGears[goal.id], heFrozen) == 0
   409 		elseif goal.type == "waterSkip" then
   428 		elseif goal.type == "waterSkip" then
   410 			local skips = goal.skips or 1
   429 			local skips = goal.skips or 1
   411 			local hasEnoughSkips = getGearValue(_G.sm.goalGears[goal.id], "sm_waterSkips") >= skips
   430 			local hasEnoughSkips = getGearValue(_G.sm.goalGears[goal.id], "sm_waterSkips") >= skips
   412 			-- Fail if gear was destroyed before it got the required number of skips
   431 			-- Fail if gear was destroyed before it got the required number of skips
   413 			if not hasEnoughSkips and getGearValue(_G.sm.goalGears[goal.id], "sm_destroyed") then
   432 			if not hasEnoughSkips and getGearValue(_G.sm.goalGears[goal.id], "sm_destroyed") then
   414 				return "fail"
   433 				return "fail", criticalGearFailText(goal.id)
   415 			end
   434 			end
   416 			return hasEnoughSkips
   435 			return hasEnoughSkips
   417 		elseif goal.type == "teamDefeat" then
   436 		elseif goal.type == "teamDefeat" then
   418 			return #teamHogs[goal.teamName] == 0
   437 			return #teamHogs[goal.teamName] == 0
   419 		else
   438 		else
   427 	Returns "fail" if any of the goals has failed (i.e. is impossible to complete).
   446 	Returns "fail" if any of the goals has failed (i.e. is impossible to complete).
   428 	Returns nil when there are no custom goals ]]
   447 	Returns nil when there are no custom goals ]]
   429 	_G.sm.checkGoals = function()
   448 	_G.sm.checkGoals = function()
   430 		if params.customGoals ~= nil and #params.customGoals > 0 then
   449 		if params.customGoals ~= nil and #params.customGoals > 0 then
   431 			for key, goal in pairs(params.customGoals) do
   450 			for key, goal in pairs(params.customGoals) do
   432 				local done = _G.sm.checkGoal(goal)
   451 				local done, defaultFailText = _G.sm.checkGoal(goal)
   433 				if done == false or done == "fail" then
   452 				if done == false or done == "fail" then
   434 					return done
   453 					local failText
       
   454 					if goal.failText then
       
   455 						failText = goal.failText
       
   456 					else
       
   457 						failText = customFailText
       
   458 					end
       
   459 					return done, failText
   435 				end
   460 				end
   436 			end
   461 			end
   437 			return true
   462 			return true
   438 		else
   463 		else
   439 			return nil
   464 			return nil
   446 	_G.sm.checkNonGoals = function()
   471 	_G.sm.checkNonGoals = function()
   447 		if params.customNonGoals ~= nil and #params.customNonGoals > 0 then
   472 		if params.customNonGoals ~= nil and #params.customNonGoals > 0 then
   448 			for key, nonGoal in pairs(params.customNonGoals) do
   473 			for key, nonGoal in pairs(params.customNonGoals) do
   449 				local done = _G.sm.checkGoal(nonGoal)
   474 				local done = _G.sm.checkGoal(nonGoal)
   450 				if done == true then
   475 				if done == true then
   451 					return true
   476 					return true, nonGoal.failText
   452 				end
   477 				end
   453 			end
   478 			end
   454 		end
   479 		end
   455 		return false
   480 		return false
   456 	end
   481 	end
   478 	_G.sm.checkWinOrFail = function()
   503 	_G.sm.checkWinOrFail = function()
   479 		if errord then
   504 		if errord then
   480 			return
   505 			return
   481 		end
   506 		end
   482 		_G.sm.checkRegularVictory()
   507 		_G.sm.checkRegularVictory()
   483 		if _G.sm.checkNonGoals() == true or _G.sm.checkGoals() == "fail" then
   508 		local nonGoalStatus, nonGoalFailText = _G.sm.checkNonGoals()
   484 			_G.sm.lose()
   509 		local goalStatus, goalFailText = _G.sm.checkGoals()
   485 		elseif _G.sm.checkGoals() == true then
   510 		if nonGoalStatus == true then
       
   511 			_G.sm.lose(nonGoalFailText)
       
   512 		elseif goalStatus == "fail" then
       
   513 			_G.sm.lose(goalText)
       
   514 		elseif goalStatus == true then
   486 			_G.sm.win()
   515 			_G.sm.win()
   487 		end
   516 		end
   488 	end
   517 	end
   489 
   518 
   490 	_G.sm.win = function()
   519 	_G.sm.win = function()
   500 			_G.sm.makeStats(_G.sm.playerClan)
   529 			_G.sm.makeStats(_G.sm.playerClan)
   501 			EndGame()
   530 			EndGame()
   502 		end
   531 		end
   503 	end
   532 	end
   504 
   533 
   505 	_G.sm.lose = function()
   534 	_G.sm.lose = function(failReason)
   506 		if not _G.sm.gameEnded then
   535 		if not _G.sm.gameEnded then
   507 			_G.sm.gameEnded = true
   536 			_G.sm.gameEnded = true
   508 			AddCaption(loc("Scenario failed!"), 0xFFFFFFFF, capgrpGameState)
   537 			AddCaption(loc("Scenario failed!"), 0xFFFFFFFF, capgrpGameState)
   509 			SendStat(siGameResult, loc("You lose!"))
   538 			SendStat(siGameResult, loc("You lose!"))
       
   539 			if failReason then
       
   540 				SendStat(siCustomAchievement, failReason)
       
   541 			end
   510 			if GetHogLevel(CurrentHedgehog) == 0 then
   542 			if GetHogLevel(CurrentHedgehog) == 0 then
   511 				SetState(CurrentHedgehog, bor(GetState(CurrentHedgehog), gstLoser))
   543 				SetState(CurrentHedgehog, bor(GetState(CurrentHedgehog), gstLoser))
   512 				SetState(CurrentHedgehog, band(GetState(CurrentHedgehog), bnot(gstHHDriven)))
   544 				SetState(CurrentHedgehog, band(GetState(CurrentHedgehog), bnot(gstHHDriven)))
   513 			end
   545 			end
   514 			local clan = ClansCount-1
   546 			local clan = ClansCount-1