share/hedgewars/Data/Scripts/TargetPractice.lua
changeset 10656 2093cf51eea1
child 11767 c21278c4218d
equal deleted inserted replaced
10655:1c85a442bd56 10656:2093cf51eea1
       
     1 --[=[
       
     2 Target Practice Mission Framework for Hedgewars
       
     3 
       
     4 This is a simple library intended to make setting up simple training missions a trivial
       
     5 task requiring just. The library has been created to reduce redundancy in Lua scripts.
       
     6 
       
     7 The training framework generates complete and fully usable training missions by just
       
     8 one function call.
       
     9 
       
    10 The missions generated by this script are all the same:
       
    11 - The player will get a team with a single hedgehog.
       
    12 - The team gets a single predefined weapon infinitely times.
       
    13 - A fixed sequence of targets will spawn at predefined positions.
       
    14 - When a target has been destroyed, the next target of the target sequence appears
       
    15 - The mission ends successfully when all targets have been destroyed
       
    16 - The mission ends unsuccessfully when the time runs out or the hedgehog dies
       
    17 - When the mission ends, a score is awarded, based on the performance (hit targets,
       
    18   accuracy and remaining time) of the hedgehog. When not all targets are hit, there
       
    19   will be no accuracy and time bonuses.
       
    20 
       
    21 To use this library, you first have to load it and to call TrainingMission once with
       
    22 the appropriate parameters. Really, that’s all!
       
    23 See the comment of TrainingMission for a specification of all parameters.
       
    24 
       
    25 Below is a template for your convenience, you just have to fill in the fields and delete
       
    26 optional arguments you don’t want.
       
    27 ----- snip -----
       
    28 HedgewarsScriptLoad("/Scripts/Training.lua")
       
    29 params = {
       
    30 	missionTitle = ,
       
    31 	map = ,
       
    32 	theme = ,
       
    33 	time = ,
       
    34 	ammoType = ,
       
    35 	gearType = ,
       
    36 	targets = {
       
    37 		{ x = , y = },
       
    38 		{ x = , y = },
       
    39 		-- etc.
       
    40 	},
       
    41 
       
    42 	wind = ,
       
    43 	solidLand = ,
       
    44 	artillery = ,
       
    45 	hogHat = ,
       
    46 	hogName = ,
       
    47 	teamName = ,
       
    48 	teamGrave = ,
       
    49 	clanColor = ,
       
    50 	goalText = ,
       
    51 	shootText =
       
    52 }
       
    53 TargetPracticeMission(params)
       
    54 ----- snip -----
       
    55 ]=]
       
    56 
       
    57 HedgewarsScriptLoad("/Scripts/Locale.lua")
       
    58 
       
    59 local player = nil
       
    60 local scored = 0
       
    61 local shots = 0
       
    62 local end_timer = 1000
       
    63 local game_lost = false
       
    64 local time_goal = 0
       
    65 local total_targets
       
    66 local targets
       
    67 
       
    68 --[[
       
    69 TrainingMission(params)
       
    70 
       
    71 This function sets up the *entire* training mission and needs one argument: params.
       
    72 The argument “params” is a table containing fields which describe the training mission.
       
    73 	mandatory fields:
       
    74 	- missionTitle:	the name of the mission
       
    75 	- map:		the name map to be used
       
    76 	- theme:	the name of the theme (does not need to be a standalone theme)
       
    77 	- time:		the time limit in milliseconds
       
    78 	- ammoType:	the ammo type of the weapon to be used
       
    79 	- gearType:	the gear type of the gear which is fired (used to count shots)
       
    80 	- targets:	The coordinates of where the targets will be spawned.
       
    81 			It is a table containing tables containing coordinates of format
       
    82 			{ x=value, y=value }. The targets will be spawned in the same
       
    83 			order as specified the coordinate tables appear. Example:
       
    84 				targets = {
       
    85 					{ x = 324, y = 43 },
       
    86 					{ x = 123, y = 56 },
       
    87 					{ x = 6, y = 0 },
       
    88 				}
       
    89 			There must be at least 1 target.
       
    90 
       
    91 	optional fields:
       
    92 	- wind:		the initial wind (-100 to 100) (default: 0 (no wind))
       
    93 	- solidLand:	weather the terrain is indestructible (default: false)
       
    94 	- artillery:	if true, the hog can’t move (default: false)
       
    95 	- hogHat:	hat of the hedgehog (default: "NoHat")
       
    96 	- hogName:	name of the hedgehog (default: "Trainee")
       
    97 	- teamName:	name of the hedgehog’s team (default: "Training Team")
       
    98 	- teamGrave:	name of the hedgehog’s grave
       
    99 	- clanColor:	color of the (only) clan (default: 0xFF0204, which is a red tone)
       
   100 	- goalText:	A short string explaining the goal of the mission
       
   101 			(default: "Destroy all targets within the time!")
       
   102 	- shootText:	A string which says how many times the player shot, “%d” is replaced
       
   103 			by the number of shots. (default: "You have shot %d times.")
       
   104 ]]
       
   105 function TargetPracticeMission(params)
       
   106 	if params.hogHat == nil then params.hogHat = "NoHat" end
       
   107 	if params.hogName == nil then params.hogName = loc("Trainee") end
       
   108 	if params.teamName == nil then params.teamName = loc("Training Team") end
       
   109 	if params.goalText == nil then params.goalText = loc("Eliminate all targets before your time runs out.|You have unlimited ammo for this mission.") end
       
   110 	if params.shootText == nil then params.shootText = loc("You have shot %d times.") end
       
   111 	if params.clanColor == nil then params.clanColor = 0xFF0204 end
       
   112 	if params.teamGrave == nil then params.teamGrave= "Statue" end
       
   113 	if params.wind == nil then params.wind = 0 end
       
   114 
       
   115 	local solid, artillery
       
   116 	if params.solidLand == true then solid = gfSolidLand else solid = 0 end
       
   117 	if params.artillery == true then artillery = gfArtillery else artillery = 0 end
       
   118 
       
   119 	targets = params.targets
       
   120 
       
   121 	total_targets = #targets
       
   122 
       
   123 	_G.onAmmoStoreInit = function()
       
   124 		SetAmmo(params.ammoType, 9, 0, 0, 0)
       
   125 	end
       
   126 
       
   127 	_G.onGameInit = function()
       
   128 		Seed = 1
       
   129 		GameFlags = gfDisableWind + gfInfAttack + gfOneClanMode + solid + artillery
       
   130 		TurnTime = params.time
       
   131 		Map = params.map
       
   132 		Theme = params.theme
       
   133 		Goals = params.goalText
       
   134 		CaseFreq = 0
       
   135 		MinesNum = 0
       
   136 		Explosives = 0
       
   137 
       
   138 		SetWind(params.wind)
       
   139 
       
   140 		AddTeam(loc(params.teamName), params.clanColor, params.teamGrave, "Island", "Default", "Flowerhog")
       
   141 
       
   142 		player = AddHog(loc(params.hogName), 0, 1, params.hogHat)
       
   143 		SetGearPosition(player, params.hog_x, params.hog_y)
       
   144 	end
       
   145 
       
   146 	_G.onGameStart = function()
       
   147 		SendHealthStatsOff()
       
   148 		ShowMission(params.missionTitle, loc("Aiming practice"), params.goalText, -params.ammoType, 5000)
       
   149 		spawnTarget()
       
   150 	end
       
   151 
       
   152 	_G.onNewTurn = function()
       
   153 		-- TODO: Remove the else branch when 0.9.21 has been released
       
   154 		if SetWeapon ~= nil then
       
   155 			SetWeapon(params.ammoType)
       
   156 		else
       
   157 			ParseCommand("/setweap "..string.char(params.ammoType))
       
   158 		end
       
   159 	end
       
   160 
       
   161 	_G.spawnTarget = function()
       
   162 		gear = AddGear(0, 0, gtTarget, 0, 0, 0, 0)
       
   163 
       
   164 		x = targets[scored+1].x
       
   165 		y = targets[scored+1].y
       
   166 
       
   167 		SetGearPosition(gear, x, y)
       
   168 	end
       
   169 
       
   170 	_G.onGameTick20 = function()
       
   171 		if TurnTimeLeft < 40 and TurnTimeLeft > 0 and scored < total_targets and game_lost == false then
       
   172 			game_lost = true
       
   173 			AddCaption(loc("Time’s up!"), 0xFFFFFFFF, capgrpGameState)
       
   174 			ShowMission(params.missionTitle, loc("Aiming practice"), loc("Oh no! Time's up! Just try again."), -amSkip, 0)
       
   175 			SetHealth(player, 0)
       
   176 			time_goal = 1
       
   177 		end
       
   178 
       
   179 		if band(GetState(player), gstDrowning) == gstDrowning and game_lost == false and scored < total_targets then
       
   180 			game_lost = true
       
   181 			time_goal = 1
       
   182 			AddCaption(loc("You lose!"), 0xFFFFFFFF, capgrpGameState)
       
   183 			ShowMission(params.missionTitle, loc("Aiming practice"), loc("Oh no! You failed! Just try again."), -amSkip, 0)
       
   184 		end
       
   185 
       
   186 		if scored == total_targets  or game_lost then
       
   187 			if end_timer == 0 then
       
   188 				generateStats()
       
   189 				EndGame()
       
   190 			else
       
   191 				TurnTimeLeft = time_goal
       
   192 			end
       
   193 	        end_timer = end_timer - 20
       
   194 		end
       
   195 	end
       
   196 
       
   197 	_G.onGearAdd = function(gear)
       
   198 		if GetGearType(gear) == params.gearType then
       
   199 			shots = shots + 1
       
   200 		end
       
   201 	end
       
   202 
       
   203 	_G.onGearDamage = function(gear, damage)
       
   204 		if GetGearType(gear) == gtTarget then
       
   205 			scored = scored + 1
       
   206 			if scored < total_targets then
       
   207 				AddCaption(string.format(loc("Targets left: %d"), (total_targets-scored)), 0xFFFFFFFF, capgrpMessage)
       
   208 				spawnTarget()
       
   209 			else
       
   210 				if not game_lost then
       
   211 					AddCaption(loc("You have destroyed all targets!"), 0xFFFFFFFF, capgrpGameState)
       
   212 					ShowMission(params.missionTitle, loc("Aiming practice"), loc("Congratulations! You have destroyed all targets within the time."), 0, 0)
       
   213 					PlaySound(sndVictory, player)
       
   214 					SetState(player, bor(GetState(player), gstWinner))
       
   215 					time_goal = TurnTimeLeft
       
   216 				end
       
   217 			end
       
   218 		end
       
   219 
       
   220 		if GetGearType(gear) == gtHedgehog then
       
   221 			if not game_lost then
       
   222 				game_lost = true
       
   223 				AddCaption(loc("You lose!", 0xFFFFFFFF, capgrpGameState))
       
   224 				ShowMission(params.missionTitle, loc("Aiming practice"), loc("Oh no! You failed! Just try again."), -amSkip, 0)
       
   225 
       
   226 				SetHealth(player, 0)
       
   227 				time_goal = 1
       
   228 			end
       
   229 		end
       
   230 	end
       
   231 
       
   232 	_G.generateStats = function()
       
   233 		local accuracy = (scored/shots)*100
       
   234 		local end_score_targets = scored * math.ceil(6000/#targets)
       
   235 		local end_score_overall
       
   236 		if not game_lost then
       
   237 			local end_score_time = math.ceil(time_goal/(params.time/6000))
       
   238 			local end_score_accuracy = math.ceil(accuracy * 60)
       
   239 			end_score_overall = end_score_time + end_score_targets + end_score_accuracy
       
   240 
       
   241 			SendStat(siGameResult, loc("You have finished the target practice!"))
       
   242 
       
   243 			SendStat(siCustomAchievement, string.format(loc("You have destroyed %d of %d targets (+%d points)."), scored, total_targets, end_score_targets))
       
   244 			SendStat(siCustomAchievement, string.format(params.shootText, shots))
       
   245 			SendStat(siCustomAchievement, string.format(loc("Your accuracy was %.1f%% (+%d points)."), accuracy, end_score_accuracy))
       
   246 			SendStat(siCustomAchievement, string.format(loc("You had %.1fs remaining on the clock (+%d points)."), (time_goal/1000), end_score_time))
       
   247 		else
       
   248 			SendStat(siGameResult, loc("You lose!"))
       
   249 
       
   250 			SendStat(siCustomAchievement, string.format(loc("You have destroyed %d of %d targets (+%d points)."), scored, total_targets, end_score_targets))
       
   251 			SendStat(siCustomAchievement, string.format(params.shootText, shots))
       
   252 			if(shots > 0) then
       
   253 				SendStat(siCustomAchievement, string.format(loc("Your accuracy was %.1f%%."), accuracy))
       
   254 			end
       
   255 			end_score_overall = end_score_targets
       
   256 		end
       
   257 		SendStat(siPlayerKills, tostring(end_score_overall), loc(params.teamName))
       
   258 		SendStat(siPointType, loc("points"))
       
   259 	end
       
   260 end