share/hedgewars/Data/Scripts/Multiplayer/WxW.lua
changeset 12065 7df7c58ea965
parent 11301 fa18940f290d
child 12067 18677a537d58
equal deleted inserted replaced
12064:54f53cf14ae4 12065:7df7c58ea965
     1 
     1 
     2 ----------------------
     2 ----------------------
     3 -- WALL TO WALL 0.4
     3 -- WALL TO WALL 0.7
     4 ----------------------
     4 ----------------------
     5 -- a shoppa minigame
     5 -- a shoppa minigame
     6 -- by mikade
     6 -- by mikade
     7 
     7 
     8 -- feel free to add map specific walls to LoadConfig, or post additional
     8 -- feel free to add map specific walls to LoadConfig, or post additional
    42 -- numerous improvements to user feedback
    42 -- numerous improvements to user feedback
    43 -- walls disappear after being touched
    43 -- walls disappear after being touched
    44 -- added backwards compatibility with 0.9.17
    44 -- added backwards compatibility with 0.9.17
    45 
    45 
    46 ----------------
    46 ----------------
    47 --TO DO
    47 --0.5
       
    48 ----------------
       
    49 -- Support for multiple sets of walls per map (instead of “all or nothing”)
       
    50 -- Ropes, ShoppaKing, ShoppaHell and ShoppaNeon can now be played with the classic left and right walls
       
    51 -- New wall sets for Ropes, ShoppaNeon, ShoppaDesert, ShoppaWild, ShoppaKing and ShoppaHell, and more.
       
    52 -- Basic support for a bunch of Shoppa maps
       
    53 -- Alternative configuration method with Script parameter
       
    54 -- Possible to set max. number of weapons in game (script parameter only)
       
    55 -- Possible to set number of crates per turn
       
    56 -- Menu can be disabled (with script parameter) for insant game start
       
    57 -- WxW is now fully functional even without a map border.
       
    58 -- WxW now allows for almost all game modifiers and game settings to be changed
       
    59 -- More sound effects
       
    60 -- No smoke when hog is near near a WxW wall but Walls Before Crate rule is not in place
       
    61 -- More readable mission display after configuration has been accepted
       
    62 -- Hide “Surf Before Crate” setting if surfing is disabled for this map, or the bottom is active and water never rises
       
    63 -- Hide walls setting if script does not provide walls for map yet
       
    64 -- Bugfix: Other player was able to change the menu config in the short period before the first "turn"
       
    65 -- Lots of refactoring
       
    66 
       
    67 ----------------
       
    68 --0.6
       
    69 ----------------
       
    70 -- Bugfix: 2 crates spawned at the 1st turn if script parameter was set to “menu=false, walls=none” or similar
       
    71 -- Bugfix: Annoying faulty error message appeared when hitting attack when on a rope with a weapon selected
       
    72 
       
    73 
       
    74 ----------------
       
    75 --0.7
       
    76 ----------------
       
    77 -- To enforce the rules more strictly, all crates will be frozen at turn start if WBC or SBC rule is in place.
       
    78 --	The crates are unfrozen if you met the crate criteria (that is, surfed and/or bounced off all walls).
       
    79 --      Frozen crates can't be collected and appear as small white dots in the radar.
       
    80 -- Add support for the “Crate Before Attack” rule
       
    81 -- Add support for the “All But Last” rule
       
    82 -- Add support for the “Kill The Leader” rule
       
    83 -- Allow toggling crate radar with “switch hog” key while roping
       
    84 -- The game continues now with the first team after the menu has been closed (rather than the second team)
       
    85 
       
    86 ----------------
       
    87 --TODO
    48 ----------------
    88 ----------------
    49 -- achievements / try detect shoppa moves? :|
    89 -- achievements / try detect shoppa moves? :|
    50 -- maybe add ability for the user to place zones like in Racer?
    90 -- maybe add ability for the user to place zones like in Racer?
    51 -- add more hard-coded values for specific maps
    91 -- add more hard-coded values for specific maps
    52 
    92 
       
    93 
       
    94 --[[
       
    95 # CONFIGURATION
       
    96 
       
    97 By default, this script is easily configured via the in-game menu. The player of the first team can choose the rules and
       
    98 required walls (or none at all). After accepted, the game will start with the second team (!).
       
    99 
       
   100 = SCRIPT PARAMETER =
       
   101 
       
   102 Using the script parameter is optional, it mostly is just an alternative way for configuration and for convenience
       
   103 reasons, so often-used configurations can be saved and loaded.
       
   104 
       
   105 The script parameter is specified as a comma-sperated list of “key=value” pairs (see examples below).
       
   106 
       
   107 Truth values can be set true or false, and numeric values always use a whole number.
       
   108 
       
   109 == Basic parameters ==
       
   110 
       
   111 key		default	description
       
   112 ----------------------------------------
       
   113 menu		true	Show configuration menu at the beginning. If no menu is used, a random wall set is used (see wall filters below)
       
   114 SBC		false	Surf Before Crate: Player must bounce off the water (“surfing”) before crates can be collected
       
   115 AFR		false	Attack From Rope: Players must attack from the rope. Weapons which can't be fired from rope are removed
       
   116 CBA		false	Crate Before Attack: Player must collect at least one crate before attacking
       
   117 attackrule	off	If present, enable one of the attack rules “ABL” or “KTL”:
       
   118 			ABL: All But Last: Players must not only attack the team with the lowest total health
       
   119 			KTL: Kill The Leader: If players hit some enemy hedgehog, at least one of them must be a hog from
       
   120 			the team with the highest total health.
       
   121 			The ABL and KTL rules exclude each other. If a player breaks the rule (if enabled), he must
       
   122 			skip in the next round.
       
   123 SW		false	Super Weapons: A few crates may contain very powerful weapons (melon, hellish grenade, RC plane, ballgun)
       
   124 maxcrates	12	Number of crates which can be at maximum in the game (limited to up to 100 to avoid lag)
       
   125 cratesperturn	1	Number of crates which appear each turn
       
   126 
       
   127 == Advanced parameters ==
       
   128 
       
   129 Wall filters: The following parameters allow you to filter out wall sets based on their type and number of walls.
       
   130 If this is used together with the menu, the filtered wall sets can't be selected. Without a menu, the wall set
       
   131 will be randomly selected among the wall sets that meet all criteria.
       
   132 
       
   133 If the criteria filter out all available wall sets of the map, the game is played without the Walls Before Crate rule.
       
   134 
       
   135 parameter	default	description
       
   136 ----------------------------------------
       
   137 walls		N/A	
       
   138 
       
   139 Permitted values:
       
   140 - leftright:		The left and right part of the border. Traditional W2W-style.
       
   141 - roof:			Only the top part of the border
       
   142 - leftrightroof:	Combination of the two above
       
   143 - inside:		Map-specific wall set where all walls are part of the terrain
       
   144 - mixed:		Map-specific wall set where some walls are part of the terrain, and some are part of the map border
       
   145 - none:			No walls required.
       
   146 - all:			Shorthand: All wall sets are allowed.
       
   147 
       
   148 Combination of multiple types is possible by concatenating the names with plus signs (see examples below).
       
   149 
       
   150 
       
   151 Restrict wall numbers: With the following parameters you can restrict the pool of wall sets to only those with a certain
       
   152 number of walls. Note that 2 walls are the most common type of wall set, as this is often available by default.
       
   153 
       
   154 parameter	default	description
       
   155 ----------------------------------------
       
   156 minwalls	N/A	Filter out wall sets with less than this
       
   157 maxwalls	N/A	Filter out wall sets with more than this
       
   158 
       
   159 wallsnum	N/A	Shorthand: Combintion of minwalls and maxwalls if they are the equal.
       
   160 
       
   161 
       
   162 == Examples ==
       
   163 
       
   164 
       
   165 SBC=true
       
   166 --> Keep the menu, enable Surf Before Crate by default (if available).
       
   167 
       
   168 SBC=true, menu=false
       
   169 --> Enable Surf Before Crate (if available) and use the defaul walls set.
       
   170 
       
   171 AFR=true, menu=false, wallsnum=2
       
   172 --> Attack From Rope rule active, and use a random wall set with 2 walls
       
   173 
       
   174 menu=false, walls=leftright
       
   175 --> Always use the classic left/right wall set automatically. Traditional W2W-style.
       
   176 
       
   177 walls=none, menu=false
       
   178 --> Like classic Shoppa
       
   179 
       
   180 walls=leftright+inside+mixed, menu=false
       
   181 --> Randomly use either the left/right wall set, an Inside or Mixed wall set.
       
   182 
       
   183 
       
   184 
       
   185 = MORE GAME SCHEME CONFIGURATION =
       
   186 You can almost set everything in the game scheme freely, and the script will work just fine together with it.
       
   187 Feel free to experiment a bit.
       
   188 The only exception are the crate frequencies. Setting them has no effect, crates are handled uniquiely in this game.
       
   189 
       
   190 At this stage, the script does not allow for custom weapon sets.
       
   191 ]]
       
   192 
       
   193 
       
   194 
    53 -----------------------------
   195 -----------------------------
    54 -- GO PONIES, GO PONIES, GO!
   196 -- GO PONIES, GO PONIES, GO!
    55 -----------------------------
   197 -----------------------------
    56 
   198 
    57 HedgewarsScriptLoad("/Scripts/Locale.lua")
   199 HedgewarsScriptLoad("/Scripts/Locale.lua")
    58 HedgewarsScriptLoad("/Scripts/Tracker.lua")
   200 HedgewarsScriptLoad("/Scripts/Tracker.lua")
    59 HedgewarsScriptLoad("/Scripts/Utils.lua")
   201 HedgewarsScriptLoad("/Scripts/Utils.lua")
    60 
   202 HedgewarsScriptLoad("/Scripts/Params.lua")
    61 -- experimental menu stuff
   203 
       
   204 -- HARDCODED values
       
   205 local ammoTypesNum = 58	-- number of weapon types (permanent TODO: Check this number for each Hedgewars version)
       
   206 local PlacementTime = 15000
       
   207 
       
   208 -- menu stuff
    62 local menuIndex = 1
   209 local menuIndex = 1
    63 local menu = {}
   210 local menu = {}
    64 local preMenuCfg
   211 local preMenuCfg
    65 local postMenuCfg
   212 local postMenuCfg
       
   213 
       
   214 --[[ WxW preparation phase.
       
   215 0 = Game not started yet
       
   216 1 = Configuration phase
       
   217 2 = Hedgehog placement phase
       
   218 100 = Game phase
       
   219 ]]
    66 local roundN = 0
   220 local roundN = 0
    67 
   221 
       
   222 -- Used to select one of the wall sets
       
   223 -- 0: no walls
       
   224 -- 1 and above: ID of wall sets
       
   225 local wallSetID = 0
       
   226 
       
   227 -- Store the wall sets here
       
   228 local wallSets = {}
       
   229 
       
   230 -- Wall set types and wall number limits for filtering
       
   231 local allWallSetTypes = {"roof", "leftright", "leftrightroof", "mixed", "inside"}
       
   232 local allowedWallSetTypes = {roof=true, leftright=true, leftrightroof=true, mixed=true, inside=true}
       
   233 local minWalls, maxWalls = nil, nil
       
   234 
    68 -- config and wall variables
   235 -- config and wall variables
    69 local AFR = false
   236 local useMenu = true
    70 local allowCrazyWeps = false
   237 local AFR = false		-- Attack From Rope
    71 local requireSurfer = true
   238 local WBC = true		-- Wall(s) Before Crate, will later only be set again in script parameter
       
   239 local CBA = false		-- Crate Before Attack
       
   240 local attackRule = nil		-- Either nil, "KTL" (Kill The Leader) or "ABL" (All But Last)
       
   241 local allowCrazyWeps = false	-- Super weapons
       
   242 local requireSurfer = false	-- Surf Before Crate
       
   243 local crateSpawned = false	-- Has the crate (or crates) been spawned in this turn yet?
       
   244 local cratesPerTurn = 1		-- How many crates appear per turn (respects crate limit)
       
   245 local maxCrates = 12		-- default crate limit, can be configured with params
       
   246 local maxCratesHard = 100	-- "hard" crate limit, to avoid extreme lagging due to many crates
       
   247 local crateGearsInGame = 0
    72 local wX = {}
   248 local wX = {}
    73 local wY = {}
   249 local wY = {}
    74 local wWidth = {}
   250 local wWidth = {}
    75 local wHeight = {}
   251 local wHeight = {}
    76 local wTouched = {}
   252 local wTouched = {}
    77 --local margin
       
    78 local wallsLeft = 0
   253 local wallsLeft = 0
    79 
   254 
    80 local hasSurfed = false
   255 local hasSurfed = false
    81 local allWallsHit = false
   256 local allWallsHit = false
       
   257 local crateCollected = false
       
   258 
       
   259 -- ABL and KTL stuff
       
   260 local teamNames = {}		-- List of all teams
       
   261 local teamsAttacked = {}	-- List of attacked teams (in this turn)
       
   262 local lastTeam = nil		-- Team with the least health. Determined only at start of turn. If it's a tie, use nil.
       
   263 local leaderTeam = nil		-- Team with the most health. Determined only at start of turn. If it's a tie, use nil.
       
   264 local runnerUpTeam = nil	-- Team with the second-most health
       
   265 local previousTeam = nil	-- Remember the name of the team in the previous turn
    82 
   266 
    83 local gTimer = 1
   267 local gTimer = 1
    84 local effectTimer = 1
   268 local effectTimer = 1
    85 
   269 
    86 local ropeG = nil
   270 local ropeG = nil
    87 local crateG = nil
       
    88 local allowCrate = true
   271 local allowCrate = true
       
   272 local crates = {}
       
   273 
       
   274 -- Variables for place hedgehogs mode
       
   275 local hogCount = 0		-- Used to detect the end of the hog placement phase
       
   276 local turnsCount = 0
    89 
   277 
    90 -- crate radar vars
   278 -- crate radar vars
       
   279 
       
   280 -- Set the initial radar mode here
       
   281 -- 0: Radar is always active
       
   282 -- 1: Radar is only active shortly after crate spawn
       
   283 -- 2: Radar is disabled
       
   284 local radarMode = 0
       
   285 
    91 local rCirc = {}
   286 local rCirc = {}
    92 local rAlpha = 255
   287 local rAlpha = 255
    93 local rPingTimer = 0
   288 local rPingTimer = 0
    94 local m2Count = 0
   289 local m2Count = 0
    95 
   290 
    96 local weapons = {}
   291 local weapons = {}
    97 
   292 
    98 --[[local unlisted = {amTardis, amLandGun,amExtraTime,amExtraDamage,
       
    99 				amVampiric, amSwitch, amInvulnerable, amGirder, amJetpack,
       
   100 				amPortalGun, amTeleport, amResurrector, amLaserSight, amLowGravity,
       
   101 				amAirAttack, amNapalm, amMineStrike, amDrillStrike,
       
   102 				amKamikaze, amSnowball, amSeduction}]]
       
   103 
       
   104 local crazyWeps = {amWatermelon, amHellishBomb, amBallgun, amRCPlane}
   293 local crazyWeps = {amWatermelon, amHellishBomb, amBallgun, amRCPlane}
   105 
   294 
   106 local groundWeps = 	{amBee, amShotgun,amDEagle,amFirePunch, amWhip,
   295 local groundWeps = 	{amBee, amShotgun,amDEagle,amFirePunch, amWhip,
   107 				amPickHammer, amBaseballBat, amCake,amBallgun,
   296 				amPickHammer, amBaseballBat, amCake,amBallgun,
   108 				amRCPlane, amSniperRifle, amBirdy, amBlowTorch, amGasBomb,
   297 				amRCPlane, amSniperRifle, amBirdy, amBlowTorch,
   109 				amFlamethrower, amSMine, amMortar, amHammer}
   298 				amFlamethrower, amMortar, amHammer}
   110 
   299 
   111 local ropeWeps = {amGrenade, amClusterBomb, amBazooka, amMine, amDynamite,
   300 local ropeWeps = {amGrenade, amClusterBomb, amBazooka, amMine, amDynamite,
   112 				amWatermelon, amHellishBomb, amDrill, amMolotov}
   301 				amWatermelon, amHellishBomb, amDrill, amMolotov,
       
   302 				amSMine, amGasBomb}
       
   303 
       
   304 local msgColorTech = 0xFFBA00FF
       
   305 local msgColorWarn = 0xFF4000FF
   113 
   306 
   114 -- 0.9.18+ extra custom data for preset maps
   307 -- 0.9.18+ extra custom data for preset maps
   115 local MapList =
   308 local MapList =
   116 	{
   309 	{
   117 	--name,      						surfer, roof, 	LRwalls
   310 	--name,					surfer, roof, 	LRwalls
   118 	{"Atlantis Shoppa", 			    true, 	false, true},
   311 	{"Alien",				true, 	true,  true},
   119 	{"BambooPlinko", 				    true,	false, true},
   312 	{"Atlantis Shoppa",			true, 	true,  true},
   120 	{"BrickShoppa", 				    false, 	false, true},
   313 	{"BasketballField",			false,  false, false},
   121 	{"BubbleFlow",   					true, 	false, true},
   314 	{"BattleCity_v1",			true,	true, true},
   122 	{"Cave",       						false, 	false, true},
   315 	{"BIGshoppa",				true,	true, true},
   123 	{"Glass Shoppa",      				true, 	false, true},
   316 	{"BambooPlinko",			true,	false, true},
   124 	{"HardIce",      					false, 	false, true},
   317 	{"BoatWxW",				true,	true,  true},
   125 	{"Industrial",       				false,	false, true},
   318 	{"BrickShoppa",				false, 	false, true},
   126 	{"Islands",       					true, 	false, true},
   319 	{"BubbleFlow",				true, 	false, true},
   127 	{"Hedgelove",       				true, 	false, true},
   320 	{"Citrouille",				true, 	true,  true},
   128 	{"NeonStyle",       				false, 	false, true},
   321 	{"Cave",				false, 	false, true},
   129 	{"Octorama",       					false, 	false, true},
   322 	{"Cheese_Ropes", 			false, 	true,  true},
       
   323 	{"CookieShoppa", 			true, 	false, true},
       
   324 	{"CrossRopes",				false,	false, true},
       
   325 	{"FutuShoppa",				true,	false, true},
       
   326 	{"Garden",				false,	false, true},
       
   327 	{"Glass Shoppa",			true, 	false, true},
       
   328 	{"GlassShoppa2",			true, 	false, true},
       
   329 	{"HardIce",      			false, 	false, true},
       
   330 	{"Industrial",       			false,	false, true},
       
   331 	{"Islands",       			true, 	false, true},
       
   332 	{"IslandsFlipped",     			true, 	false, true},
       
   333 	{"IslandsRearranged",  			true, 	false, true},
       
   334 	{"Hedgelove",       			true, 	false, true},
       
   335 	{"HellishRopes",       			false, 	false, true},
       
   336 	{"Hedgeland_v1",			true,	false, true},
       
   337 	{"HeyLandShoppa",			false,	false, true},
       
   338 	{"NeonStyle",       			false, 	false, true},
       
   339 	{"MaskedRopes",       			false, 	false, true},
       
   340 	{"Octorama",       			false, 	false, true},
   130 	{"red vs blue - Castle",     		true, 	false, true},
   341 	{"red vs blue - Castle",     		true, 	false, true},
   131 	{"red vs blue - castle2",     		true, 	false, true},
   342 	{"red vs blue - castle2",     		true, 	false, true},
   132 	{"red vs blue - True Shoppa Sky",   true, 	false, true},
   343 	{"red vs blue - True Shoppa Sky",	true,	false, true},
   133 	{"Ropes",       					false, 	false, true},
   344 	{"Ropes",       			false, 	false, true},
   134 	{"Ropes Rearranged",      			false, 	false, true},
   345 	{"RopeLikeAKingInHellWithNeon",		false, 	true,  true},
       
   346 	{"Ropes Flipped",      			false, 	false, true},
       
   347 	{"Ropes Rearranged",      		false, 	false, true},
   135 	{"RopesRevenge Flipped",    		true, 	false, true},
   348 	{"RopesRevenge Flipped",    		true, 	false, true},
   136 	{"Ropes Three",      				false, 	false, true},
   349 	{"RopesThree",      			false, 	false, true},
   137 	{"RopesTwo",      					false, 	false, true},
   350 	{"RopesTwo",      			false, 	false, true},
   138 	{"ShapeShoppa1.0",     				true, 	false, true},
   351 	{"Ruler",	      			false, 	false, true},
   139 	{"ShappeShoppa Darkhow",      		true, 	false, true},
   352 	{"SandShoppa",				false,	false, true},
   140 	{"ShoppaCave2",      				true, 	false, true},
   353 	{"ShapeShoppa1.0",     			true, 	false, true},
   141 	{"ShoppaFun",      					true, 	false, true},
   354 	{"ShapeShoppa Darkhow",      		true, 	false, true},
   142 	{"ShoppaGolf",      				false, 	false,  true},
   355 	{"SheepyShoppa_v2",      		true, 	false, true},
   143 	{"ShoppaHell",      				false, 	true,  false},
   356 	{"shopppa",				false,  true,  true},
   144 	{"ShoppaKing",       				false, 	false, false},
   357 	{"ShoppaCave2",      			true, 	false, true},
   145 	{"ShoppaNeon",       				false, 	false, true},
   358 	{"ShoppaChallenge",    			false, 	true, true},
   146 	{"ShoppaSky",       				false, 	false, true},
   359 	{"ShoppaDesert",    			false, 	false, true},
   147 	{"Shoppawall",       				false, 	false, true},
   360 	{"ShoppaEvoRope_v1",			true, 	false, true},
   148 	{"SkatePark",       				false, 	false, true},
   361 	{"ShoppaFun",      			true, 	false, true},
   149 	{"SloppyShoppa",      				false, 	false, true},
   362 	{"ShoppaFun2",      			true, 	false, true},
   150 	{"Sticks",       					true, 	false, true},
   363 	{"ShoppaGolf",      			false, 	false, true},
   151 	{"Symmetrical Ropes ",       		false, 	false, true},
   364 	{"ShoppaHalloween",    			false, 	false, true},
   152 	{"Tetris",       					false, 	false, true},
   365 	{"ShoppaHell",      			false,	true,  false},
   153 	{"TransRopes2",      				false, 	false, true},
   366 	{"ShoppaHellFlipped",  			true,	true,  false},
   154 	{"Wildmap",      					false, 	false, true},
   367 	{"ShoppaHellRemake",			false,	true,  false},
   155 	{"Winter Shoppa",      				false, 	false, true},
   368 	{"ShoppaKing",       			false, 	true, false},
   156 	{"2Cshoppa",      					true, 	false, true}
   369 	{"ShoppaKingFlipped",      		true, 	false, false},
       
   370 	{"ShoppaKingSideways",      		true, 	true,  false},
       
   371 	{"ShoppaMeme",				false,	true, false},
       
   372 	{"ShoppaNeon",       			false, 	false, true},
       
   373 	{"ShoppaNeonFlipped",			true, 	false, true},
       
   374 	{"ShoppaOnePiece2",			false, 	true, false},
       
   375 	{"ShoppaQuotes2",			false,  true,  true},
       
   376 	{"ShoppaRainbow",			false,  false, false},
       
   377 	{"ShoppaRadigme",			false,  true,  true},
       
   378 	{"ShoppaSilhouette",			false,  false, true},
       
   379 	{"ShoppaSpace",				true,   false, true},
       
   380 	{"ShoppaSea",				true,  false, false},
       
   381 	{"ShoppaShapex_v1",			false,  true, true},
       
   382 	{"ShoppaSparkle",			true,  true, true},
       
   383 	{"ShoppaSky",				false,  false, true},
       
   384 	{"ShoppaSky2",				true,  false, true},
       
   385 	{"ShoppaSsion",				false,  false, true},
       
   386 	{"ShoppaStyle2",			true,  false, true},
       
   387 	{"ShoppaThology",			false,  false, true},
       
   388 	{"ShoppaTournament2012",		false,  false, true},
       
   389 	{"ShoppaWild",				false,  false, true},
       
   390 	{"Shoppawall",				false,  false, false},
       
   391 	{"ShoppaWall2",				false,  false, false},
       
   392 	{"ShBall",				false,  true, false},
       
   393 	{"ShHell",				false,  true, false},
       
   394 	{"ShNeon",       			false, 	false, true},
       
   395 	{"ShoppaSky",       			false, 	false, true},
       
   396 	{"SloppyShoppa",       			false, 	true,  true},
       
   397 	{"SloppyShoppa2",      			false, 	true,  true},
       
   398 	{"SkatePark",       			false, 	true,  true},
       
   399 	{"Snow_Ropes",       			false, 	true, false},
       
   400 	{"Sticks",       			true, 	false, true},
       
   401 	{"Symmetrical Ropes",       		false, 	false, true},
       
   402 	{"SpartanShoppa",       		false, 	true,  true},
       
   403 	{"Tetris",       			false, 	false, true},
       
   404 	{"TransRopes2",      			false, 	false, true},
       
   405 	{"TRBShoppa",      			false, 	false, true},
       
   406 	{"TrickyShoppa",      			false, 	true, false},
       
   407 	{"Wildmap",      			false, 	false, true},
       
   408 	{"Winter Shoppa",      			false, 	false, true},
       
   409 	{"WarShoppa",      			false, 	true,  true},
       
   410 	{"2Cshoppa",      			true, 	false, true},
   157 	}
   411 	}
       
   412 
       
   413 local Ropes_WallSet = {
       
   414 	{ add="none", {299,932,20,856}, {4056,0,30,1788} },
       
   415 	{ add="none", {299,109,20,779}, {4056,0,30,1788} },
       
   416 	{ add="none", {299,109,20,779}, {299,932,20,856}, {4056,0,30,1788} },
       
   417 	{ add="default", {2253,326,20,574}, {3280,326,33,253}, needsborder=false },
       
   418 	{ add="roof", {2322,326,457,20} },
       
   419 	{ add="default", {1092,934,54,262}, {2822,323,33,137}, needsborder=false },
       
   420 	{ add="none", {203,1193,20,595}, {3280,326,20,253}, needsborder=false },
       
   421 }
       
   422 local Shoppawall_WallSet = {
       
   423 	{ add="none", {80+290,61+878,20,1018}, {3433+290,61+878,20,1018}, default=true, needsborder=false },
       
   424 }
       
   425 
       
   426 -- List of map with special wall settings
       
   427 local SpecialMapList = {
       
   428 	["Ropes"] = Ropes_WallSet,
       
   429 	["HellishRopes"] = Ropes_WallSet,
       
   430 	["MaskedRopes"] = Ropes_WallSet,
       
   431 	["TransRopes2"] = Ropes_WallSet,
       
   432 	["ShoppaKing"] = {
       
   433 		{ add="none", {3777,1520,50,196}, {1658,338,46,670}, needsborder=false },
       
   434 		{ add="none", {125,0,30,2048}, {4066,515,30,1528}, default=true},
       
   435 	},
       
   436 	["ShoppaHell"] = {
       
   437 		{ add="none", {3491,697,30,1150}, {0,0,30,1847}, default=true},
       
   438 		{ add="none", {3810,0,30,1616}, {0,0,30,1847}, },
       
   439 		{ add="none", {2045,832,20,260}, {2107,832,20,260}, needsborder=false },
       
   440 		{ add="default", {2035,831,30,263}, {3968,1668,31,383}, needsborder=false },
       
   441 	},
       
   442 	["ShoppaNeon"] = {
       
   443 		{ add="default", {980,400,20,300}, {1940,400,20,300}, {3088,565,26,284}, {187,270,28,266}, needsborder=false },
       
   444 	},
       
   445 	["Shoppawall"] = Shoppawall_WallSet,
       
   446 	["ShoppaWall2"] = Shoppawall_WallSet,
       
   447 	["ShoppaDesert"] = {
       
   448 		{ add="none", {2322,349,20,471}, {295,93,24,1479}, needsborder=false },
       
   449 		{ add="none", {3001,1535,20,232}, {2264,349,20,495},{716,696,20,119}, needsborder=false },
       
   450 		{ add="leftright", {209,656,20,367},{2810,838,20,96}, needsborder=false},
       
   451 		{ add="none", {2649,0,445,20}, {2322,349,947,20},{299,696,381,20}},
       
   452 	},
       
   453 	["ShoppaOnePiece2"] = {
       
   454 		{ add="default", {42,0,20,2048}, {4048,0,20,2048}, needsborder=false, },
       
   455 		{ add="default", {42,0,20,2048}, {3852,273,20,1637}, needsborder=false, default="noborder" },
       
   456 	},
       
   457 	["ShoppaWild"] = {
       
   458 		{ add="default", {2123,1365,20,293}, {3102,1365,20,293}, {1215,1391,20,291}, needsborder=false },
       
   459 		{ add="none", {144,167,1904,20}, {2350,167,753,20}, {3793,167,303,20}, needsborder=false},
       
   460 	},
       
   461 	["ShoppaRainbow"] = {
       
   462 		{ add="none", {67+602,61+80,20,1847}, {2779+602,61+80,20,1847}, needsborder=false },
       
   463 	},
       
   464 }
   158 
   465 
   159 function BoolToCfgTxt(p)
   466 function BoolToCfgTxt(p)
   160 	if p == false then
   467 	if p == false then
   161 		return loc("Disabled")
   468 		return loc("Disabled")
   162 	else
   469 	else
   163 		return loc("Enabled")
   470 		return loc("Enabled")
   164 	end
   471 	end
   165 end
   472 end
   166 
   473 
   167 function LoadConfig(p)
   474 function AttackRuleToCfgTxt(attackRule)
   168 
   475 	if attackRule == nil then
       
   476 		return loc("Disabled")
       
   477 	elseif attackRule == "ABL" then
       
   478 		return loc("All But Last")
       
   479 	elseif attackRule == "KTL" then
       
   480 		return loc("Kill The Leader")
       
   481 	else
       
   482 		return "ERROR"
       
   483 	end
       
   484 end
       
   485 
       
   486 function NewWallSet(newWallSet, wType)
       
   487 	-- Filter out wall sets which are not in allowed categories or have too many or few walls
       
   488 	if allowedWallSetTypes[wType] == true then
       
   489 		local inBounds = true
       
   490 		if minWalls ~= nil and #newWallSet < minWalls then
       
   491 			inBounds = false
       
   492 		end
       
   493 		if maxWalls ~= nil and #newWallSet > maxWalls then
       
   494 			inBounds = false
       
   495 		end
       
   496 		if inBounds then
       
   497 			table.insert(wallSets, newWallSet)
       
   498 		end
       
   499 	end
       
   500 end
       
   501 
       
   502 function MapsInit()
       
   503 	mapID = nil
   169 	margin = 20
   504 	margin = 20
   170 	mapID = nil
       
   171 
   505 
   172 	-- 0.9.17
   506 	-- 0.9.17
   173 	if Map == "CHANGE_ME" then
   507 	if Map == "CHANGE_ME" then
   174 		AddCaption(loc("For improved features/stability, play 0.9.18+"))
   508 		AddCaption(loc("For improved features/stability, play 0.9.18+"))
   175 		--AddWall(10,10,4085,margin)
       
   176 		AddWall(10,10,margin,2025)
   509 		AddWall(10,10,margin,2025)
   177 		AddWall(4085-margin,10,margin,2025)
   510 		AddWall(4085-margin,10,margin,2025)
   178 	end
   511 	end
   179 
   512 
   180 	--0.9.18+
   513 	--0.9.18+
   181 	for i = 1, #MapList do
   514 	for i = 1, #MapList do
   182 		if Map == MapList[i][1] then
   515 		if Map == MapList[i][1] then
   183 			mapID = i
   516 			mapID = i
   184 			--AddCaption(MapList[i][1] .. " found. reqSurf is " .. BoolToCfgTxt(MapList[i][2]))
   517 		end
   185 		end
   518 	end
   186 	end
   519 
   187 
   520 	local left, right, roof
   188 	if (p == 1) and (mapID ~= nil) then
   521 	left = {LeftX+10,TopY+10,margin,WaterLine}
   189 		requireSurfer = MapList[mapID][2]
   522 	right = {RightX-10-margin,TopY+10,margin,WaterLine}
   190 	end
   523 	roof = {LeftX+10,TopY+10,RightX-LeftX-20,margin}
       
   524 
       
   525 	local border = MapHasBorder()
   191 
   526 
   192 	if mapID ~= nil then
   527 	if mapID ~= nil then
   193 
   528 		if border then
   194 		-- add a wall to the roof
   529 			if MapList[mapID][3] == true then
   195 		if MapList[mapID][3] == true then
   530 				NewWallSet({roof, desc=loc("Roof")}, "roof")
   196 			AddWall(LeftX+10,TopY+10,RightX-LeftX-20,margin)
   531 				wallSetID = #wallSets
   197 		end
   532 			end
   198 
   533 			if MapList[mapID][4] == true then
   199 		-- add walls on the left and right border
   534 				NewWallSet({left, right, desc=loc("Left and right")}, "leftright")
   200 		if MapList[mapID][4] == true then
   535 				wallSetID = #wallSets
   201 			AddWall(LeftX+10,TopY+10,margin,WaterLine)
   536 			end
   202 			AddWall(RightX-10-margin,TopY+10,margin,WaterLine)
   537 			if MapList[mapID][3] == true and MapList[mapID][4] == true then
       
   538 				NewWallSet({left, right, roof, desc=loc("Left, right and roof")}, "leftrightroof")
       
   539 			end
   203 		end
   540 		end
   204 
   541 
   205 		-- add map specific walls
   542 		-- add map specific walls
   206 		if Map == "Ropes" then
   543 		if SpecialMapList[Map] ~= nil then
   207 			AddWall(1092,934,54,262)
   544 			local insideID = 1
   208 			AddWall(2822,323,33,137)
   545 			local previousInside = nil
   209 		elseif Map == "ShoppaKing" then
   546 			local mixedID = 1
   210 			AddWall(3777,1520,50,196)
   547 			local previousMixed = nil
   211 			AddWall(1658,338,46,670)
   548 
   212 		elseif Map == "ShoppaHell" then
   549 			-- Helper function to build the wall set name.
   213 			AddWall(2035,831,30,263)
   550 			-- Basically just to ensure that names like "Inside 1" are only used when there are at least 2 "Insides"
   214 			AddWall(3968,1668,31,383)
   551 			local function newInsideOrMixed(ws, previous_ws, id, string, stringD)
   215 		elseif Map == "ShoppaNeon" then
   552 				if id == 1 then
   216 			AddWall(980,400,20,300)
   553 					ws.desc = string
   217 			AddWall(1940,400,20,300)
   554 				else
   218 			AddWall(3088,565,26,284)
   555 					ws.desc = string.format(stringD, id)
   219 			AddWall(187,270,28,266)
   556 				end
       
   557 				if id == 2 then
       
   558 					previous_ws.desc = string.format(stringD, id-1)
       
   559 				end
       
   560 				id = id + 1
       
   561 				previous_ws = ws
       
   562 				return id, previous_ws
       
   563 			end
       
   564 			for ws=1,#SpecialMapList[Map] do
       
   565 				local walls = SpecialMapList[Map][ws]
       
   566 				if walls.needsborder == false then
       
   567 					local newwallset2 = {}
       
   568 					for w=1,#walls do
       
   569 						table.insert(newwallset2, walls[w])
       
   570 					end
       
   571 					insideID, previousInside = newInsideOrMixed(newwallset2, previousInside, insideID, loc("Inside"), loc("Inside %d"))
       
   572 					newwallset2.custom = true
       
   573 					NewWallSet(newwallset2, "inside")
       
   574 					if SpecialMapList[Map][ws].default == "noborder" then
       
   575 						wallSetID = #wallSets
       
   576 					end
       
   577 				end
       
   578 				local newwallset = {}
       
   579 				if border then
       
   580 					if walls.add == "all" then
       
   581 						table.insert(newwallset, roof)
       
   582 						table.insert(newwallset, left)
       
   583 						table.insert(newwallset, right)
       
   584 					elseif walls.add == "default" then
       
   585 						if MapList[mapID][3] == true then
       
   586 							table.insert(newwallset, roof)
       
   587 						end
       
   588 						if MapList[mapID][4] == true then
       
   589 							table.insert(newwallset, left)
       
   590 							table.insert(newwallset, right)
       
   591 						end
       
   592 					elseif walls.add == "roof" then
       
   593 						table.insert(newwallset, roof)
       
   594 					elseif walls.add == "leftright" then
       
   595 						table.insert(newwallset, left)
       
   596 						table.insert(newwallset, right)
       
   597 					end
       
   598 				end
       
   599 				for w=1,#walls do
       
   600 					table.insert(newwallset, walls[w])
       
   601 				end
       
   602 				if border and ((walls.add ~= "none" and walls.add ~= nil) or walls.needsborder ~= false) then
       
   603 					mixedID, previousMixed = newInsideOrMixed(newwallset, previousMixed, mixedID, loc("Mixed"), loc("Mixed %d"))
       
   604 					newwallset.custom = true
       
   605 					NewWallSet(newwallset, "mixed")
       
   606 				end
       
   607 				if SpecialMapList[Map][ws].default == true then
       
   608 					wallSetID = #wallSets
       
   609 				end
       
   610 			end
       
   611 		end
       
   612 
       
   613 	else
       
   614 		if border then
       
   615 			NewWallSet({roof, desc=loc("Roof")}, "roof")
       
   616 			NewWallSet({left, right, desc=loc("Left and right")}, "leftright")
       
   617 			NewWallSet({left, right, roof, desc=loc("Left, right and roof")}, "leftrightroof")
       
   618 			wallSetID = 2
       
   619 		end
       
   620 	end
       
   621 
       
   622 	-- Choose random map when without without menu
       
   623 	if useMenu == false and #wallSets > 0 then
       
   624 		wallSetID = GetRandom(#wallSets)+1
       
   625 	end
       
   626 	-- Select first wall set by default if we still haven't selected anything for some reason
       
   627 	if wallSetID == 0 and #wallSets > 0 then
       
   628 		wallSetID = 1	
       
   629 	end
       
   630 	-- But disabled walls from script parameter have higher priority
       
   631 	if WBC == false then
       
   632 		wallSetID = 0
       
   633 	end
       
   634 
       
   635 	if CanSurf() == false then
       
   636 		requireSurfer = false
       
   637 	end
       
   638 end
       
   639 
       
   640 function LoadConfig(p)
       
   641 	ClearWalls()
       
   642 	if mapID ~= nil then
       
   643 		if p > 0 then
       
   644 			local walls = wallSets[p]
       
   645 			for i=1,#walls do
       
   646 				AddWall(walls[i][1], walls[i][2], walls[i][3], walls[i][4])
       
   647 			end
   220 		end
   648 		end
   221 
   649 
   222 	-- if map is unrecognized, add two walls on the side borders
   650 	-- if map is unrecognized, add two walls on the side borders
   223 	-- also, if version of hw is not 0.9.17 or lower
   651 	-- also, if version of hw is not 0.9.17 or lower
   224 	elseif Map ~= "CHANGE_ME" then
   652 	elseif Map ~= "CHANGE_ME" then
   225 		AddWall(LeftX+10,TopY+10,margin,WaterLine)
   653 		if p == 1 or p == 3 then
   226 		AddWall(RightX-10-margin,TopY+10,margin,WaterLine)
   654 			AddWall(LeftX+10,TopY+10,RightX-LeftX-20,margin)
   227 	end
   655 		end
   228 
   656 		if p == 2 or p == 3 then
       
   657 			AddWall(LeftX+10,TopY+10,margin,WaterLine)
       
   658 			AddWall(RightX-10-margin,TopY+10,margin,WaterLine)
       
   659 		end
       
   660 	end
   229 
   661 
   230 end
   662 end
   231 
   663 
   232 function AddWall(zXMin,zYMin, zWidth, zHeight)
   664 function AddWall(zXMin,zYMin, zWidth, zHeight)
   233 
   665 
   237 	table.insert(wHeight, zHeight)
   669 	table.insert(wHeight, zHeight)
   238 	table.insert(wTouched, false)
   670 	table.insert(wTouched, false)
   239 
   671 
   240 end
   672 end
   241 
   673 
       
   674 function ClearWalls()
       
   675 
       
   676 	wX = {}
       
   677 	wY = {}
       
   678 	wWidth = {}
       
   679 	wHeight = {}
       
   680 	wTouched = {}
       
   681 
       
   682 end
       
   683 
       
   684 -- Draw a single point for the crate radar
   242 function DrawBlip(gear)
   685 function DrawBlip(gear)
   243 	SetVisualGearValues(getGearValue(gear,"CIRC"), getGearValue(gear,"RX"), getGearValue(gear,"RY"), 100, 255, 1, 10, 0, 40, 3, GetClanColor(GetHogClan(CurrentHedgehog))-rAlpha)
   686 	if GetGearType(gear) ~= gtCase then
       
   687 		return
       
   688 	end
       
   689 
       
   690 	local baseColor, radius, alpha
       
   691 	if getGearValue(gear, "frozen") then
       
   692 		radius = 25
       
   693 		baseColor = 0xFFFFFFFF
       
   694 		alpha = math.min(255, rAlpha+127)
       
   695 	else
       
   696 		radius = 40
       
   697 		baseColor = GetClanColor(GetHogClan(CurrentHedgehog))
       
   698 		alpha = rAlpha
       
   699 	end
       
   700 	SetVisualGearValues(getGearValue(gear,"CIRC"), getGearValue(gear,"RX"), getGearValue(gear,"RY"), 100, 255, 1, 10, 0, radius, 3, baseColor-alpha)
   244 end
   701 end
   245 
   702 
   246 function TrackRadarBlip(gear)
   703 function TrackRadarBlip(gear)
       
   704 	if GetGearType(gear) ~= gtCase then
       
   705 		return
       
   706 	end
   247 
   707 
   248 	-- work out the distance to the target
   708 	-- work out the distance to the target
   249 	g1X, g1Y = GetGearPosition(CurrentHedgehog)
   709 	g1X, g1Y = GetGearPosition(CurrentHedgehog)
   250 	g2X, g2Y = GetX(gear), GetY(gear)
   710 	g2X, g2Y = GetX(gear), GetY(gear)
   251 	q = g1X - g2X
   711 	q = g1X - g2X
   292 end
   752 end
   293 
   753 
   294 
   754 
   295 function HandleCircles()
   755 function HandleCircles()
   296 
   756 
   297 	-- enable this if you want the radar to only show for a few seconds
   757 	if radarMode == 0 then
   298 	-- after you spawn the crate
   758 		rAlpha = 0
   299 	--[[if rAlpha ~= 255 then
   759 	elseif radarMode == 1 then
   300 
   760 		-- Only show radar for a short time after a crate spawn
   301 		rPingTimer = rPingTimer + 1
   761 		if rAlpha ~= 255 then
   302 		if rPingTimer == 100 then
   762 			rPingTimer = rPingTimer + 1
   303 			rPingTimer = 0
   763 			if rPingTimer == 100 then
   304 
   764 				rPingTimer = 0
   305 			rAlpha = rAlpha + 5
   765 	
   306 			if rAlpha >= 255 then
   766 				rAlpha = rAlpha + 5
   307 				rAlpha = 255
   767 				if rAlpha >= 255 then
   308 			end
   768 					rAlpha = 255
   309 		end
   769 				end
   310 
   770 			end
   311 	end]]
   771 		end
       
   772 	elseif radarMode == 2 then
       
   773 		rAlpha = 255
       
   774 	end
   312 
   775 
   313 	runOnGears(DrawBlip)
   776 	runOnGears(DrawBlip)
   314 
   777 
   315 	m2Count = m2Count + 1
   778 	m2Count = m2Count + 1
   316 	if m2Count == 25 then
   779 	if m2Count == 25 then
   322 
   785 
   323 	end
   786 	end
   324 
   787 
   325 end
   788 end
   326 
   789 
   327 
   790 -- Returns true if crates are allowed to be accessed right now (used for unfreezing and spawning)
   328 function CheckCrateConditions()
   791 function AreCratesUnlocked()
   329 
   792 
   330 	crateSpawn = true
   793 	local crateSpawn = true
   331 
   794 
   332 	if requireSurfer == true then
   795 	if requireSurfer == true then
   333 		if hasSurfed == false then
   796 		if hasSurfed == false then
   334 			crateSpawn = false
   797 			crateSpawn = false
   335 		end
   798 		end
   339 		if allWallsHit == false then
   802 		if allWallsHit == false then
   340 			crateSpawn = false
   803 			crateSpawn = false
   341 		end
   804 		end
   342 	end
   805 	end
   343 
   806 
   344 	if crateSpawn == true then
   807 	return crateSpawn
       
   808 
       
   809 end
       
   810 
       
   811 -- Freeze all crates,
       
   812 function FreezeCrates()
       
   813 
       
   814 	local cratesFrozen = 0
       
   815 	for crate, isCrate in pairs(crates) do
       
   816 		local state = GetState(crate)
       
   817 		-- Freeze crate if it wasn't already frozen
       
   818 		if band(state, gstFrozen) == 0 then
       
   819 			cratesFrozen = cratesFrozen + 1
       
   820 			SetState(crate, bor(GetState(crate), gstFrozen))
       
   821 			setGearValue(crate, "frozen", true)
       
   822 		end
       
   823 	end
       
   824 	-- Play sound if at least one new (!) crate was frozen
       
   825 	if cratesFrozen > 0 then
       
   826 		PlaySound(sndHogFreeze)
       
   827 	end
       
   828 
       
   829 end
       
   830 
       
   831 -- Unfreeze all crates
       
   832 function UnfreezeCrates()
       
   833 
       
   834 	for crate, isCrate in pairs(crates) do
       
   835 		SetState(crate, band(GetState(crate), bnot(gstFrozen)))
       
   836 		setGearValue(crate, "frozen", false)
       
   837 	end
       
   838 
       
   839 end
       
   840 
       
   841 function CheckCrateConditions()
       
   842 
       
   843 	local crateSpawn = AreCratesUnlocked()
       
   844 
       
   845 	if crateSpawn == true and crateSpawned == false then
       
   846 		UnfreezeCrates()
   345 		if allowCrate == true then
   847 		if allowCrate == true then
   346 		--if (crateG == nil) and (allowCrate == true) then
   848 			local cratesInGame = crateGearsInGame
   347 			--AddCaption("")
   849 			local toSpawn = cratesPerTurn
   348 			SpawnAmmoCrate(0, 0, weapons[1+GetRandom(#weapons)] )
   850 			if cratesInGame + toSpawn > maxCrates then
       
   851 				toSpawn = maxCrates - cratesInGame
       
   852 			end
       
   853 			for i=1,toSpawn do
       
   854 				SpawnAmmoCrate(0, 0, weapons[1+GetRandom(#weapons)] )
       
   855 			end
   349 			rPingTimer = 0
   856 			rPingTimer = 0
   350 			rAlpha = 0
   857 			rAlpha = 0
   351 			PlaySound(sndWarp)
   858 			if toSpawn > 0 then
       
   859 				PlaySound(sndWarp)
       
   860 			end
   352 		end
   861 		end
   353 	end
   862 	end
   354 
   863 
   355 end
   864 end
   356 
   865 
   357 function onGearWaterSkip(gear)
   866 function onGearWaterSkip(gear)
   358 	if gear == CurrentHedgehog then
   867 	if gear == CurrentHedgehog then
   359 		hasSurfed = true
   868 		hasSurfed = true
   360 		AddCaption(loc("Surfer!"),0xffba00ff,capgrpMessage2)
   869 		AddCaption(loc("Surfer!"), 0xFFFFFFFF, capgrpMessage2)
   361 	end
   870 	end
   362 end
   871 end
   363 
   872 
   364 
   873 
   365 function WallHit(id, zXMin,zYMin, zWidth, zHeight)
   874 function WallHit(id, zXMin,zYMin, zWidth, zHeight)
   371 
   880 
   372 		if wallsLeft == 0 then
   881 		if wallsLeft == 0 then
   373 			AddCaption(loc("All walls touched!"))
   882 			AddCaption(loc("All walls touched!"))
   374 			allWallsHit = true
   883 			allWallsHit = true
   375 			if (requireSurfer == true) and (hasSurfed == false) then
   884 			if (requireSurfer == true) and (hasSurfed == false) then
   376 				AddCaption(loc("Go surf!"),0xffba00ff,capgrpMessage2)
   885 				AddCaption(loc("Go surf!"), 0xFFFFFFFF, capgrpMessage2)
   377 			end
   886 			end
   378 		else
   887 		else
   379 			AddCaption(loc("Walls Left") .. ": " .. wallsLeft)
   888 			AddCaption(string.format(loc("Walls left: %d"), wallsLeft))
   380 		end
   889 		end
   381 
   890 
   382 	end
   891 	end
   383 
   892 
   384 	wTouched[id] = true
   893 	wTouched[id] = true
   385 	tempE = AddVisualGear(GetX(CurrentHedgehog), GetY(CurrentHedgehog), vgtSmoke, 0, false)
   894 	if #wTouched > 0 then
   386 	--PlaySound(sndVaporize) -- yeah, this is just annoying as shit
   895 		tempE = AddVisualGear(GetX(CurrentHedgehog), GetY(CurrentHedgehog), vgtSmoke, 0, false)
       
   896 	end
   387 
   897 
   388 end
   898 end
   389 
   899 
   390 function CheckForWallCollision()
   900 function CheckForWallCollision()
   391 
   901 
   417 	if effectTimer > 15 then --25
   927 	if effectTimer > 15 then --25
   418 
   928 
   419 		effectTimer = 1
   929 		effectTimer = 1
   420 
   930 
   421 		for i = 1, #wTouched do
   931 		for i = 1, #wTouched do
   422 			if wTouched[i] == true then
   932 			if wTouched[i] == false then
   423 				--bCol = GetClanColor(GetHogClan(CurrentHedgehog))
       
   424 			else
       
   425 				--bCol = 0xFFFFFFFF
       
   426 				bCol = GetClanColor(GetHogClan(CurrentHedgehog))
   933 				bCol = GetClanColor(GetHogClan(CurrentHedgehog))
   427 				BorderSpark(wX[i],wY[i],wWidth[i],wHeight[i], bCol)
   934 				BorderSpark(wX[i],wY[i],wWidth[i],wHeight[i], bCol)
   428 			end
   935 			end
   429 			--BorderSpark(wX[i],wY[i],wWidth[i],wHeight[i], bCol)
   936 		end
   430 		end
   937 
   431 
   938 	end
   432 	end
   939 
   433 
   940 end
       
   941 
       
   942 function PlaceWarn()
       
   943 	PlaySound(sndDenied)
       
   944 	AddCaption(loc("Please place your hedgehog first!"), msgColorWarn, capgrpMessage2)
   434 end
   945 end
   435 
   946 
   436 function onLJump()
   947 function onLJump()
   437 	if roundN < 2 then
   948 	if roundN == 1 then
   438 		roundN = 100
   949 		PlaySound(sndPlaced)
   439 		SetInputMask(0xFFFFFFFF)
   950 		SetInputMask(0xFFFFFFFF)
   440 		TurnTimeLeft = 1
   951 		AddCaption(loc("Configuration accepted."), msgColorTech, capgrpMessage)
   441 		AddCaption(loc("Configuration accepted."),0xffba00ff,capgrpMessage)
   952 		if GetGameFlag(gfPlaceHog) then
   442 		HideMission()
   953 			TurnTimeLeft = PlacementTime
       
   954 			AddAmmo(CurrentHedgehog, amTeleport, 100)
       
   955 			SetWeapon(amTeleport)
       
   956 			AddCaption(
       
   957 				string.format(loc("%s, place the first hedgehog!"), GetHogTeamName(CurrentHedgehog)),
       
   958 				0xFFFFFFFF,
       
   959 				capgrpMessage2
       
   960 			)
       
   961 			roundN = 2
       
   962 		else
       
   963 			TurnTimeLeft = TurnTime
       
   964 			AddCaption(string.format(loc("Let's go, %s!"), GetHogTeamName(CurrentHedgehog)), 0xFFFFFFFF, capgrpMessage2)
       
   965 			roundN = 100
       
   966 			wallsLeft = #wTouched
       
   967 			allowCrate = true
       
   968 		end
       
   969 		PlaySound(sndYesSir, CurrentHedgehog)
       
   970 		FinalizeMenu()
       
   971 	elseif roundN == 2 then
       
   972 		PlaceWarn()
       
   973 	elseif roundN == 100 then
       
   974 		if CBA and not crateCollected then
       
   975 			if (GetCurAmmoType() ~= amRope) and
       
   976 				(GetCurAmmoType() ~= amSkip) and
       
   977 				(GetCurAmmoType() ~= amNothing) and
       
   978 				(ropeG ~= nil)
       
   979 			then
       
   980 				AddCaption(loc("You must first collect a crate before you attack!"), msgColorWarn, capgrpMessage2)
       
   981 				PlaySound(sndDenied)
       
   982 			end
       
   983 		end
   443 	end
   984 	end
   444 end
   985 end
   445 
   986 
   446 function onAttack()
   987 function onAttack()
   447 
   988 	if roundN == 1 then
   448 	if roundN < 2 then
   989 		if menu[menuIndex].activate ~= nil then
   449 
   990 			menu[menuIndex].activate()
   450 		if menuIndex == 1 then
   991 		else
   451 
   992 			menu[menuIndex].doNext()
   452 			if #wTouched > 0 then
       
   453 				for i = 1, #wTouched do
       
   454 					wTouched[i] = nil
       
   455 					wX[i] = nil
       
   456 					wY[i] = nil
       
   457 					wWidth[i] = nil
       
   458 					wHeight[i] = nil
       
   459 				end
       
   460 			else
       
   461 				LoadConfig(2)
       
   462 			end
       
   463 
       
   464 		elseif menuIndex == 2 then
       
   465 			requireSurfer = not(requireSurfer)
       
   466 		elseif menuIndex == 3 then
       
   467 			AFR = not(AFR)
       
   468 		elseif menuIndex == 4 then
       
   469 			allowCrazyWeps = not(allowCrazyWeps)
       
   470 		end
   993 		end
   471 
   994 
   472 		UpdateMenu()
   995 		UpdateMenu()
   473 		configureWeapons()
   996 		configureWeapons()
   474 		HandleStartingStage()
   997 		HandleStartingStage()
   475 
   998 
   476 	elseif (AFR == true) then
   999 		PlaySound(sndSwitchHog)
   477 
  1000 
   478 		if (GetCurAmmoType() ~= amRope) and
  1001 	elseif roundN == 2 then
       
  1002 		if GetCurAmmoType() ~= amSkip and GetCurAmmoType() ~= amNothing then
       
  1003 			PlaceWarn()
       
  1004 		end
       
  1005 
       
  1006 	elseif roundN == 100 then
       
  1007 		local weaponSelected = (GetCurAmmoType() ~= amRope) and
   479 			(GetCurAmmoType() ~= amSkip) and
  1008 			(GetCurAmmoType() ~= amSkip) and
   480 			(GetCurAmmoType() ~= amNothing)
  1009 			(GetCurAmmoType() ~= amNothing) and
   481 		then
  1010 			(ropeG == nil)
   482 			AddCaption(loc("You may only attack from a rope!"),0xffba00ff,capgrpMessage2)
  1011 
   483 		end
  1012 		if weaponSelected then
   484 
  1013 			if AFR and CBA and not crateCollected then
   485 	end
  1014 				AddCaption(loc("You must attack from a rope, after you collected a crate!"), msgColorWarn, capgrpMessage2)
   486 
  1015 				PlaySound(sndDenied)
       
  1016 			elseif AFR then
       
  1017 				AddCaption(loc("You may only attack from a rope!"), msgColorWarn, capgrpMessage2)
       
  1018 				PlaySound(sndDenied)
       
  1019 			elseif CBA and not crateCollected then
       
  1020 				AddCaption(loc("You must first collect a crate before you attack!"), msgColorWarn, capgrpMessage2)
       
  1021 				PlaySound(sndDenied)
       
  1022 			end
       
  1023 		end
       
  1024 	end
       
  1025 end
       
  1026 
       
  1027 function onSwitch()
       
  1028 	-- Must be in-game, hog must be controlled by player and hog must be on rope or have rope selected
       
  1029 	if roundN == 100 and CurrentHedgehog ~= nil and band(GetState(CurrentHedgehog), gstHHDriven) ~= 0 and (ropeG ~= nil or GetCurAmmoType() == amRope) then
       
  1030 		-- Toggle radar mode
       
  1031 		radarMode = radarMode + 1
       
  1032 		if radarMode > 2 then
       
  1033 			radarMode = 0
       
  1034 		end
       
  1035 		local message
       
  1036 		if radarMode == 0 then
       
  1037 			message = loc("Radar: On")
       
  1038 		elseif radarMode == 1 then
       
  1039 			message = loc("Radar: Show after crate drop")
       
  1040 		elseif radarMode == 2 then
       
  1041 			message = loc("Radar: Off")
       
  1042 		end
       
  1043 		AddCaption(message, GetClanColor(GetHogClan(CurrentHedgehog)), capgrpAmmostate)
       
  1044 		-- Remember the radar mode for this team to restore it on the team's next turn
       
  1045 		setTeamValue(GetHogTeamName(CurrentHedgehog), "radarMode", radarMode)
       
  1046 	end
       
  1047 end
       
  1048 
       
  1049 function onLeft()
       
  1050 	if roundN == 1 then
       
  1051 		if menu[menuIndex].doPrev ~= nil then
       
  1052 			menu[menuIndex].doPrev()
       
  1053 		else
       
  1054 			menu[menuIndex].activate()
       
  1055 		end
       
  1056 
       
  1057 		UpdateMenu()
       
  1058 		configureWeapons()
       
  1059 		HandleStartingStage()
       
  1060 
       
  1061 		PlaySound(sndSwitchHog)
       
  1062 	end
       
  1063 end
       
  1064 
       
  1065 function onRight()
       
  1066 	if roundN == 1 then
       
  1067 		if menu[menuIndex].doNext ~= nil then
       
  1068 			menu[menuIndex].doNext()
       
  1069 		else
       
  1070 			menu[menuIndex].activate()
       
  1071 		end
       
  1072 
       
  1073 		UpdateMenu()
       
  1074 		configureWeapons()
       
  1075 		HandleStartingStage()
       
  1076 
       
  1077 		PlaySound(sndSwitchHog)
       
  1078 	end
   487 end
  1079 end
   488 
  1080 
   489 function onDown()
  1081 function onDown()
   490 	if roundN < 2 then
  1082 	if roundN == 1 then
       
  1083 		PlaySound(sndSteps)
   491 		menuIndex = menuIndex +1
  1084 		menuIndex = menuIndex +1
   492 		if menuIndex > #menu then
  1085 		if menuIndex > #menu then
   493 			menuIndex = 1
  1086 			menuIndex = 1
   494 		end
  1087 		end
   495 		HandleStartingStage()
  1088 		HandleStartingStage()
   496 	end
  1089 	end
   497 end
  1090 end
   498 
  1091 
   499 function onUp()
  1092 function onUp()
   500 	if roundN < 2 then
  1093 	if roundN == 1 then
       
  1094 		PlaySound(sndSteps)
   501 		menuIndex = menuIndex -1
  1095 		menuIndex = menuIndex -1
   502 		if 	menuIndex == 0 then
  1096 		if 	menuIndex == 0 then
   503 			menuIndex = #menu
  1097 			menuIndex = #menu
   504 		end
  1098 		end
   505 		HandleStartingStage()
  1099 		HandleStartingStage()
   506 	end
  1100 	end
   507 end
  1101 end
   508 
  1102 
       
  1103 function parseBool(key, default)
       
  1104 	if params[key]=="true" then
       
  1105 		return true
       
  1106 	elseif params[key]=="false" then
       
  1107 		return false
       
  1108 	else
       
  1109 		return default
       
  1110 	end
       
  1111 end
       
  1112 
       
  1113 function parseInt(key, default, min, max)
       
  1114 	local num = tonumber(params[key])
       
  1115 	if type(num) ~= "number" then
       
  1116 		return default
       
  1117 	end
       
  1118 	if min ~= nil then
       
  1119 		num = math.max(min, num)
       
  1120 	end
       
  1121 	if max ~= nil then
       
  1122 		num = math.min(max, num)
       
  1123 	end
       
  1124 	return num
       
  1125 end
       
  1126 
       
  1127 function onParameters()
       
  1128 	parseParams()
       
  1129 	local tmpParam
       
  1130 	useMenu = parseBool("menu", useMenu)
       
  1131 	requireSurfer = parseBool("SBC", requireSurfer)
       
  1132 	AFR = parseBool("AFR", AFR)
       
  1133 	CBA = parseBool("CBA", CBA)
       
  1134 	if params["attackrule"] == "ABL" then
       
  1135 		attackRule = "ABL"
       
  1136 	elseif params["attackrule"] == "KTL" then
       
  1137 		attackRule = "KTL"
       
  1138 	end
       
  1139 	allowCrazyWeps = parseBool("SW", allowCrazyWeps)
       
  1140 	maxCrates = parseInt("maxcrates", maxCrates, 1, maxCratesHard)
       
  1141 	cratesPerTurn = parseInt("cratesperturn", cratesPerTurn, 1, maxCrates)
       
  1142 	local wallsParam = params["walls"]
       
  1143 	local wallsParamSelection = false
       
  1144 	if wallsParam ~= nil then
       
  1145 		if wallsParam == "all" then
       
  1146 			wallsParamSelection = true
       
  1147 			allowedWallSetTypes = {}
       
  1148 			for i=1,#allWallSetTypes do
       
  1149 				allowedWallSetTypes[allWallSetTypes[i]] = true
       
  1150 			end
       
  1151 		elseif wallsParam == "none" then
       
  1152 			WBC = false
       
  1153 			allowedWallSetTypes = {}
       
  1154 		else
       
  1155 			wallsParamSelection = true
       
  1156 			allowedWallSetTypes = {}
       
  1157 			local parsedWords = {}
       
  1158 			for k,v in string.gmatch(wallsParam, "(%w+)") do
       
  1159 				table.insert(parsedWords, k)
       
  1160 			end
       
  1161 			for i=1,#allWallSetTypes do
       
  1162 				for j=1,#parsedWords do
       
  1163 					if allWallSetTypes[i] == parsedWords[j] then
       
  1164 						allowedWallSetTypes[allWallSetTypes[i]] = true
       
  1165 					end
       
  1166 				end
       
  1167 			end
       
  1168 		end
       
  1169 	end
       
  1170 
       
  1171 	-- Upper and lower bounds
       
  1172 	local wallsNum = parseInt("wallsnum", nil, 0)
       
  1173 	if wallsNum == 0 then
       
  1174 		WBC = false
       
  1175 	end
       
  1176 	minWalls = wallsNum
       
  1177 	maxWalls = wallsNum
       
  1178 	-- minwalls and maxwalls take precedence over wallsnum
       
  1179 	minWalls = parseInt("minwalls", minWalls, 1)
       
  1180 	maxWalls = parseInt("maxwalls", maxWalls, 1)
       
  1181 end
       
  1182 
   509 function onGameInit()
  1183 function onGameInit()
   510 
  1184 
   511 	ClearGameFlags()
       
   512 	EnableGameFlags(gfRandomOrder, gfBorder, gfSolidLand) --, gfInfAttack
       
   513 	HealthCaseProb = 0
  1185 	HealthCaseProb = 0
   514 	CaseFreq = 0
  1186 	CaseFreq = 0
   515 
  1187 
   516 end
  1188 end
   517 
  1189 
   556 
  1228 
   557 end
  1229 end
   558 
  1230 
   559 function onGameStart()
  1231 function onGameStart()
   560 
  1232 
   561 	LoadConfig(1)
  1233 	trackTeams()
       
  1234 
       
  1235 	MapsInit()
       
  1236 	LoadConfig(wallSetID)
   562 	configureWeapons()
  1237 	configureWeapons()
   563 	UpdateMenu()
  1238 
   564 	HandleStartingStage()
  1239 	-- ABL or KTL only make sense with at least 3 teams, otherwise we disable it
   565 
  1240 	if TeamsCount < 3 or ClansCount < 3 then
       
  1241 		attackRule = nil
       
  1242 	end
       
  1243 
       
  1244 	if useMenu then
       
  1245 		ShowMission(loc("Wall to wall"), loc("Please wait …"), "", 2, 300000)
       
  1246 		UpdateMenu()
       
  1247 	else
       
  1248 		if GetGameFlag(gfPlaceHog) then
       
  1249 			roundN = 2
       
  1250 			FinalizeMenu()
       
  1251 		else
       
  1252 			allowCrate = false
       
  1253 			roundN = 100
       
  1254 			FinalizeMenu()
       
  1255 		end
       
  1256 	end
   566 end
  1257 end
   567 
  1258 
   568 function onNewTurn()
  1259 function onNewTurn()
       
  1260 	turnsCount = turnsCount + 1
       
  1261 
       
  1262 	if roundN == 0 then
       
  1263 		roundN = 1
       
  1264 	end
       
  1265 
       
  1266 	if GetGameFlag(gfPlaceHog) then
       
  1267 		if roundN < 2 then
       
  1268 			SetWeapon(amSkip)
       
  1269 			AddAmmo(CurrentHedgehog, amTeleport, 0)
       
  1270 			TurnTimeLeft = -1
       
  1271 			SetInputMask(0)
       
  1272 		end
       
  1273 		if roundN == 2 then
       
  1274 			if turnsCount > hogCount then
       
  1275 				roundN = 100
       
  1276 			end
       
  1277 		end
       
  1278 	end
   569 
  1279 
   570 	wallsLeft = #wTouched
  1280 	wallsLeft = #wTouched
   571 
  1281 
   572 	for i = 1, #wTouched do
  1282 	for i = 1, #wTouched do
   573 		wTouched[i] = false
  1283 		wTouched[i] = false
   574 	end
  1284 	end
   575 
  1285 
   576 	allowCrate = true
       
   577 
       
   578 	hasSurfed = false
  1286 	hasSurfed = false
   579 	allWallsHit = false
  1287 	allWallsHit = false
   580 
  1288 	crateCollected = false
   581 	crateG = nil
  1289 
   582 
  1290 	crateSpawned = false
   583 	-- new config stuff
  1291 
   584 	roundN = roundN + 1
  1292 	if roundN == 100 then
   585 	if roundN < 2 then
  1293 		allowCrate = crateGearsInGame < maxCrates
       
  1294 
       
  1295 		local teamName = GetHogTeamName(CurrentHedgehog)
       
  1296 
       
  1297 		-- Restore team's radar mode
       
  1298 		radarMode = getTeamValue(teamName, "radarMode")
       
  1299 
       
  1300 		if not AreCratesUnlocked() then
       
  1301 			FreezeCrates()
       
  1302 		end
       
  1303 
       
  1304 		-- Check the attack rule violation of the *previous* team and apply penalties
       
  1305 		-- This function will do nothiong in the first turn since previousTeam is still nil
       
  1306 		CheckAttackRuleViolation(previousTeam)
       
  1307 
       
  1308 		previousTeam = teamName
       
  1309 
       
  1310 		-- Update attack rule information for this turn
       
  1311 		UpdateLastAndLeaderTeams()
       
  1312 		teamsAttacked = {}
       
  1313 
       
  1314 		-- Was the team violating the attackRule the last time?
       
  1315 		if getTeamValue(teamName, "skipPenalty") then
       
  1316 			-- Then take away this turn
       
  1317 			AddCaption(string.format(loc("%s must skip this turn for rule violation ."), teamName), msgColorWarn, capgrpMessage)
       
  1318 			TurnTimeLeft = 0
       
  1319 			setTeamValue(teamName, "skipPenalty", false)
       
  1320 		end
       
  1321 
       
  1322 	else
       
  1323 		allowCrate = false
       
  1324 	end
       
  1325 
       
  1326 	if roundN == 1 then
   586 		TurnTimeLeft = -1
  1327 		TurnTimeLeft = -1
   587 		SetInputMask(0)
  1328 		SetInputMask(0)
   588 		allowCrate = false
  1329 		allowCrate = false
   589 		HandleStartingStage() -- new
  1330 		UpdateMenu()
   590 	end
  1331 		AddCaption(string.format(loc("%s may choose the rules."), GetHogTeamName(CurrentHedgehog)), msgColorTech, capgrpGameState)
   591 
  1332 		HandleStartingStage()
       
  1333 	end
       
  1334 
       
  1335 end
       
  1336 
       
  1337 function CanSurf()
       
  1338 	if mapID ~= nil then
       
  1339 		if GetGameFlag(gfBottomBorder) and WaterRise == 0 then
       
  1340 			return false
       
  1341 		else
       
  1342 			return MapList[mapID][2]
       
  1343 		end
       
  1344 	else
       
  1345 		return nil
       
  1346 	end
   592 end
  1347 end
   593 
  1348 
   594 function UpdateMenu()
  1349 function UpdateMenu()
   595 
  1350 	local teamInfo
   596 	preMenuCfg = loc("Spawn the crate, and attack!") .. "|"
  1351 	if roundN == 1 and CurrentHedgehog ~= nil then
   597 	postMenuCfg = loc("Press [Enter] to accept this configuration.")
  1352 		teamInfo = string.format(loc("%s, you may choose the rules."), GetHogTeamName(CurrentHedgehog)) 
   598 
  1353 	else
   599 	menu = 	{
  1354 		teamInfo = ""
   600 			loc("Walls Required") .. ": " .. #wTouched .. "|",
  1355 	end
   601 			loc("Surf Before Crate") .. ": " .. BoolToCfgTxt(requireSurfer) .. "|",
  1356 	preMenuCfg =	teamInfo .. "|" ..
   602 			loc("Attack From Rope") .. ": " .. BoolToCfgTxt(AFR) .. "|",
  1357 			loc("Press [Up] and [Down] to move between menu items.|Press [Attack], [Left], or [Right] to toggle.") .. "|"
   603 			loc("Super Weapons") .. ": " .. BoolToCfgTxt(allowCrazyWeps) .. "|"
  1358 	if GetGameFlag(gfPlaceHog) then
   604 			}
  1359 		postMenuCfg = loc("Press [Long jump] to accept this configuration and begin placing hedgehogs.")
       
  1360 	else
       
  1361 		postMenuCfg = loc("Press [Long jump] to accept this configuration and start the game.")
       
  1362 	end
       
  1363 
       
  1364 	-- This table contains the menu strings and functions to be called when the entry is activated.
       
  1365 	menu = {}
       
  1366 
       
  1367 	-- Walls required (hidden if the current settings don't allow for any walls)
       
  1368 	if #wallSets > 0 then
       
  1369 		local line
       
  1370 		if #wTouched > 0 then
       
  1371 			if wallSets[wallSetID].custom then
       
  1372 				line = string.format(loc("Wall set: %s (%d walls)"), wallSets[wallSetID].desc, #wTouched) .. "|"
       
  1373 			else
       
  1374 				line = string.format(loc("Wall set: %s"), wallSets[wallSetID].desc) .. "|"
       
  1375 			end
       
  1376 		else
       
  1377 			line = loc("Wall set: No walls") .. "|"
       
  1378 		end
       
  1379 		table.insert(menu, {
       
  1380 			line = line,
       
  1381 			doNext = function()
       
  1382 				wallSetID = wallSetID + 1
       
  1383 				if wallSetID > #wallSets then
       
  1384 					wallSetID = 0
       
  1385 				end
       
  1386 				LoadConfig(wallSetID)
       
  1387 			end,
       
  1388 			doPrev = function()
       
  1389 				wallSetID = wallSetID - 1
       
  1390 				if wallSetID < 0 then
       
  1391 					wallSetID = #wallSets
       
  1392 				end
       
  1393 				LoadConfig(wallSetID)
       
  1394 			end,
       
  1395 		})
       
  1396 	end
       
  1397 
       
  1398 	-- Surf Before Crate (hidden if map disabled it)
       
  1399 	if CanSurf() == true or CanSurf() == nil then
       
  1400 		local toggleSurf = function() requireSurfer = not(requireSurfer) end
       
  1401 		table.insert(menu, {
       
  1402 			line = string.format(loc("Surf Before Crate: %s"), BoolToCfgTxt(requireSurfer)) .. "|",
       
  1403 			activate = function() requireSurfer = not requireSurfer end,
       
  1404 		})
       
  1405 	end
       
  1406 
       
  1407 	-- Attack From Rope
       
  1408 	table.insert(menu, {
       
  1409 		line = string.format(loc("Attack From Rope: %s"), BoolToCfgTxt(AFR)) .. "|",
       
  1410 		activate = function() AFR = not AFR end,
       
  1411 	})
       
  1412 
       
  1413 	-- Crate Before Attack
       
  1414 	table.insert(menu, {
       
  1415 		line = string.format(loc("Crate Before Attack: %s"), BoolToCfgTxt(CBA)) .. "|",
       
  1416 		activate = function() CBA = not CBA end,
       
  1417 	})
       
  1418 
       
  1419 	if TeamsCount >= 3 then
       
  1420 		-- Attack rule (Disabled / All But Last / Kill The Leader)
       
  1421 		table.insert(menu, {
       
  1422 			line = string.format(loc("Attack rule: %s"), AttackRuleToCfgTxt(attackRule)) .. "|",
       
  1423 			doNext = function()
       
  1424 				if attackRule == nil then
       
  1425 					attackRule = "ABL"
       
  1426 				elseif attackRule == "ABL" then
       
  1427 					attackRule = "KTL"
       
  1428 				elseif attackRule == "KTL" then
       
  1429 					attackRule = nil
       
  1430 				end
       
  1431 			end,
       
  1432 			doPrev = function()
       
  1433 				if attackRule == nil then
       
  1434 					attackRule = "KTL"
       
  1435 				elseif attackRule == "ABL" then
       
  1436 					attackRule = nil 
       
  1437 				elseif attackRule == "KTL" then
       
  1438 					attackRule = "ABL"
       
  1439 				end
       
  1440 			end,
       
  1441 		})
       
  1442 	end
       
  1443 
       
  1444 	-- Super weapons
       
  1445 	table.insert(menu, {
       
  1446 		line = string.format(loc("Super weapons: %s"), BoolToCfgTxt(allowCrazyWeps)) .. "|",
       
  1447 		activate = function() allowCrazyWeps = not allowCrazyWeps end,
       
  1448 	})
       
  1449 
       
  1450 	-- Number of crates which appear per turn
       
  1451 	if maxCrates > 1 then
       
  1452 		table.insert(menu, {
       
  1453 			line = string.format(loc("Crates per turn: %d"), cratesPerTurn) .. "|",
       
  1454 			doNext = function()
       
  1455 				cratesPerTurn = cratesPerTurn + 1
       
  1456 				if cratesPerTurn > maxCrates then
       
  1457 					cratesPerTurn = 1
       
  1458 				end
       
  1459 			end,
       
  1460 			doPrev = function()
       
  1461 				cratesPerTurn = cratesPerTurn - 1
       
  1462 				if cratesPerTurn < 1 then
       
  1463 					cratesPerTurn = maxCrates
       
  1464 				end
       
  1465 			end,
       
  1466 		})
       
  1467 	end
       
  1468 end
       
  1469 
       
  1470 function FinalizeMenu()
       
  1471 	local text = ""
       
  1472 	local showTime = 3000
       
  1473 	if #wTouched == 0 and not requireSurfer then
       
  1474 		text = text .. loc("Collect the crate and attack!") .. "|"
       
  1475 	else
       
  1476 		text = text .. loc("Spawn the crate and attack!") .. "|"
       
  1477 	end
       
  1478 
       
  1479 	-- Expose a few selected game flags
       
  1480 	if GetGameFlag(gfPlaceHog)  then
       
  1481 		text = text .. loc("Place hedgehogs: Place your hedgehogs at the start of the game.") .. "|"
       
  1482 		showTime = 6000
       
  1483 	end
       
  1484 	if GetGameFlag(gfResetWeps) then
       
  1485 		text = text .. loc("Weapons reset: The weapons are reset after each turn.") .. "|"
       
  1486 	end
       
  1487 
       
  1488 	-- Show the WxW rules
       
  1489 	if #wTouched == 1 then
       
  1490 		text = text .. loc("Wall Before Crate: You must touch the marked wall before you can get crates.") .. "|"
       
  1491 	elseif #wTouched > 0 then
       
  1492 		text = text .. string.format(loc("Walls Before Crate: You must touch the %d marked walls before you can get crates."), #wTouched) .. "|"
       
  1493 	end
       
  1494 
       
  1495 	if requireSurfer then
       
  1496 		text = text .. loc("Surf Before Crate: You must bounce off the water once before you can get crates.") .. "|"
       
  1497 	end
       
  1498 
       
  1499 	if AFR then
       
  1500 		text = text .. loc("Attack From Rope: You may only attack from a rope.") .. "|"
       
  1501 	end
       
  1502 
       
  1503 	if CBA then
       
  1504 		text = text .. loc("Crate Before Attack: You must collect a crate before you can attack.") .. "|"
       
  1505 	end
       
  1506 
       
  1507 	if attackRule == "ABL" then
       
  1508 		text = text .. loc("All But Last: You must not solely attack the team with the least health") .. "|"
       
  1509 	elseif attackRule == "KTL" then
       
  1510 		text = text .. loc("Kill The Leader: You must also hit the team with the most health.") .. "|"
       
  1511 	end
       
  1512 	if attackRule ~= nil then
       
  1513 		text = text .. loc("Penalty: If you violate above rule, you have to skip in the next turn.") .. "|"
       
  1514 	end
       
  1515 
       
  1516 	if allowCrazyWeps then
       
  1517 		text = text .. loc("Super weapons: A few crates contain very powerful weapons.") .. "|"
       
  1518 	end
       
  1519 
       
  1520 	ShowMission(loc("Wall to wall"), loc("A Shoppa minigame"), text, 1, showTime)
   605 end
  1521 end
   606 
  1522 
   607 function HandleStartingStage()
  1523 function HandleStartingStage()
   608 
  1524 
   609 	temp = menu[menuIndex]
  1525 	temp = menu[menuIndex].line
   610 	menu[menuIndex] = "--> " .. menu[menuIndex]
  1526 	menu[menuIndex].line = "--> " .. menu[menuIndex].line
   611 
  1527 
   612 	missionComment = ""
  1528 	missionComment = ""
   613 	for i = 1, #menu do
  1529 	for i = 1, #menu do
   614 		missionComment = missionComment .. menu[i]
  1530 		missionComment = missionComment .. menu[i].line
   615 	end
  1531 	end
   616 
  1532 
   617 	ShowMission	(
  1533 	ShowMission	(
   618 				loc("WALL TO WALL") .. " 0.4",
  1534 				loc("Wall to wall"),
   619 				loc("a shoppa minigame"),
  1535 				loc("Configuration phase"),
   620 				preMenuCfg..
  1536 				preMenuCfg..
   621 				missionComment ..
  1537 				missionComment ..
   622 				postMenuCfg ..
  1538 				postMenuCfg ..
   623 				--" " .. "|" ..
  1539 				"", 2, 300000
   624 				"", 4, 300000
       
   625 				)
  1540 				)
   626 
  1541 
   627 	menu[menuIndex] = temp
  1542 	menu[menuIndex].line = temp
   628 
  1543 
   629 end
  1544 end
   630 
  1545 
   631 function onGameTick()
  1546 function onGameTick()
   632 
  1547 
   633 	if CurrentHedgehog ~= nil then
  1548 	if CurrentHedgehog ~= nil and roundN >= 0 then
   634 
       
   635 		--AddCaption(Map)
       
   636 		--AddCaption(RightX ..";" .. GetX(CurrentHedgehog))
       
   637 
  1549 
   638 		gTimer = gTimer + 1
  1550 		gTimer = gTimer + 1
   639 		if gTimer == 25 then
  1551 		if gTimer == 25 then
   640 			gTimer = 1
  1552 			gTimer = 1
   641 
  1553 
   642 			CheckForWallCollision()
  1554 			if roundN == 100 then
   643 			CheckCrateConditions()
  1555 				CheckForWallCollision()
   644 
  1556 				CheckCrateConditions()
   645 			if (crateG == GetFollowGear()) and (crateG ~= nil) then
  1557 
   646 				FollowGear(CurrentHedgehog)
  1558 				if (GetGearType(GetFollowGear()) == gtCase) then
   647 			end
  1559 					FollowGear(CurrentHedgehog)
   648 
  1560 				end
   649 			-- if attackfromrope is set, forbid firing unless using rope
  1561 				
   650 			if (AFR == true) and (roundN >= 2) then
  1562 				-- AFR and CBA handling
   651 				if (GetCurAmmoType() == amRope) or
  1563 				local allowAttack = true
       
  1564 				local shootException
       
  1565 				shootException = (GetCurAmmoType() == amRope) or
   652 					(GetCurAmmoType() == amSkip) or
  1566 					(GetCurAmmoType() == amSkip) or
   653 					(GetCurAmmoType() == amNothing)
  1567 					(GetCurAmmoType() == amNothing)
   654 				then
  1568 				-- If Attack From Rope is set, forbid firing unless using rope
   655 					SetInputMask(0xFFFFFFFF)
  1569 				if AFR then
   656 				elseif ropeG == nil then
  1570 					if ropeG == nil then
   657 					SetInputMask(bnot(gmAttack))
  1571 						allowAttack = false
       
  1572 					end
       
  1573 				end
       
  1574 				-- If Crate Before Attack is set, forbid firing if crate is not collected
       
  1575 				if CBA then
       
  1576 					if not crateCollected then
       
  1577 						allowAttack = false
       
  1578 					end
       
  1579 				end
       
  1580 				if allowAttack or shootException then
       
  1581 					SetInputMask(bor(GetInputMask(), gmAttack))
       
  1582 					if CBA then
       
  1583 						SetInputMask(bor(GetInputMask(), gmLJump))
       
  1584 					end
       
  1585 				else
       
  1586 					if CBA then
       
  1587 						if ropeG == nil then
       
  1588 							SetInputMask(band(GetInputMask(), bnot(gmAttack)))
       
  1589 							SetInputMask(bor(GetInputMask(), gmLJump))
       
  1590 						else
       
  1591 							SetInputMask(bor(GetInputMask(), gmAttack))
       
  1592 							SetInputMask(band(GetInputMask(), bnot(gmLJump)))
       
  1593 						end
       
  1594 					else
       
  1595 						SetInputMask(band(GetInputMask(), bnot(gmAttack)))
       
  1596 					end
   658 				end
  1597 				end
   659 			end
  1598 			end
   660 
  1599 
   661 		end
  1600 		end
   662 
  1601 
   671 
  1610 
   672 	if GetGearType(gear) == gtRope then
  1611 	if GetGearType(gear) == gtRope then
   673 		ropeG = gear
  1612 		ropeG = gear
   674 	elseif GetGearType(gear) == gtCase then
  1613 	elseif GetGearType(gear) == gtCase then
   675 
  1614 
   676 		crateG = gear
  1615 		crates[gear] = true
       
  1616 		crateGearsInGame = crateGearsInGame + 1
       
  1617 
   677 		trackGear(gear)
  1618 		trackGear(gear)
   678 
  1619 
   679 		table.insert(rCirc, AddVisualGear(0,0,vgtCircle,0,true) )
  1620 		table.insert(rCirc, AddVisualGear(0,0,vgtCircle,0,true) )
   680 		setGearValue(gear,"CIRC",rCirc[#rCirc])
  1621 		setGearValue(gear,"CIRC",rCirc[#rCirc])
   681 		setGearValue(gear,"RX",0)
  1622 		setGearValue(gear,"RX",0)
   682 		setGearValue(gear,"RY",0)
  1623 		setGearValue(gear,"RY",0)
   683 		SetVisualGearValues(rCirc[#rCirc], 0, 0, 100, 255, 1, 10, 0, 40, 3, 0xff00ffff)
  1624 		SetVisualGearValues(rCirc[#rCirc], 0, 0, 100, 255, 1, 10, 0, 40, 3, 0xff00ffff)
   684 
  1625 
   685 		allowCrate = false
  1626 		allowCrate = false
       
  1627 		crateSpawned = true
   686 
  1628 
   687 		rPingTimer = 0
  1629 		rPingTimer = 0
   688 		rAlpha = 0
  1630 		rAlpha = 0
   689 
  1631 
       
  1632 	elseif GetGearType(gear) == gtHedgehog then
       
  1633 		trackGear(gear)
       
  1634 		local teamName = GetHogTeamName(gear)
       
  1635 		-- Initialize radar mode to “on” and set other team values
       
  1636 		setTeamValue(teamName, "radarMode", 0)
       
  1637 		setTeamValue(teamName, "skipPenalty", false)
       
  1638 
       
  1639 		if getTeamValue(teamName, "hogs") == nil then
       
  1640 			setTeamValue(teamName, "hogs", 1)
       
  1641 		else
       
  1642 			increaseTeamValue(teamName, "hogs")
       
  1643 		end
       
  1644 		hogCount = hogCount + 1
       
  1645 		teamNames[GetHogTeamName(gear)] = true
   690 	end
  1646 	end
   691 
  1647 
   692 end
  1648 end
   693 
  1649 
   694 function onGearDelete(gear)
  1650 function onGearDelete(gear)
   695 
  1651 
   696 	if gear == ropeG then
  1652 	local gt = GetGearType(gear)
       
  1653 	if gt == gtRope then
   697 		ropeG = nil
  1654 		ropeG = nil
   698 	elseif GetGearType(gear) == gtCase then
  1655 	elseif gt == gtCase then
   699 
  1656 
   700 		if gear == crateG then
  1657 		crates[gear] = nil
   701 			crateG = nil
  1658 		crateGearsInGame = crateGearsInGame - 1
   702 		--	rAlpha = 255
       
   703 		end
       
   704 
  1659 
   705 		for i = 1, #rCirc do
  1660 		for i = 1, #rCirc do
   706 			if rCirc[i] == getGearValue(gear,"CIRC") then
  1661 			if rCirc[i] == getGearValue(gear,"CIRC") then
   707 				DeleteVisualGear(rCirc[i])
  1662 				DeleteVisualGear(rCirc[i])
   708 				table.remove(rCirc, i)
  1663 				table.remove(rCirc, i)
   709 			end
  1664 			end
   710 		end
  1665 		end
   711 
  1666 
   712 		trackDeletion(gear)
  1667 		trackDeletion(gear)
   713 
  1668 
       
  1669 		-- Was crate collected?
       
  1670 		if band(GetGearMessage(gear), gmDestroy) ~= 0 then
       
  1671 			crateCollected = true
       
  1672 		end
       
  1673 
       
  1674 	elseif gt == gtHedgehog then
       
  1675 		teamsAttacked[GetHogTeamName(gear)] = true
       
  1676 		decreaseTeamValue(GetHogTeamName(gear), "hogs")
       
  1677 		trackDeletion(gear)
       
  1678 	end
       
  1679 
       
  1680 end
       
  1681 
       
  1682 function onGearDamage(gear)
       
  1683 
       
  1684 	if GetGearType(gear) == gtHedgehog then
       
  1685 		teamsAttacked[GetHogTeamName(gear)] = true
       
  1686 	end
       
  1687 
       
  1688 end
       
  1689 
       
  1690 -- Check which team is the last and which is the leader (used for ABL and KTL)
       
  1691 function UpdateLastAndLeaderTeams()
       
  1692 	local teamHealths = {}
       
  1693 
       
  1694 	for team, x in pairs(teamNames) do
       
  1695 		UpdateTeamHealth(team)
       
  1696 		local totalHealth = getTeamValue(team, "totalHealth")
       
  1697 		if totalHealth > 0 then
       
  1698 			table.insert(teamHealths, {name = team, health = totalHealth } )
       
  1699 		end
       
  1700 	end
       
  1701 
       
  1702 	-- Sort the table by health, lowest health comes first
       
  1703 	table.sort(teamHealths, function(team1, team2) return team1.health < team2.health end)
       
  1704 
       
  1705 	-- ABL and KTL rules are only active at 3 teams; when there are only 2 teams left, it's “everything goes”.
       
  1706 	if #teamHealths >= 3 then
       
  1707 		if teamHealths[1].health == teamHealths[2].health then
       
  1708 			-- ABL rule is disabled if it's a tie for “least health”
       
  1709 			lastTeam = nil
       
  1710 		else
       
  1711 			-- Normal assignment of ABL variable
       
  1712 			lastTeam = teamHealths[1].name
       
  1713 		end
       
  1714 		if teamHealths[#teamHealths].health == teamHealths[#teamHealths-1].health then
       
  1715 			-- KTL rule is disabled if it's a tie for “most health”
       
  1716 			leaderTeam = nil
       
  1717 			runnerUpTeam = nil
       
  1718 		else
       
  1719 			-- Normal assignment of KTL variables
       
  1720 			leaderTeam = teamHealths[#teamHealths].name
       
  1721 			runnerUpTeam = teamHealths[#teamHealths-1].name
       
  1722 		end
       
  1723 	else
       
  1724 		-- The KTL and ABL rules are disabled with only 2 teams left
       
  1725 		lastTeam = nil
       
  1726 		runnerUpTeam = nil
       
  1727 		leaderTeam = nil
       
  1728 	end
       
  1729 end
       
  1730 
       
  1731 function UpdateTeamHealth(team)
       
  1732 	setTeamValue(team, "totalHealth", 0)
       
  1733 	runOnHogsInTeam(function(hog)
       
  1734 		if(GetGearType(hog) ~= gtHedgehog) then return end
       
  1735 		local h = getTeamValue(GetHogTeamName(hog), "totalHealth")
       
  1736 		setTeamValue(GetHogTeamName(hog), "totalHealth", h + GetHealth(hog))
       
  1737 	end, team)
       
  1738 end
       
  1739 
       
  1740 -- Check if the ABL or KTL rule (if active) has been violated by teamToCheck
       
  1741 function CheckAttackRuleViolation(teamToCheck)
       
  1742 
       
  1743 	if teamToCheck == nil then return end
       
  1744 
       
  1745 	local violated = false
       
  1746 	if attackRule == "ABL" then
       
  1747 		-- We don't care if the last team hurts itself
       
  1748 		if lastTeam ~= nil and lastTeam ~= teamToCheck then
       
  1749 			local lastAttacked = false
       
  1750 			local attackNum = 0	-- count the attacked teams but we'll ignore the attacking team
       
  1751 			for team, wasAttacked in pairs(teamsAttacked) do
       
  1752 				-- Ignore the attacking team
       
  1753 				if team ~= teamToCheck then
       
  1754 					attackNum = attackNum + 1
       
  1755 					if team == lastTeam then
       
  1756 						lastAttacked = true
       
  1757 					end
       
  1758 				end
       
  1759 			end
       
  1760 			-- Rule is violated iff only the last team is attacked (damage to attacking team is ignored)
       
  1761 			if attackNum == 1 and lastAttacked then
       
  1762 				violated = true
       
  1763 			end
       
  1764 		end
       
  1765 		if violated then
       
  1766 			AddCaption(string.format(loc("%s violated the “All But Last” rule and will be penalized."), teamToCheck), msgColorWarn, capgrpGameState)
       
  1767 		end
       
  1768 	elseif attackRule == "KTL" then
       
  1769 		local leaderAttacked = false
       
  1770 		if leaderTeam ~= nil then
       
  1771 			local attackNum = 0
       
  1772 			local selfHarm = false
       
  1773 			for team, wasAttacked in pairs(teamsAttacked) do
       
  1774 				attackNum = attackNum + 1
       
  1775 				if team == teamToCheck then
       
  1776 					selfHarm = true
       
  1777 				end
       
  1778 				-- The leader must attack the runner-up, everyone else must attack the leader
       
  1779 				if (teamToCheck ~= leaderTeam and team == leaderTeam) or (teamToCheck == leaderTeam and team == runnerUpTeam) then
       
  1780 					leaderAttacked = true
       
  1781 					break
       
  1782 				end
       
  1783 			end
       
  1784 			-- If teams were attacked but not the leader, it is a violation,
       
  1785 			-- but we don't care if the team *only* harmed itself.
       
  1786 			if (attackNum >= 2 and not leaderAttacked) or (attackNum == 1 and not selfHarm and not leaderAttacked) then
       
  1787 				violated = true
       
  1788 			end
       
  1789 		end
       
  1790 		if violated then
       
  1791 			AddCaption(string.format(loc("%s violated the “Kill The Leader” rule and will be penalized."), teamToCheck), msgColorWarn, capgrpGameState)
       
  1792 		end
       
  1793 	end
       
  1794 	if violated then
       
  1795 		setTeamValue(teamToCheck, "skipPenalty", true)
   714 	end
  1796 	end
   715 
  1797 
   716 end
  1798 end
   717 
  1799 
   718 function onAmmoStoreInit()
  1800 function onAmmoStoreInit()