Add (and slightly tweak) Wuzzy's Target Practice missions
authormikade <redgrinner@gmail.com>
Thu, 11 Dec 2014 21:00:42 +0900
changeset 10656 2093cf51eea1
parent 10655 1c85a442bd56
child 10657 f2507005be87
Add (and slightly tweak) Wuzzy's Target Practice missions
share/hedgewars/Data/Graphics/Missions/Training/Target_Practice_-_Bazooka_easy@2x.png
share/hedgewars/Data/Graphics/Missions/Training/Target_Practice_-_Bazooka_hard@2x.png
share/hedgewars/Data/Graphics/Missions/Training/Target_Practice_-_Cluster_Bomb@2x.png
share/hedgewars/Data/Graphics/Missions/Training/Target_Practice_-_Grenade_easy@2x.png
share/hedgewars/Data/Graphics/Missions/Training/Target_Practice_-_Grenade_hard@2x.png
share/hedgewars/Data/Graphics/Missions/Training/Target_Practice_-_Homing_Bee@2x.png
share/hedgewars/Data/Graphics/Missions/Training/Target_Practice_-_Shotgun@2x.png
share/hedgewars/Data/Locale/missions_en.txt
share/hedgewars/Data/Missions/Training/Target_Practice_-_Bazooka_easy.lua
share/hedgewars/Data/Missions/Training/Target_Practice_-_Bazooka_hard.lua
share/hedgewars/Data/Missions/Training/Target_Practice_-_Cluster_Bomb.lua
share/hedgewars/Data/Missions/Training/Target_Practice_-_Grenade_easy.lua
share/hedgewars/Data/Missions/Training/Target_Practice_-_Grenade_hard.lua
share/hedgewars/Data/Missions/Training/Target_Practice_-_Homing_Bee.lua
share/hedgewars/Data/Missions/Training/Target_Practice_-_Shotgun.lua
share/hedgewars/Data/Scripts/TargetPractice.lua
Binary file share/hedgewars/Data/Graphics/Missions/Training/Target_Practice_-_Bazooka_easy@2x.png has changed
Binary file share/hedgewars/Data/Graphics/Missions/Training/Target_Practice_-_Bazooka_hard@2x.png has changed
Binary file share/hedgewars/Data/Graphics/Missions/Training/Target_Practice_-_Cluster_Bomb@2x.png has changed
Binary file share/hedgewars/Data/Graphics/Missions/Training/Target_Practice_-_Grenade_easy@2x.png has changed
Binary file share/hedgewars/Data/Graphics/Missions/Training/Target_Practice_-_Grenade_hard@2x.png has changed
Binary file share/hedgewars/Data/Graphics/Missions/Training/Target_Practice_-_Homing_Bee@2x.png has changed
Binary file share/hedgewars/Data/Graphics/Missions/Training/Target_Practice_-_Shotgun@2x.png has changed
--- a/share/hedgewars/Data/Locale/missions_en.txt	Thu Dec 11 20:38:03 2014 +0900
+++ b/share/hedgewars/Data/Locale/missions_en.txt	Thu Dec 11 21:00:42 2014 +0900
@@ -1,53 +1,74 @@
-Basic_Training_-_Bazooka.name=Basic Bazooka Training
-Basic_Training_-_Bazooka.desc="Using the wind to your advantage is key!"
-
-Basic_Training_-_Grenade.name=Basic Grenade Training
-Basic_Training_-_Grenade.desc="Remember, you pull the pin out AND throw!"
-
-Basic_Training_-_Cluster_Bomb.name=Basic Cluster Bomb Training
-Basic_Training_-_Cluster_Bomb.desc="Someone needs hot shower!"
-
-Basic_Training_-_Shotgun.name=Basic Shotgun Training
-Basic_Training_-_Shotgun.desc="Shoot first, ask questions later!"
-
-Basic_Training_-_Sniper_Rifle.name=Basic Sniper Rifle Training
-Basic_Training_-_Sniper_Rifle.desc="Boom, headshot!"
-
-Basic_Training_-_Rope.name=Basic Rope Training
-Basic_Training_-_Rope.desc="Get out there and swing!"
-
-User_Mission_-_Dangerous_Ducklings.name=Mission: Dangerous Ducklings
-User_Mission_-_Dangerous_Ducklings.desc="Alright, rookie! Time to put what we learned in Basic Training into practice!"
-
-User_Mission_-_Diver.name=Mission: Diver
-User_Mission_-_Diver.desc="This 'amphibious assault' thing is harder than it looks..."
-
-User_Mission_-_Teamwork.name=Mission: Teamwork
-User_Mission_-_Teamwork.desc="Sometimes, love hurts."
-
-User_Mission_-_Spooky_Tree.name=Mission: Spooky Tree
-User_Mission_-_Spooky_Tree.desc="Lots of crates out here. I sure hope that bird ain't feeling hungry."
-
-User_Mission_-_Bamboo_Thicket.name=Mission: Bamboo Thicket
-User_Mission_-_Bamboo_Thicket.desc="Death comes from above."
-
-User_Mission_-_That_Sinking_Feeling.name=Mission: That Sinking Feeling
-User_Mission_-_That_Sinking_Feeling.desc="The water is rising rapidly and time is limited. Many have tried and failed. Can you save them all?"
-
-User_Mission_-_Newton_and_the_Hammock.name=Mission: Newton and the Hammock
-User_Mission_-_Newton_and_the_Hammock.desc="Remember hoglets: The velocity of a body remains constant unless the body is acted upon by an external force!"
-
-User_Mission_-_The_Great_Escape.name=Mission: The Great Escape
-User_Mission_-_The_Great_Escape.desc="You think you can cage me!?"
-
-User_Mission_-_Rope_Knock_Challenge.name=Challenge: Rope Knocking
-User_Mission_-_Rope_Knock_Challenge.desc="Look behind you!"
-
-User_Mission_-_Nobody_Laugh.name=Mission: Nobody Laugh
-User_Mission_-_Nobody_Laugh.desc="This ain't no joke."
-
-User_Mission_-_RCPlane_Challenge.name=Challenge: RC Plane
-User_Mission_-_RCPlane_Challenge.desc="Feeling pretty confident, eh, flyboy?"
-
-portal.name=Mission: Portal Mind Challenge
-portal.desc="Use the portal to move fast and far, use it to kill, use it with caution!"
+Basic_Training_-_Bazooka.name=Basic Bazooka Training
+Basic_Training_-_Bazooka.desc="Using the wind to your advantage is key!"
+
+Basic_Training_-_Grenade.name=Basic Grenade Training
+Basic_Training_-_Grenade.desc="Remember, you pull the pin out AND throw!"
+
+Basic_Training_-_Cluster_Bomb.name=Basic Cluster Bomb Training
+Basic_Training_-_Cluster_Bomb.desc="Someone needs hot shower!"
+
+Basic_Training_-_Shotgun.name=Basic Shotgun Training
+Basic_Training_-_Shotgun.desc="Shoot first, ask questions later!"
+
+Basic_Training_-_Sniper_Rifle.name=Basic Sniper Rifle Training
+Basic_Training_-_Sniper_Rifle.desc="Boom, headshot!"
+
+Basic_Training_-_Rope.name=Basic Rope Training
+Basic_Training_-_Rope.desc="Get out there and swing!"
+
+User_Mission_-_Dangerous_Ducklings.name=Mission: Dangerous Ducklings
+User_Mission_-_Dangerous_Ducklings.desc="Alright, rookie! Time to put what we learned in Basic Training into practice!"
+
+User_Mission_-_Diver.name=Mission: Diver
+User_Mission_-_Diver.desc="This 'amphibious assault' thing is harder than it looks..."
+
+User_Mission_-_Teamwork.name=Mission: Teamwork
+User_Mission_-_Teamwork.desc="Sometimes, love hurts."
+
+User_Mission_-_Spooky_Tree.name=Mission: Spooky Tree
+User_Mission_-_Spooky_Tree.desc="Lots of crates out here. I sure hope that bird ain't feeling hungry."
+
+User_Mission_-_Bamboo_Thicket.name=Mission: Bamboo Thicket
+User_Mission_-_Bamboo_Thicket.desc="Death comes from above."
+
+User_Mission_-_That_Sinking_Feeling.name=Mission: That Sinking Feeling
+User_Mission_-_That_Sinking_Feeling.desc="The water is rising rapidly and time is limited. Many have tried and failed. Can you save them all?"
+
+User_Mission_-_Newton_and_the_Hammock.name=Mission: Newton and the Hammock
+User_Mission_-_Newton_and_the_Hammock.desc="Remember hoglets: The velocity of a body remains constant unless the body is acted upon by an external force!"
+
+User_Mission_-_The_Great_Escape.name=Mission: The Great Escape
+User_Mission_-_The_Great_Escape.desc="You think you can cage me!?"
+
+User_Mission_-_Rope_Knock_Challenge.name=Challenge: Rope Knocking
+User_Mission_-_Rope_Knock_Challenge.desc="Look behind you!"
+
+User_Mission_-_Nobody_Laugh.name=Mission: Nobody Laugh
+User_Mission_-_Nobody_Laugh.desc="This ain't no joke."
+
+User_Mission_-_RCPlane_Challenge.name=Challenge: RC Plane
+User_Mission_-_RCPlane_Challenge.desc="Feeling pretty confident, eh, flyboy?"
+
+portal.name=Mission: Portal Mind Challenge
+portal.desc="Use the portal to move fast and far, use it to kill, use it with caution!"
+
+Target_Practice_-_Bazooka_easy.name=Target Practice: Bazooka (easy)
+Target_Practice_-_Bazooka_easy.desc="Alright, soldier, blow those targets up as fast as you can!"
+
+Target_Practice_-_Bazooka_hard.name=Target Practice: Bazooka (hard)
+Target_Practice_-_Bazooka_hard.desc="Can you hit the targets even when they are very far away?"
+
+Target_Practice_-_Cluster_Bomb.name=Target Practice: Cluster Bomb
+Target_Practice_-_Cluster_Bomb.desc="Someone needs hot shower!"
+
+Target_Practice_-_Shotgun.name=Target Practice: Shotgun
+Target_Practice_-_Shotgun.desc="Shoot first, ask questions later!"
+
+Target_Practice_-_Homing_Bee.name=Target Practice: Homing Bee
+Target_Practice_-_Homing_Bee.desc="Using the homing bee is trickier than it seems."
+
+Target_Practice_-_Grenade_easy.name=Target Practice: Grenade (easy)
+Target_Practice_-_Grenade_easy.desc="A warm-up training for the aspiring grenadier."
+
+Target_Practice_-_Grenade_hard.name=Target Practice: Grenade (hard)
+Target_Practice_-_Grenade_hard.desc="This is nothing for greenhorns! We will place the targets at some really tricky positions."
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/share/hedgewars/Data/Missions/Training/Target_Practice_-_Bazooka_easy.lua	Thu Dec 11 21:00:42 2014 +0900
@@ -0,0 +1,35 @@
+HedgewarsScriptLoad("/Scripts/TargetPractice.lua")
+
+local params = {
+	ammoType = amBazooka,
+	gearType = gtShell,
+	missionTitle = "Target Practice: Bazooka (easy)",
+	wind = 50,
+	solidLand = true,
+	map = "Lonely_Island",
+	theme = "Island",
+	hog_x = 1439,
+	hog_y = 482,
+	hogName = loc("Zook"),
+	hogHat = "war_americanww2helmet",
+	teamName = loc("Team Zook"),
+	targets = {
+		{ x = 1310, y = 756 },
+		{ x = 1281, y = 893 },
+		{ x = 1376, y = 670 },
+		{ x = 1725, y = 907 },
+		{ x = 1971, y = 914 },
+		{ x = 1098, y = 955 },
+		{ x = 1009, y = 877 },
+		{ x = 930, y = 711 },
+		{ x = 771, y = 744 },
+		{ x = 385, y = 405 },
+		{ x = 442, y = 780 },
+		{ x = 620, y = 639 },
+		{ x = 311, y = 239 },
+	},
+	time = 80000,
+	shootText = loc("You have launched %d bazookas."),
+}
+
+TargetPracticeMission(params)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/share/hedgewars/Data/Missions/Training/Target_Practice_-_Bazooka_hard.lua	Thu Dec 11 21:00:42 2014 +0900
@@ -0,0 +1,40 @@
+HedgewarsScriptLoad("/Scripts/TargetPractice.lua")
+
+local params = {
+	ammoType = amBazooka,
+	gearType = gtShell,
+	missionTitle = "Target Practice: Bazooka (hard)",
+	wind = 20,
+	solidLand = true,
+	artillery = true,
+	map = "SB_Grassy",
+	theme = "Castle",
+	hog_x = 2112,
+	hog_y = 913,
+	hogName = loc("Zook"),
+	hogHat = "war_americanww2helmet",
+	teamName = loc("Team Zook"),
+	targets = {
+		{ x = 2660, y = 936 },
+		{ x = 2857, y = 629 },
+		{ x = 3044, y = 851 },
+		{ x = 3200, y = 441 },
+		{ x = 2931, y = 440 },
+		{ x = 3264, y = 370 },
+		{ x = 1880, y = 684 },
+		{ x = 1392, y = 672 },
+		{ x = 1587, y = 656 },
+		{ x = 1300, y = 840 },
+		{ x = 952, y = 814 },
+		{ x = 862, y = 634 },
+		{ x = 748, y = 794 },
+		{ x = 639, y = 548 },
+		{ x = 1730, y = 549 },
+		{ x = 1466, y = 566 },
+		{ x = 3716, y = 954 },
+	},
+	time = 180000,
+	shootText = loc("You have launched %d bazookas."),
+}
+
+TargetPracticeMission(params)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/share/hedgewars/Data/Missions/Training/Target_Practice_-_Cluster_Bomb.lua	Thu Dec 11 21:00:42 2014 +0900
@@ -0,0 +1,33 @@
+HedgewarsScriptLoad("/Scripts/TargetPractice.lua")
+
+local params = {
+	ammoType = amClusterBomb,
+	gearType = gtClusterBomb,
+	missionTitle = "Cluster Bomb Training",
+	solidLand = false,
+	map = "Trash",
+	theme = "Golf",
+	hog_x = 756,
+	hog_y = 370,
+	hogName = loc("Private Nolak"),
+	hogHat = "war_desertgrenadier1",
+	teamName = loc("The Hogies"),
+	targets = {
+		{ x = 628, y = 0 },
+		{ x = 891, y = 0 },
+		{ x = 1309, y = 0 },
+		{ x = 1128, y = 0 },
+		{ x = 410, y = 0 },
+		{ x = 1564, y = 0 },
+		{ x = 1248, y = 476 },
+		{ x = 169, y = 0 },
+		{ x = 1720, y = 0 },
+		{ x = 1441, y = 0 },
+		{ x = 599, y = 0 },
+		{ x = 1638, y = 0 },
+	},
+	time = 180000,
+	shootText = loc("You have thrown %d cluster bombs."),
+}
+
+TargetPracticeMission(params)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/share/hedgewars/Data/Missions/Training/Target_Practice_-_Grenade_easy.lua	Thu Dec 11 21:00:42 2014 +0900
@@ -0,0 +1,30 @@
+HedgewarsScriptLoad("/Scripts/TargetPractice.lua")
+
+local params = {
+	ammoType = amGrenade,
+	gearType = gtGrenade,
+	missionTitle = "Target Practice: Grenade (easy)",
+	solidLand = true,
+	artillery = true,
+	map = "SB_Crystal",
+	theme = "Cave",
+	hog_x = 2039,
+	hog_y = 684,
+	hogName = loc("Grenadier"),
+	hogHat = "war_desertgrenadier2",
+	teamName = loc("Grenade Group"),
+	targets = {
+		{ x = 1834, y = 747 },
+		{ x = 2308, y = 729 },
+		{ x = 1659, y = 718 },
+		{ x = 1196, y = 704 },
+		{ x = 2650, y = 826 },
+		{ x = 1450, y = 705 },
+		{ x = 2774, y = 848 },
+		{ x = 2970, y = 704 },
+	},
+	time = 80000,
+	shootText = loc("You have thrown %d grenades."),
+}
+
+TargetPracticeMission(params)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/share/hedgewars/Data/Missions/Training/Target_Practice_-_Grenade_hard.lua	Thu Dec 11 21:00:42 2014 +0900
@@ -0,0 +1,41 @@
+HedgewarsScriptLoad("/Scripts/TargetPractice.lua")
+
+local params = {
+	ammoType = amGrenade,
+	gearType = gtGrenade,
+	missionTitle = "Target Practice: Grenade (hard)",
+	solidLand = true,
+	artillery = true,
+	map = "SB_Crystal",
+	theme = "Cave",
+	hog_x = 1456,
+	hog_y = 669,
+	hogName = loc("Grenadier"),
+	hogHat = "war_desertgrenadier2",
+	teamName = loc("Grenade Group"),
+	targets = {
+		{ x = 1190, y = 694 },
+		{ x = 962, y = 680 },
+		{ x = 1090, y = 489 },
+		{ x = 1664, y = 666 },
+		{ x = 1584, y = 580 },
+		{ x = 2160, y = 738 },
+		{ x = 1836, y = 726 },
+		{ x = 618, y = 753 },
+		{ x = 837, y = 668 },
+		{ x = 2424, y = 405 },
+		{ x = 2310, y = 742 },
+		{ x = 294, y = 897 },
+		{ x = 472, y = 855 },
+		{ x = 2949, y = 724},
+		{ x = 3356, y = 926 },
+		{ x = 3734, y = 918 },
+		{ x = 170, y = 874 },
+		
+		
+	},
+	time = 180000,
+	shootText = loc("You have thrown %d grenades."),
+}
+
+TargetPracticeMission(params)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/share/hedgewars/Data/Missions/Training/Target_Practice_-_Homing_Bee.lua	Thu Dec 11 21:00:42 2014 +0900
@@ -0,0 +1,35 @@
+HedgewarsScriptLoad("/Scripts/TargetPractice.lua")
+
+local params = {
+	ammoType = amBee,
+	gearType = gtBee,
+	missionTitle = "Target Practice: Homing Bee",
+	solidLand = true,
+	map = "Hedgewars",
+	theme = "Nature",
+	hog_x = 1990,
+	hog_y = 514,
+	hogHat = "NoHat",
+	teamGrave = "bp2",
+	targets = {
+		{ x = 1949, y = 273 },
+		{ x = 1734, y = 322 },
+		{ x = 1574, y = 340 },
+		{ x = 1642, y = 474 },
+		{ x = 2006, y = 356 },
+		{ x = 1104, y = 285 },
+		{ x = 565, y = 440 },
+		{ x = 732, y = 350 },
+		{ x = 2022, y = 396 },
+		{ x = 366, y = 360 },
+		{ x = 556, y = 300 },
+		{ x = 902, y = 306 },
+		{ x = 924, y = 411 },
+		{ x = 227, y = 510 },
+		{ x = 150, y = 300 },
+	},
+	time = 120000,
+	shootText = loc("You have launched %d homing bees."),
+}
+
+TargetPracticeMission(params)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/share/hedgewars/Data/Missions/Training/Target_Practice_-_Shotgun.lua	Thu Dec 11 21:00:42 2014 +0900
@@ -0,0 +1,38 @@
+HedgewarsScriptLoad("/Scripts/TargetPractice.lua")
+
+local params = {
+	ammoType = amShotgun,
+	gearType = gtShotgunShot,
+	missionTitle = "Target Practice: Shotgun",
+	solidLand = false,
+	map = "SB_Haunty",
+	theme = "Halloween",
+	hog_x = 320,
+	hog_y = 324,
+	hogHat = "NoHat",
+	hogGrave = "Bones",
+	targets = {
+		{ x = 495, y = 501 },
+		{ x = 227, y = 530 },
+		{ x = 835, y = 934 },
+		{ x = 1075, y = 889 },
+		{ x = 887, y = 915 },
+		{ x = 1148, y = 750 },
+		{ x = 916, y = 915 },
+		{ x = 1211, y = 700 },
+		{ x = 443, y = 505 },
+		{ x = 822, y = 964 },
+		{ x = 1092, y = 819 },
+		{ x = 1301, y = 683 },
+		{ x = 1480, y = 661 },
+		{ x = 1492, y = 786 },
+		{ x = 1605, y = 562 },
+		{ x = 1545, y = 466 },
+		{ x = 1654, y = 392 },
+		{ x = 1580, y = 334 },
+		{ x = 1730, y = 222 },
+	},
+	time = 90000,
+}
+
+TargetPracticeMission(params)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/share/hedgewars/Data/Scripts/TargetPractice.lua	Thu Dec 11 21:00:42 2014 +0900
@@ -0,0 +1,260 @@
+--[=[
+Target Practice Mission Framework for Hedgewars
+
+This is a simple library intended to make setting up simple training missions a trivial
+task requiring just. The library has been created to reduce redundancy in Lua scripts.
+
+The training framework generates complete and fully usable training missions by just
+one function call.
+
+The missions generated by this script are all the same:
+- The player will get a team with a single hedgehog.
+- The team gets a single predefined weapon infinitely times.
+- A fixed sequence of targets will spawn at predefined positions.
+- When a target has been destroyed, the next target of the target sequence appears
+- The mission ends successfully when all targets have been destroyed
+- The mission ends unsuccessfully when the time runs out or the hedgehog dies
+- When the mission ends, a score is awarded, based on the performance (hit targets,
+  accuracy and remaining time) of the hedgehog. When not all targets are hit, there
+  will be no accuracy and time bonuses.
+
+To use this library, you first have to load it and to call TrainingMission once with
+the appropriate parameters. Really, that’s all!
+See the comment of TrainingMission for a specification of all parameters.
+
+Below is a template for your convenience, you just have to fill in the fields and delete
+optional arguments you don’t want.
+----- snip -----
+HedgewarsScriptLoad("/Scripts/Training.lua")
+params = {
+	missionTitle = ,
+	map = ,
+	theme = ,
+	time = ,
+	ammoType = ,
+	gearType = ,
+	targets = {
+		{ x = , y = },
+		{ x = , y = },
+		-- etc.
+	},
+
+	wind = ,
+	solidLand = ,
+	artillery = ,
+	hogHat = ,
+	hogName = ,
+	teamName = ,
+	teamGrave = ,
+	clanColor = ,
+	goalText = ,
+	shootText =
+}
+TargetPracticeMission(params)
+----- snip -----
+]=]
+
+HedgewarsScriptLoad("/Scripts/Locale.lua")
+
+local player = nil
+local scored = 0
+local shots = 0
+local end_timer = 1000
+local game_lost = false
+local time_goal = 0
+local total_targets
+local targets
+
+--[[
+TrainingMission(params)
+
+This function sets up the *entire* training mission and needs one argument: params.
+The argument “params” is a table containing fields which describe the training mission.
+	mandatory fields:
+	- missionTitle:	the name of the mission
+	- map:		the name map to be used
+	- theme:	the name of the theme (does not need to be a standalone theme)
+	- time:		the time limit in milliseconds
+	- ammoType:	the ammo type of the weapon to be used
+	- gearType:	the gear type of the gear which is fired (used to count shots)
+	- targets:	The coordinates of where the targets will be spawned.
+			It is a table containing tables containing coordinates of format
+			{ x=value, y=value }. The targets will be spawned in the same
+			order as specified the coordinate tables appear. Example:
+				targets = {
+					{ x = 324, y = 43 },
+					{ x = 123, y = 56 },
+					{ x = 6, y = 0 },
+				}
+			There must be at least 1 target.
+
+	optional fields:
+	- wind:		the initial wind (-100 to 100) (default: 0 (no wind))
+	- solidLand:	weather the terrain is indestructible (default: false)
+	- artillery:	if true, the hog can’t move (default: false)
+	- hogHat:	hat of the hedgehog (default: "NoHat")
+	- hogName:	name of the hedgehog (default: "Trainee")
+	- teamName:	name of the hedgehog’s team (default: "Training Team")
+	- teamGrave:	name of the hedgehog’s grave
+	- clanColor:	color of the (only) clan (default: 0xFF0204, which is a red tone)
+	- goalText:	A short string explaining the goal of the mission
+			(default: "Destroy all targets within the time!")
+	- shootText:	A string which says how many times the player shot, “%d” is replaced
+			by the number of shots. (default: "You have shot %d times.")
+]]
+function TargetPracticeMission(params)
+	if params.hogHat == nil then params.hogHat = "NoHat" end
+	if params.hogName == nil then params.hogName = loc("Trainee") end
+	if params.teamName == nil then params.teamName = loc("Training Team") end
+	if params.goalText == nil then params.goalText = loc("Eliminate all targets before your time runs out.|You have unlimited ammo for this mission.") end
+	if params.shootText == nil then params.shootText = loc("You have shot %d times.") end
+	if params.clanColor == nil then params.clanColor = 0xFF0204 end
+	if params.teamGrave == nil then params.teamGrave= "Statue" end
+	if params.wind == nil then params.wind = 0 end
+
+	local solid, artillery
+	if params.solidLand == true then solid = gfSolidLand else solid = 0 end
+	if params.artillery == true then artillery = gfArtillery else artillery = 0 end
+
+	targets = params.targets
+
+	total_targets = #targets
+
+	_G.onAmmoStoreInit = function()
+		SetAmmo(params.ammoType, 9, 0, 0, 0)
+	end
+
+	_G.onGameInit = function()
+		Seed = 1
+		GameFlags = gfDisableWind + gfInfAttack + gfOneClanMode + solid + artillery
+		TurnTime = params.time
+		Map = params.map
+		Theme = params.theme
+		Goals = params.goalText
+		CaseFreq = 0
+		MinesNum = 0
+		Explosives = 0
+
+		SetWind(params.wind)
+
+		AddTeam(loc(params.teamName), params.clanColor, params.teamGrave, "Island", "Default", "Flowerhog")
+
+		player = AddHog(loc(params.hogName), 0, 1, params.hogHat)
+		SetGearPosition(player, params.hog_x, params.hog_y)
+	end
+
+	_G.onGameStart = function()
+		SendHealthStatsOff()
+		ShowMission(params.missionTitle, loc("Aiming practice"), params.goalText, -params.ammoType, 5000)
+		spawnTarget()
+	end
+
+	_G.onNewTurn = function()
+		-- TODO: Remove the else branch when 0.9.21 has been released
+		if SetWeapon ~= nil then
+			SetWeapon(params.ammoType)
+		else
+			ParseCommand("/setweap "..string.char(params.ammoType))
+		end
+	end
+
+	_G.spawnTarget = function()
+		gear = AddGear(0, 0, gtTarget, 0, 0, 0, 0)
+
+		x = targets[scored+1].x
+		y = targets[scored+1].y
+
+		SetGearPosition(gear, x, y)
+	end
+
+	_G.onGameTick20 = function()
+		if TurnTimeLeft < 40 and TurnTimeLeft > 0 and scored < total_targets and game_lost == false then
+			game_lost = true
+			AddCaption(loc("Time’s up!"), 0xFFFFFFFF, capgrpGameState)
+			ShowMission(params.missionTitle, loc("Aiming practice"), loc("Oh no! Time's up! Just try again."), -amSkip, 0)
+			SetHealth(player, 0)
+			time_goal = 1
+		end
+
+		if band(GetState(player), gstDrowning) == gstDrowning and game_lost == false and scored < total_targets then
+			game_lost = true
+			time_goal = 1
+			AddCaption(loc("You lose!"), 0xFFFFFFFF, capgrpGameState)
+			ShowMission(params.missionTitle, loc("Aiming practice"), loc("Oh no! You failed! Just try again."), -amSkip, 0)
+		end
+
+		if scored == total_targets  or game_lost then
+			if end_timer == 0 then
+				generateStats()
+				EndGame()
+			else
+				TurnTimeLeft = time_goal
+			end
+	        end_timer = end_timer - 20
+		end
+	end
+
+	_G.onGearAdd = function(gear)
+		if GetGearType(gear) == params.gearType then
+			shots = shots + 1
+		end
+	end
+
+	_G.onGearDamage = function(gear, damage)
+		if GetGearType(gear) == gtTarget then
+			scored = scored + 1
+			if scored < total_targets then
+				AddCaption(string.format(loc("Targets left: %d"), (total_targets-scored)), 0xFFFFFFFF, capgrpMessage)
+				spawnTarget()
+			else
+				if not game_lost then
+					AddCaption(loc("You have destroyed all targets!"), 0xFFFFFFFF, capgrpGameState)
+					ShowMission(params.missionTitle, loc("Aiming practice"), loc("Congratulations! You have destroyed all targets within the time."), 0, 0)
+					PlaySound(sndVictory, player)
+					SetState(player, bor(GetState(player), gstWinner))
+					time_goal = TurnTimeLeft
+				end
+			end
+		end
+
+		if GetGearType(gear) == gtHedgehog then
+			if not game_lost then
+				game_lost = true
+				AddCaption(loc("You lose!", 0xFFFFFFFF, capgrpGameState))
+				ShowMission(params.missionTitle, loc("Aiming practice"), loc("Oh no! You failed! Just try again."), -amSkip, 0)
+
+				SetHealth(player, 0)
+				time_goal = 1
+			end
+		end
+	end
+
+	_G.generateStats = function()
+		local accuracy = (scored/shots)*100
+		local end_score_targets = scored * math.ceil(6000/#targets)
+		local end_score_overall
+		if not game_lost then
+			local end_score_time = math.ceil(time_goal/(params.time/6000))
+			local end_score_accuracy = math.ceil(accuracy * 60)
+			end_score_overall = end_score_time + end_score_targets + end_score_accuracy
+
+			SendStat(siGameResult, loc("You have finished the target practice!"))
+
+			SendStat(siCustomAchievement, string.format(loc("You have destroyed %d of %d targets (+%d points)."), scored, total_targets, end_score_targets))
+			SendStat(siCustomAchievement, string.format(params.shootText, shots))
+			SendStat(siCustomAchievement, string.format(loc("Your accuracy was %.1f%% (+%d points)."), accuracy, end_score_accuracy))
+			SendStat(siCustomAchievement, string.format(loc("You had %.1fs remaining on the clock (+%d points)."), (time_goal/1000), end_score_time))
+		else
+			SendStat(siGameResult, loc("You lose!"))
+
+			SendStat(siCustomAchievement, string.format(loc("You have destroyed %d of %d targets (+%d points)."), scored, total_targets, end_score_targets))
+			SendStat(siCustomAchievement, string.format(params.shootText, shots))
+			if(shots > 0) then
+				SendStat(siCustomAchievement, string.format(loc("Your accuracy was %.1f%%."), accuracy))
+			end
+			end_score_overall = end_score_targets
+		end
+		SendStat(siPlayerKills, tostring(end_score_overall), loc(params.teamName))
+		SendStat(siPointType, loc("points"))
+	end
+end