--- a/share/hedgewars/Data/Scripts/Multiplayer/WxW.lua Thu Nov 24 06:33:00 2016 +0100
+++ b/share/hedgewars/Data/Scripts/Multiplayer/WxW.lua Thu Nov 24 15:09:26 2016 +0100
@@ -1,6 +1,6 @@
----------------------
--- WALL TO WALL 0.4
+-- WALL TO WALL 0.7
----------------------
-- a shoppa minigame
-- by mikade
@@ -44,12 +44,154 @@
-- added backwards compatibility with 0.9.17
----------------
---TO DO
+--0.5
+----------------
+-- Support for multiple sets of walls per map (instead of “all or nothing”)
+-- Ropes, ShoppaKing, ShoppaHell and ShoppaNeon can now be played with the classic left and right walls
+-- New wall sets for Ropes, ShoppaNeon, ShoppaDesert, ShoppaWild, ShoppaKing and ShoppaHell, and more.
+-- Basic support for a bunch of Shoppa maps
+-- Alternative configuration method with Script parameter
+-- Possible to set max. number of weapons in game (script parameter only)
+-- Possible to set number of crates per turn
+-- Menu can be disabled (with script parameter) for insant game start
+-- WxW is now fully functional even without a map border.
+-- WxW now allows for almost all game modifiers and game settings to be changed
+-- More sound effects
+-- No smoke when hog is near near a WxW wall but Walls Before Crate rule is not in place
+-- More readable mission display after configuration has been accepted
+-- Hide “Surf Before Crate” setting if surfing is disabled for this map, or the bottom is active and water never rises
+-- Hide walls setting if script does not provide walls for map yet
+-- Bugfix: Other player was able to change the menu config in the short period before the first "turn"
+-- Lots of refactoring
+
+----------------
+--0.6
+----------------
+-- Bugfix: 2 crates spawned at the 1st turn if script parameter was set to “menu=false, walls=none” or similar
+-- Bugfix: Annoying faulty error message appeared when hitting attack when on a rope with a weapon selected
+
+
+----------------
+--0.7
+----------------
+-- To enforce the rules more strictly, all crates will be frozen at turn start if WBC or SBC rule is in place.
+-- The crates are unfrozen if you met the crate criteria (that is, surfed and/or bounced off all walls).
+-- Frozen crates can't be collected and appear as small white dots in the radar.
+-- Add support for the “Crate Before Attack” rule
+-- Add support for the “All But Last” rule
+-- Add support for the “Kill The Leader” rule
+-- Allow toggling crate radar with “switch hog” key while roping
+-- The game continues now with the first team after the menu has been closed (rather than the second team)
+
+----------------
+--TODO
----------------
-- achievements / try detect shoppa moves? :|
-- maybe add ability for the user to place zones like in Racer?
-- add more hard-coded values for specific maps
+
+--[[
+# CONFIGURATION
+
+By default, this script is easily configured via the in-game menu. The player of the first team can choose the rules and
+required walls (or none at all). After accepted, the game will start with the second team (!).
+
+= SCRIPT PARAMETER =
+
+Using the script parameter is optional, it mostly is just an alternative way for configuration and for convenience
+reasons, so often-used configurations can be saved and loaded.
+
+The script parameter is specified as a comma-sperated list of “key=value” pairs (see examples below).
+
+Truth values can be set true or false, and numeric values always use a whole number.
+
+== Basic parameters ==
+
+key default description
+----------------------------------------
+menu true Show configuration menu at the beginning. If no menu is used, a random wall set is used (see wall filters below)
+SBC false Surf Before Crate: Player must bounce off the water (“surfing”) before crates can be collected
+AFR false Attack From Rope: Players must attack from the rope. Weapons which can't be fired from rope are removed
+CBA false Crate Before Attack: Player must collect at least one crate before attacking
+attackrule off If present, enable one of the attack rules “ABL” or “KTL”:
+ ABL: All But Last: Players must not only attack the team with the lowest total health
+ KTL: Kill The Leader: If players hit some enemy hedgehog, at least one of them must be a hog from
+ the team with the highest total health.
+ The ABL and KTL rules exclude each other. If a player breaks the rule (if enabled), he must
+ skip in the next round.
+SW false Super Weapons: A few crates may contain very powerful weapons (melon, hellish grenade, RC plane, ballgun)
+maxcrates 12 Number of crates which can be at maximum in the game (limited to up to 100 to avoid lag)
+cratesperturn 1 Number of crates which appear each turn
+
+== Advanced parameters ==
+
+Wall filters: The following parameters allow you to filter out wall sets based on their type and number of walls.
+If this is used together with the menu, the filtered wall sets can't be selected. Without a menu, the wall set
+will be randomly selected among the wall sets that meet all criteria.
+
+If the criteria filter out all available wall sets of the map, the game is played without the Walls Before Crate rule.
+
+parameter default description
+----------------------------------------
+walls N/A
+
+Permitted values:
+- leftright: The left and right part of the border. Traditional W2W-style.
+- roof: Only the top part of the border
+- leftrightroof: Combination of the two above
+- inside: Map-specific wall set where all walls are part of the terrain
+- mixed: Map-specific wall set where some walls are part of the terrain, and some are part of the map border
+- none: No walls required.
+- all: Shorthand: All wall sets are allowed.
+
+Combination of multiple types is possible by concatenating the names with plus signs (see examples below).
+
+
+Restrict wall numbers: With the following parameters you can restrict the pool of wall sets to only those with a certain
+number of walls. Note that 2 walls are the most common type of wall set, as this is often available by default.
+
+parameter default description
+----------------------------------------
+minwalls N/A Filter out wall sets with less than this
+maxwalls N/A Filter out wall sets with more than this
+
+wallsnum N/A Shorthand: Combintion of minwalls and maxwalls if they are the equal.
+
+
+== Examples ==
+
+
+SBC=true
+--> Keep the menu, enable Surf Before Crate by default (if available).
+
+SBC=true, menu=false
+--> Enable Surf Before Crate (if available) and use the defaul walls set.
+
+AFR=true, menu=false, wallsnum=2
+--> Attack From Rope rule active, and use a random wall set with 2 walls
+
+menu=false, walls=leftright
+--> Always use the classic left/right wall set automatically. Traditional W2W-style.
+
+walls=none, menu=false
+--> Like classic Shoppa
+
+walls=leftright+inside+mixed, menu=false
+--> Randomly use either the left/right wall set, an Inside or Mixed wall set.
+
+
+
+= MORE GAME SCHEME CONFIGURATION =
+You can almost set everything in the game scheme freely, and the script will work just fine together with it.
+Feel free to experiment a bit.
+The only exception are the crate frequencies. Setting them has no effect, crates are handled uniquiely in this game.
+
+At this stage, the script does not allow for custom weapon sets.
+]]
+
+
+
-----------------------------
-- GO PONIES, GO PONIES, GO!
-----------------------------
@@ -57,37 +199,90 @@
HedgewarsScriptLoad("/Scripts/Locale.lua")
HedgewarsScriptLoad("/Scripts/Tracker.lua")
HedgewarsScriptLoad("/Scripts/Utils.lua")
+HedgewarsScriptLoad("/Scripts/Params.lua")
--- experimental menu stuff
+-- HARDCODED values
+local ammoTypesNum = 58 -- number of weapon types (permanent TODO: Check this number for each Hedgewars version)
+local PlacementTime = 15000
+
+-- menu stuff
local menuIndex = 1
local menu = {}
local preMenuCfg
local postMenuCfg
+
+--[[ WxW preparation phase.
+0 = Game not started yet
+1 = Configuration phase
+2 = Hedgehog placement phase
+100 = Game phase
+]]
local roundN = 0
+-- Used to select one of the wall sets
+-- 0: no walls
+-- 1 and above: ID of wall sets
+local wallSetID = 0
+
+-- Store the wall sets here
+local wallSets = {}
+
+-- Wall set types and wall number limits for filtering
+local allWallSetTypes = {"roof", "leftright", "leftrightroof", "mixed", "inside"}
+local allowedWallSetTypes = {roof=true, leftright=true, leftrightroof=true, mixed=true, inside=true}
+local minWalls, maxWalls = nil, nil
+
-- config and wall variables
-local AFR = false
-local allowCrazyWeps = false
-local requireSurfer = true
+local useMenu = true
+local AFR = false -- Attack From Rope
+local WBC = true -- Wall(s) Before Crate, will later only be set again in script parameter
+local CBA = false -- Crate Before Attack
+local attackRule = nil -- Either nil, "KTL" (Kill The Leader) or "ABL" (All But Last)
+local allowCrazyWeps = false -- Super weapons
+local requireSurfer = false -- Surf Before Crate
+local crateSpawned = false -- Has the crate (or crates) been spawned in this turn yet?
+local cratesPerTurn = 1 -- How many crates appear per turn (respects crate limit)
+local maxCrates = 12 -- default crate limit, can be configured with params
+local maxCratesHard = 100 -- "hard" crate limit, to avoid extreme lagging due to many crates
+local crateGearsInGame = 0
local wX = {}
local wY = {}
local wWidth = {}
local wHeight = {}
local wTouched = {}
---local margin
local wallsLeft = 0
local hasSurfed = false
local allWallsHit = false
+local crateCollected = false
+
+-- ABL and KTL stuff
+local teamNames = {} -- List of all teams
+local teamsAttacked = {} -- List of attacked teams (in this turn)
+local lastTeam = nil -- Team with the least health. Determined only at start of turn. If it's a tie, use nil.
+local leaderTeam = nil -- Team with the most health. Determined only at start of turn. If it's a tie, use nil.
+local runnerUpTeam = nil -- Team with the second-most health
+local previousTeam = nil -- Remember the name of the team in the previous turn
local gTimer = 1
local effectTimer = 1
local ropeG = nil
-local crateG = nil
local allowCrate = true
+local crates = {}
+
+-- Variables for place hedgehogs mode
+local hogCount = 0 -- Used to detect the end of the hog placement phase
+local turnsCount = 0
-- crate radar vars
+
+-- Set the initial radar mode here
+-- 0: Radar is always active
+-- 1: Radar is only active shortly after crate spawn
+-- 2: Radar is disabled
+local radarMode = 0
+
local rCirc = {}
local rAlpha = 255
local rPingTimer = 0
@@ -95,67 +290,179 @@
local weapons = {}
---[[local unlisted = {amTardis, amLandGun,amExtraTime,amExtraDamage,
- amVampiric, amSwitch, amInvulnerable, amGirder, amJetpack,
- amPortalGun, amTeleport, amResurrector, amLaserSight, amLowGravity,
- amAirAttack, amNapalm, amMineStrike, amDrillStrike,
- amKamikaze, amSnowball, amSeduction}]]
-
local crazyWeps = {amWatermelon, amHellishBomb, amBallgun, amRCPlane}
local groundWeps = {amBee, amShotgun,amDEagle,amFirePunch, amWhip,
amPickHammer, amBaseballBat, amCake,amBallgun,
- amRCPlane, amSniperRifle, amBirdy, amBlowTorch, amGasBomb,
- amFlamethrower, amSMine, amMortar, amHammer}
+ amRCPlane, amSniperRifle, amBirdy, amBlowTorch,
+ amFlamethrower, amMortar, amHammer}
local ropeWeps = {amGrenade, amClusterBomb, amBazooka, amMine, amDynamite,
- amWatermelon, amHellishBomb, amDrill, amMolotov}
+ amWatermelon, amHellishBomb, amDrill, amMolotov,
+ amSMine, amGasBomb}
+
+local msgColorTech = 0xFFBA00FF
+local msgColorWarn = 0xFF4000FF
-- 0.9.18+ extra custom data for preset maps
local MapList =
{
- --name, surfer, roof, LRwalls
- {"Atlantis Shoppa", true, false, true},
- {"BambooPlinko", true, false, true},
- {"BrickShoppa", false, false, true},
- {"BubbleFlow", true, false, true},
- {"Cave", false, false, true},
- {"Glass Shoppa", true, false, true},
- {"HardIce", false, false, true},
- {"Industrial", false, false, true},
- {"Islands", true, false, true},
- {"Hedgelove", true, false, true},
- {"NeonStyle", false, false, true},
- {"Octorama", false, false, true},
+ --name, surfer, roof, LRwalls
+ {"Alien", true, true, true},
+ {"Atlantis Shoppa", true, true, true},
+ {"BasketballField", false, false, false},
+ {"BattleCity_v1", true, true, true},
+ {"BIGshoppa", true, true, true},
+ {"BambooPlinko", true, false, true},
+ {"BoatWxW", true, true, true},
+ {"BrickShoppa", false, false, true},
+ {"BubbleFlow", true, false, true},
+ {"Citrouille", true, true, true},
+ {"Cave", false, false, true},
+ {"Cheese_Ropes", false, true, true},
+ {"CookieShoppa", true, false, true},
+ {"CrossRopes", false, false, true},
+ {"FutuShoppa", true, false, true},
+ {"Garden", false, false, true},
+ {"Glass Shoppa", true, false, true},
+ {"GlassShoppa2", true, false, true},
+ {"HardIce", false, false, true},
+ {"Industrial", false, false, true},
+ {"Islands", true, false, true},
+ {"IslandsFlipped", true, false, true},
+ {"IslandsRearranged", true, false, true},
+ {"Hedgelove", true, false, true},
+ {"HellishRopes", false, false, true},
+ {"Hedgeland_v1", true, false, true},
+ {"HeyLandShoppa", false, false, true},
+ {"NeonStyle", false, false, true},
+ {"MaskedRopes", false, false, true},
+ {"Octorama", false, false, true},
{"red vs blue - Castle", true, false, true},
{"red vs blue - castle2", true, false, true},
- {"red vs blue - True Shoppa Sky", true, false, true},
- {"Ropes", false, false, true},
- {"Ropes Rearranged", false, false, true},
+ {"red vs blue - True Shoppa Sky", true, false, true},
+ {"Ropes", false, false, true},
+ {"RopeLikeAKingInHellWithNeon", false, true, true},
+ {"Ropes Flipped", false, false, true},
+ {"Ropes Rearranged", false, false, true},
{"RopesRevenge Flipped", true, false, true},
- {"Ropes Three", false, false, true},
- {"RopesTwo", false, false, true},
- {"ShapeShoppa1.0", true, false, true},
- {"ShappeShoppa Darkhow", true, false, true},
- {"ShoppaCave2", true, false, true},
- {"ShoppaFun", true, false, true},
- {"ShoppaGolf", false, false, true},
- {"ShoppaHell", false, true, false},
- {"ShoppaKing", false, false, false},
- {"ShoppaNeon", false, false, true},
- {"ShoppaSky", false, false, true},
- {"Shoppawall", false, false, true},
- {"SkatePark", false, false, true},
- {"SloppyShoppa", false, false, true},
- {"Sticks", true, false, true},
- {"Symmetrical Ropes ", false, false, true},
- {"Tetris", false, false, true},
- {"TransRopes2", false, false, true},
- {"Wildmap", false, false, true},
- {"Winter Shoppa", false, false, true},
- {"2Cshoppa", true, false, true}
+ {"RopesThree", false, false, true},
+ {"RopesTwo", false, false, true},
+ {"Ruler", false, false, true},
+ {"SandShoppa", false, false, true},
+ {"ShapeShoppa1.0", true, false, true},
+ {"ShapeShoppa Darkhow", true, false, true},
+ {"SheepyShoppa_v2", true, false, true},
+ {"shopppa", false, true, true},
+ {"ShoppaCave2", true, false, true},
+ {"ShoppaChallenge", false, true, true},
+ {"ShoppaDesert", false, false, true},
+ {"ShoppaEvoRope_v1", true, false, true},
+ {"ShoppaFun", true, false, true},
+ {"ShoppaFun2", true, false, true},
+ {"ShoppaGolf", false, false, true},
+ {"ShoppaHalloween", false, false, true},
+ {"ShoppaHell", false, true, false},
+ {"ShoppaHellFlipped", true, true, false},
+ {"ShoppaHellRemake", false, true, false},
+ {"ShoppaKing", false, true, false},
+ {"ShoppaKingFlipped", true, false, false},
+ {"ShoppaKingSideways", true, true, false},
+ {"ShoppaMeme", false, true, false},
+ {"ShoppaNeon", false, false, true},
+ {"ShoppaNeonFlipped", true, false, true},
+ {"ShoppaOnePiece2", false, true, false},
+ {"ShoppaQuotes2", false, true, true},
+ {"ShoppaRainbow", false, false, false},
+ {"ShoppaRadigme", false, true, true},
+ {"ShoppaSilhouette", false, false, true},
+ {"ShoppaSpace", true, false, true},
+ {"ShoppaSea", true, false, false},
+ {"ShoppaShapex_v1", false, true, true},
+ {"ShoppaSparkle", true, true, true},
+ {"ShoppaSky", false, false, true},
+ {"ShoppaSky2", true, false, true},
+ {"ShoppaSsion", false, false, true},
+ {"ShoppaStyle2", true, false, true},
+ {"ShoppaThology", false, false, true},
+ {"ShoppaTournament2012", false, false, true},
+ {"ShoppaWild", false, false, true},
+ {"Shoppawall", false, false, false},
+ {"ShoppaWall2", false, false, false},
+ {"ShBall", false, true, false},
+ {"ShHell", false, true, false},
+ {"ShNeon", false, false, true},
+ {"ShoppaSky", false, false, true},
+ {"SloppyShoppa", false, true, true},
+ {"SloppyShoppa2", false, true, true},
+ {"SkatePark", false, true, true},
+ {"Snow_Ropes", false, true, false},
+ {"Sticks", true, false, true},
+ {"Symmetrical Ropes", false, false, true},
+ {"SpartanShoppa", false, true, true},
+ {"Tetris", false, false, true},
+ {"TransRopes2", false, false, true},
+ {"TRBShoppa", false, false, true},
+ {"TrickyShoppa", false, true, false},
+ {"Wildmap", false, false, true},
+ {"Winter Shoppa", false, false, true},
+ {"WarShoppa", false, true, true},
+ {"2Cshoppa", true, false, true},
}
+local Ropes_WallSet = {
+ { add="none", {299,932,20,856}, {4056,0,30,1788} },
+ { add="none", {299,109,20,779}, {4056,0,30,1788} },
+ { add="none", {299,109,20,779}, {299,932,20,856}, {4056,0,30,1788} },
+ { add="default", {2253,326,20,574}, {3280,326,33,253}, needsborder=false },
+ { add="roof", {2322,326,457,20} },
+ { add="default", {1092,934,54,262}, {2822,323,33,137}, needsborder=false },
+ { add="none", {203,1193,20,595}, {3280,326,20,253}, needsborder=false },
+}
+local Shoppawall_WallSet = {
+ { add="none", {80+290,61+878,20,1018}, {3433+290,61+878,20,1018}, default=true, needsborder=false },
+}
+
+-- List of map with special wall settings
+local SpecialMapList = {
+ ["Ropes"] = Ropes_WallSet,
+ ["HellishRopes"] = Ropes_WallSet,
+ ["MaskedRopes"] = Ropes_WallSet,
+ ["TransRopes2"] = Ropes_WallSet,
+ ["ShoppaKing"] = {
+ { add="none", {3777,1520,50,196}, {1658,338,46,670}, needsborder=false },
+ { add="none", {125,0,30,2048}, {4066,515,30,1528}, default=true},
+ },
+ ["ShoppaHell"] = {
+ { add="none", {3491,697,30,1150}, {0,0,30,1847}, default=true},
+ { add="none", {3810,0,30,1616}, {0,0,30,1847}, },
+ { add="none", {2045,832,20,260}, {2107,832,20,260}, needsborder=false },
+ { add="default", {2035,831,30,263}, {3968,1668,31,383}, needsborder=false },
+ },
+ ["ShoppaNeon"] = {
+ { add="default", {980,400,20,300}, {1940,400,20,300}, {3088,565,26,284}, {187,270,28,266}, needsborder=false },
+ },
+ ["Shoppawall"] = Shoppawall_WallSet,
+ ["ShoppaWall2"] = Shoppawall_WallSet,
+ ["ShoppaDesert"] = {
+ { add="none", {2322,349,20,471}, {295,93,24,1479}, needsborder=false },
+ { add="none", {3001,1535,20,232}, {2264,349,20,495},{716,696,20,119}, needsborder=false },
+ { add="leftright", {209,656,20,367},{2810,838,20,96}, needsborder=false},
+ { add="none", {2649,0,445,20}, {2322,349,947,20},{299,696,381,20}},
+ },
+ ["ShoppaOnePiece2"] = {
+ { add="default", {42,0,20,2048}, {4048,0,20,2048}, needsborder=false, },
+ { add="default", {42,0,20,2048}, {3852,273,20,1637}, needsborder=false, default="noborder" },
+ },
+ ["ShoppaWild"] = {
+ { add="default", {2123,1365,20,293}, {3102,1365,20,293}, {1215,1391,20,291}, needsborder=false },
+ { add="none", {144,167,1904,20}, {2350,167,753,20}, {3793,167,303,20}, needsborder=false},
+ },
+ ["ShoppaRainbow"] = {
+ { add="none", {67+602,61+80,20,1847}, {2779+602,61+80,20,1847}, needsborder=false },
+ },
+}
+
function BoolToCfgTxt(p)
if p == false then
return loc("Disabled")
@@ -164,15 +471,41 @@
end
end
-function LoadConfig(p)
+function AttackRuleToCfgTxt(attackRule)
+ if attackRule == nil then
+ return loc("Disabled")
+ elseif attackRule == "ABL" then
+ return loc("All But Last")
+ elseif attackRule == "KTL" then
+ return loc("Kill The Leader")
+ else
+ return "ERROR"
+ end
+end
+function NewWallSet(newWallSet, wType)
+ -- Filter out wall sets which are not in allowed categories or have too many or few walls
+ if allowedWallSetTypes[wType] == true then
+ local inBounds = true
+ if minWalls ~= nil and #newWallSet < minWalls then
+ inBounds = false
+ end
+ if maxWalls ~= nil and #newWallSet > maxWalls then
+ inBounds = false
+ end
+ if inBounds then
+ table.insert(wallSets, newWallSet)
+ end
+ end
+end
+
+function MapsInit()
+ mapID = nil
margin = 20
- mapID = nil
-- 0.9.17
if Map == "CHANGE_ME" then
AddCaption(loc("For improved features/stability, play 0.9.18+"))
- --AddWall(10,10,4085,margin)
AddWall(10,10,margin,2025)
AddWall(4085-margin,10,margin,2025)
end
@@ -181,52 +514,151 @@
for i = 1, #MapList do
if Map == MapList[i][1] then
mapID = i
- --AddCaption(MapList[i][1] .. " found. reqSurf is " .. BoolToCfgTxt(MapList[i][2]))
end
end
- if (p == 1) and (mapID ~= nil) then
- requireSurfer = MapList[mapID][2]
- end
+ local left, right, roof
+ left = {LeftX+10,TopY+10,margin,WaterLine}
+ right = {RightX-10-margin,TopY+10,margin,WaterLine}
+ roof = {LeftX+10,TopY+10,RightX-LeftX-20,margin}
+
+ local border = MapHasBorder()
if mapID ~= nil then
-
- -- add a wall to the roof
- if MapList[mapID][3] == true then
- AddWall(LeftX+10,TopY+10,RightX-LeftX-20,margin)
- end
-
- -- add walls on the left and right border
- if MapList[mapID][4] == true then
- AddWall(LeftX+10,TopY+10,margin,WaterLine)
- AddWall(RightX-10-margin,TopY+10,margin,WaterLine)
+ if border then
+ if MapList[mapID][3] == true then
+ NewWallSet({roof, desc=loc("Roof")}, "roof")
+ wallSetID = #wallSets
+ end
+ if MapList[mapID][4] == true then
+ NewWallSet({left, right, desc=loc("Left and right")}, "leftright")
+ wallSetID = #wallSets
+ end
+ if MapList[mapID][3] == true and MapList[mapID][4] == true then
+ NewWallSet({left, right, roof, desc=loc("Left, right and roof")}, "leftrightroof")
+ end
end
-- add map specific walls
- if Map == "Ropes" then
- AddWall(1092,934,54,262)
- AddWall(2822,323,33,137)
- elseif Map == "ShoppaKing" then
- AddWall(3777,1520,50,196)
- AddWall(1658,338,46,670)
- elseif Map == "ShoppaHell" then
- AddWall(2035,831,30,263)
- AddWall(3968,1668,31,383)
- elseif Map == "ShoppaNeon" then
- AddWall(980,400,20,300)
- AddWall(1940,400,20,300)
- AddWall(3088,565,26,284)
- AddWall(187,270,28,266)
+ if SpecialMapList[Map] ~= nil then
+ local insideID = 1
+ local previousInside = nil
+ local mixedID = 1
+ local previousMixed = nil
+
+ -- Helper function to build the wall set name.
+ -- Basically just to ensure that names like "Inside 1" are only used when there are at least 2 "Insides"
+ local function newInsideOrMixed(ws, previous_ws, id, string, stringD)
+ if id == 1 then
+ ws.desc = string
+ else
+ ws.desc = string.format(stringD, id)
+ end
+ if id == 2 then
+ previous_ws.desc = string.format(stringD, id-1)
+ end
+ id = id + 1
+ previous_ws = ws
+ return id, previous_ws
+ end
+ for ws=1,#SpecialMapList[Map] do
+ local walls = SpecialMapList[Map][ws]
+ if walls.needsborder == false then
+ local newwallset2 = {}
+ for w=1,#walls do
+ table.insert(newwallset2, walls[w])
+ end
+ insideID, previousInside = newInsideOrMixed(newwallset2, previousInside, insideID, loc("Inside"), loc("Inside %d"))
+ newwallset2.custom = true
+ NewWallSet(newwallset2, "inside")
+ if SpecialMapList[Map][ws].default == "noborder" then
+ wallSetID = #wallSets
+ end
+ end
+ local newwallset = {}
+ if border then
+ if walls.add == "all" then
+ table.insert(newwallset, roof)
+ table.insert(newwallset, left)
+ table.insert(newwallset, right)
+ elseif walls.add == "default" then
+ if MapList[mapID][3] == true then
+ table.insert(newwallset, roof)
+ end
+ if MapList[mapID][4] == true then
+ table.insert(newwallset, left)
+ table.insert(newwallset, right)
+ end
+ elseif walls.add == "roof" then
+ table.insert(newwallset, roof)
+ elseif walls.add == "leftright" then
+ table.insert(newwallset, left)
+ table.insert(newwallset, right)
+ end
+ end
+ for w=1,#walls do
+ table.insert(newwallset, walls[w])
+ end
+ if border and ((walls.add ~= "none" and walls.add ~= nil) or walls.needsborder ~= false) then
+ mixedID, previousMixed = newInsideOrMixed(newwallset, previousMixed, mixedID, loc("Mixed"), loc("Mixed %d"))
+ newwallset.custom = true
+ NewWallSet(newwallset, "mixed")
+ end
+ if SpecialMapList[Map][ws].default == true then
+ wallSetID = #wallSets
+ end
+ end
+ end
+
+ else
+ if border then
+ NewWallSet({roof, desc=loc("Roof")}, "roof")
+ NewWallSet({left, right, desc=loc("Left and right")}, "leftright")
+ NewWallSet({left, right, roof, desc=loc("Left, right and roof")}, "leftrightroof")
+ wallSetID = 2
+ end
+ end
+
+ -- Choose random map when without without menu
+ if useMenu == false and #wallSets > 0 then
+ wallSetID = GetRandom(#wallSets)+1
+ end
+ -- Select first wall set by default if we still haven't selected anything for some reason
+ if wallSetID == 0 and #wallSets > 0 then
+ wallSetID = 1
+ end
+ -- But disabled walls from script parameter have higher priority
+ if WBC == false then
+ wallSetID = 0
+ end
+
+ if CanSurf() == false then
+ requireSurfer = false
+ end
+end
+
+function LoadConfig(p)
+ ClearWalls()
+ if mapID ~= nil then
+ if p > 0 then
+ local walls = wallSets[p]
+ for i=1,#walls do
+ AddWall(walls[i][1], walls[i][2], walls[i][3], walls[i][4])
+ end
end
-- if map is unrecognized, add two walls on the side borders
-- also, if version of hw is not 0.9.17 or lower
elseif Map ~= "CHANGE_ME" then
- AddWall(LeftX+10,TopY+10,margin,WaterLine)
- AddWall(RightX-10-margin,TopY+10,margin,WaterLine)
+ if p == 1 or p == 3 then
+ AddWall(LeftX+10,TopY+10,RightX-LeftX-20,margin)
+ end
+ if p == 2 or p == 3 then
+ AddWall(LeftX+10,TopY+10,margin,WaterLine)
+ AddWall(RightX-10-margin,TopY+10,margin,WaterLine)
+ end
end
-
end
function AddWall(zXMin,zYMin, zWidth, zHeight)
@@ -239,11 +671,39 @@
end
+function ClearWalls()
+
+ wX = {}
+ wY = {}
+ wWidth = {}
+ wHeight = {}
+ wTouched = {}
+
+end
+
+-- Draw a single point for the crate radar
function DrawBlip(gear)
- SetVisualGearValues(getGearValue(gear,"CIRC"), getGearValue(gear,"RX"), getGearValue(gear,"RY"), 100, 255, 1, 10, 0, 40, 3, GetClanColor(GetHogClan(CurrentHedgehog))-rAlpha)
+ if GetGearType(gear) ~= gtCase then
+ return
+ end
+
+ local baseColor, radius, alpha
+ if getGearValue(gear, "frozen") then
+ radius = 25
+ baseColor = 0xFFFFFFFF
+ alpha = math.min(255, rAlpha+127)
+ else
+ radius = 40
+ baseColor = GetClanColor(GetHogClan(CurrentHedgehog))
+ alpha = rAlpha
+ end
+ SetVisualGearValues(getGearValue(gear,"CIRC"), getGearValue(gear,"RX"), getGearValue(gear,"RY"), 100, 255, 1, 10, 0, radius, 3, baseColor-alpha)
end
function TrackRadarBlip(gear)
+ if GetGearType(gear) ~= gtCase then
+ return
+ end
-- work out the distance to the target
g1X, g1Y = GetGearPosition(CurrentHedgehog)
@@ -294,21 +754,24 @@
function HandleCircles()
- -- enable this if you want the radar to only show for a few seconds
- -- after you spawn the crate
- --[[if rAlpha ~= 255 then
-
- rPingTimer = rPingTimer + 1
- if rPingTimer == 100 then
- rPingTimer = 0
-
- rAlpha = rAlpha + 5
- if rAlpha >= 255 then
- rAlpha = 255
+ if radarMode == 0 then
+ rAlpha = 0
+ elseif radarMode == 1 then
+ -- Only show radar for a short time after a crate spawn
+ if rAlpha ~= 255 then
+ rPingTimer = rPingTimer + 1
+ if rPingTimer == 100 then
+ rPingTimer = 0
+
+ rAlpha = rAlpha + 5
+ if rAlpha >= 255 then
+ rAlpha = 255
+ end
end
end
-
- end]]
+ elseif radarMode == 2 then
+ rAlpha = 255
+ end
runOnGears(DrawBlip)
@@ -324,10 +787,10 @@
end
+-- Returns true if crates are allowed to be accessed right now (used for unfreezing and spawning)
+function AreCratesUnlocked()
-function CheckCrateConditions()
-
- crateSpawn = true
+ local crateSpawn = true
if requireSurfer == true then
if hasSurfed == false then
@@ -341,14 +804,60 @@
end
end
- if crateSpawn == true then
+ return crateSpawn
+
+end
+
+-- Freeze all crates,
+function FreezeCrates()
+
+ local cratesFrozen = 0
+ for crate, isCrate in pairs(crates) do
+ local state = GetState(crate)
+ -- Freeze crate if it wasn't already frozen
+ if band(state, gstFrozen) == 0 then
+ cratesFrozen = cratesFrozen + 1
+ SetState(crate, bor(GetState(crate), gstFrozen))
+ setGearValue(crate, "frozen", true)
+ end
+ end
+ -- Play sound if at least one new (!) crate was frozen
+ if cratesFrozen > 0 then
+ PlaySound(sndHogFreeze)
+ end
+
+end
+
+-- Unfreeze all crates
+function UnfreezeCrates()
+
+ for crate, isCrate in pairs(crates) do
+ SetState(crate, band(GetState(crate), bnot(gstFrozen)))
+ setGearValue(crate, "frozen", false)
+ end
+
+end
+
+function CheckCrateConditions()
+
+ local crateSpawn = AreCratesUnlocked()
+
+ if crateSpawn == true and crateSpawned == false then
+ UnfreezeCrates()
if allowCrate == true then
- --if (crateG == nil) and (allowCrate == true) then
- --AddCaption("")
- SpawnAmmoCrate(0, 0, weapons[1+GetRandom(#weapons)] )
+ local cratesInGame = crateGearsInGame
+ local toSpawn = cratesPerTurn
+ if cratesInGame + toSpawn > maxCrates then
+ toSpawn = maxCrates - cratesInGame
+ end
+ for i=1,toSpawn do
+ SpawnAmmoCrate(0, 0, weapons[1+GetRandom(#weapons)] )
+ end
rPingTimer = 0
rAlpha = 0
- PlaySound(sndWarp)
+ if toSpawn > 0 then
+ PlaySound(sndWarp)
+ end
end
end
@@ -357,7 +866,7 @@
function onGearWaterSkip(gear)
if gear == CurrentHedgehog then
hasSurfed = true
- AddCaption(loc("Surfer!"),0xffba00ff,capgrpMessage2)
+ AddCaption(loc("Surfer!"), 0xFFFFFFFF, capgrpMessage2)
end
end
@@ -373,17 +882,18 @@
AddCaption(loc("All walls touched!"))
allWallsHit = true
if (requireSurfer == true) and (hasSurfed == false) then
- AddCaption(loc("Go surf!"),0xffba00ff,capgrpMessage2)
+ AddCaption(loc("Go surf!"), 0xFFFFFFFF, capgrpMessage2)
end
else
- AddCaption(loc("Walls Left") .. ": " .. wallsLeft)
+ AddCaption(string.format(loc("Walls left: %d"), wallsLeft))
end
end
wTouched[id] = true
- tempE = AddVisualGear(GetX(CurrentHedgehog), GetY(CurrentHedgehog), vgtSmoke, 0, false)
- --PlaySound(sndVaporize) -- yeah, this is just annoying as shit
+ if #wTouched > 0 then
+ tempE = AddVisualGear(GetX(CurrentHedgehog), GetY(CurrentHedgehog), vgtSmoke, 0, false)
+ end
end
@@ -419,75 +929,158 @@
effectTimer = 1
for i = 1, #wTouched do
- if wTouched[i] == true then
- --bCol = GetClanColor(GetHogClan(CurrentHedgehog))
- else
- --bCol = 0xFFFFFFFF
+ if wTouched[i] == false then
bCol = GetClanColor(GetHogClan(CurrentHedgehog))
BorderSpark(wX[i],wY[i],wWidth[i],wHeight[i], bCol)
end
- --BorderSpark(wX[i],wY[i],wWidth[i],wHeight[i], bCol)
end
end
end
+function PlaceWarn()
+ PlaySound(sndDenied)
+ AddCaption(loc("Please place your hedgehog first!"), msgColorWarn, capgrpMessage2)
+end
+
function onLJump()
- if roundN < 2 then
- roundN = 100
+ if roundN == 1 then
+ PlaySound(sndPlaced)
SetInputMask(0xFFFFFFFF)
- TurnTimeLeft = 1
- AddCaption(loc("Configuration accepted."),0xffba00ff,capgrpMessage)
- HideMission()
+ AddCaption(loc("Configuration accepted."), msgColorTech, capgrpMessage)
+ if GetGameFlag(gfPlaceHog) then
+ TurnTimeLeft = PlacementTime
+ AddAmmo(CurrentHedgehog, amTeleport, 100)
+ SetWeapon(amTeleport)
+ AddCaption(
+ string.format(loc("%s, place the first hedgehog!"), GetHogTeamName(CurrentHedgehog)),
+ 0xFFFFFFFF,
+ capgrpMessage2
+ )
+ roundN = 2
+ else
+ TurnTimeLeft = TurnTime
+ AddCaption(string.format(loc("Let's go, %s!"), GetHogTeamName(CurrentHedgehog)), 0xFFFFFFFF, capgrpMessage2)
+ roundN = 100
+ wallsLeft = #wTouched
+ allowCrate = true
+ end
+ PlaySound(sndYesSir, CurrentHedgehog)
+ FinalizeMenu()
+ elseif roundN == 2 then
+ PlaceWarn()
+ elseif roundN == 100 then
+ if CBA and not crateCollected then
+ if (GetCurAmmoType() ~= amRope) and
+ (GetCurAmmoType() ~= amSkip) and
+ (GetCurAmmoType() ~= amNothing) and
+ (ropeG ~= nil)
+ then
+ AddCaption(loc("You must first collect a crate before you attack!"), msgColorWarn, capgrpMessage2)
+ PlaySound(sndDenied)
+ end
+ end
end
end
function onAttack()
-
- if roundN < 2 then
-
- if menuIndex == 1 then
-
- if #wTouched > 0 then
- for i = 1, #wTouched do
- wTouched[i] = nil
- wX[i] = nil
- wY[i] = nil
- wWidth[i] = nil
- wHeight[i] = nil
- end
- else
- LoadConfig(2)
- end
-
- elseif menuIndex == 2 then
- requireSurfer = not(requireSurfer)
- elseif menuIndex == 3 then
- AFR = not(AFR)
- elseif menuIndex == 4 then
- allowCrazyWeps = not(allowCrazyWeps)
+ if roundN == 1 then
+ if menu[menuIndex].activate ~= nil then
+ menu[menuIndex].activate()
+ else
+ menu[menuIndex].doNext()
end
UpdateMenu()
configureWeapons()
HandleStartingStage()
- elseif (AFR == true) then
+ PlaySound(sndSwitchHog)
- if (GetCurAmmoType() ~= amRope) and
- (GetCurAmmoType() ~= amSkip) and
- (GetCurAmmoType() ~= amNothing)
- then
- AddCaption(loc("You may only attack from a rope!"),0xffba00ff,capgrpMessage2)
+ elseif roundN == 2 then
+ if GetCurAmmoType() ~= amSkip and GetCurAmmoType() ~= amNothing then
+ PlaceWarn()
end
+ elseif roundN == 100 then
+ local weaponSelected = (GetCurAmmoType() ~= amRope) and
+ (GetCurAmmoType() ~= amSkip) and
+ (GetCurAmmoType() ~= amNothing) and
+ (ropeG == nil)
+
+ if weaponSelected then
+ if AFR and CBA and not crateCollected then
+ AddCaption(loc("You must attack from a rope, after you collected a crate!"), msgColorWarn, capgrpMessage2)
+ PlaySound(sndDenied)
+ elseif AFR then
+ AddCaption(loc("You may only attack from a rope!"), msgColorWarn, capgrpMessage2)
+ PlaySound(sndDenied)
+ elseif CBA and not crateCollected then
+ AddCaption(loc("You must first collect a crate before you attack!"), msgColorWarn, capgrpMessage2)
+ PlaySound(sndDenied)
+ end
+ end
end
+end
+function onSwitch()
+ -- Must be in-game, hog must be controlled by player and hog must be on rope or have rope selected
+ if roundN == 100 and CurrentHedgehog ~= nil and band(GetState(CurrentHedgehog), gstHHDriven) ~= 0 and (ropeG ~= nil or GetCurAmmoType() == amRope) then
+ -- Toggle radar mode
+ radarMode = radarMode + 1
+ if radarMode > 2 then
+ radarMode = 0
+ end
+ local message
+ if radarMode == 0 then
+ message = loc("Radar: On")
+ elseif radarMode == 1 then
+ message = loc("Radar: Show after crate drop")
+ elseif radarMode == 2 then
+ message = loc("Radar: Off")
+ end
+ AddCaption(message, GetClanColor(GetHogClan(CurrentHedgehog)), capgrpAmmostate)
+ -- Remember the radar mode for this team to restore it on the team's next turn
+ setTeamValue(GetHogTeamName(CurrentHedgehog), "radarMode", radarMode)
+ end
+end
+
+function onLeft()
+ if roundN == 1 then
+ if menu[menuIndex].doPrev ~= nil then
+ menu[menuIndex].doPrev()
+ else
+ menu[menuIndex].activate()
+ end
+
+ UpdateMenu()
+ configureWeapons()
+ HandleStartingStage()
+
+ PlaySound(sndSwitchHog)
+ end
+end
+
+function onRight()
+ if roundN == 1 then
+ if menu[menuIndex].doNext ~= nil then
+ menu[menuIndex].doNext()
+ else
+ menu[menuIndex].activate()
+ end
+
+ UpdateMenu()
+ configureWeapons()
+ HandleStartingStage()
+
+ PlaySound(sndSwitchHog)
+ end
end
function onDown()
- if roundN < 2 then
+ if roundN == 1 then
+ PlaySound(sndSteps)
menuIndex = menuIndex +1
if menuIndex > #menu then
menuIndex = 1
@@ -497,7 +1090,8 @@
end
function onUp()
- if roundN < 2 then
+ if roundN == 1 then
+ PlaySound(sndSteps)
menuIndex = menuIndex -1
if menuIndex == 0 then
menuIndex = #menu
@@ -506,10 +1100,88 @@
end
end
+function parseBool(key, default)
+ if params[key]=="true" then
+ return true
+ elseif params[key]=="false" then
+ return false
+ else
+ return default
+ end
+end
+
+function parseInt(key, default, min, max)
+ local num = tonumber(params[key])
+ if type(num) ~= "number" then
+ return default
+ end
+ if min ~= nil then
+ num = math.max(min, num)
+ end
+ if max ~= nil then
+ num = math.min(max, num)
+ end
+ return num
+end
+
+function onParameters()
+ parseParams()
+ local tmpParam
+ useMenu = parseBool("menu", useMenu)
+ requireSurfer = parseBool("SBC", requireSurfer)
+ AFR = parseBool("AFR", AFR)
+ CBA = parseBool("CBA", CBA)
+ if params["attackrule"] == "ABL" then
+ attackRule = "ABL"
+ elseif params["attackrule"] == "KTL" then
+ attackRule = "KTL"
+ end
+ allowCrazyWeps = parseBool("SW", allowCrazyWeps)
+ maxCrates = parseInt("maxcrates", maxCrates, 1, maxCratesHard)
+ cratesPerTurn = parseInt("cratesperturn", cratesPerTurn, 1, maxCrates)
+ local wallsParam = params["walls"]
+ local wallsParamSelection = false
+ if wallsParam ~= nil then
+ if wallsParam == "all" then
+ wallsParamSelection = true
+ allowedWallSetTypes = {}
+ for i=1,#allWallSetTypes do
+ allowedWallSetTypes[allWallSetTypes[i]] = true
+ end
+ elseif wallsParam == "none" then
+ WBC = false
+ allowedWallSetTypes = {}
+ else
+ wallsParamSelection = true
+ allowedWallSetTypes = {}
+ local parsedWords = {}
+ for k,v in string.gmatch(wallsParam, "(%w+)") do
+ table.insert(parsedWords, k)
+ end
+ for i=1,#allWallSetTypes do
+ for j=1,#parsedWords do
+ if allWallSetTypes[i] == parsedWords[j] then
+ allowedWallSetTypes[allWallSetTypes[i]] = true
+ end
+ end
+ end
+ end
+ end
+
+ -- Upper and lower bounds
+ local wallsNum = parseInt("wallsnum", nil, 0)
+ if wallsNum == 0 then
+ WBC = false
+ end
+ minWalls = wallsNum
+ maxWalls = wallsNum
+ -- minwalls and maxwalls take precedence over wallsnum
+ minWalls = parseInt("minwalls", minWalls, 1)
+ maxWalls = parseInt("maxwalls", maxWalls, 1)
+end
+
function onGameInit()
- ClearGameFlags()
- EnableGameFlags(gfRandomOrder, gfBorder, gfSolidLand) --, gfInfAttack
HealthCaseProb = 0
CaseFreq = 0
@@ -558,14 +1230,52 @@
function onGameStart()
- LoadConfig(1)
+ trackTeams()
+
+ MapsInit()
+ LoadConfig(wallSetID)
configureWeapons()
- UpdateMenu()
- HandleStartingStage()
+
+ -- ABL or KTL only make sense with at least 3 teams, otherwise we disable it
+ if TeamsCount < 3 or ClansCount < 3 then
+ attackRule = nil
+ end
+ if useMenu then
+ ShowMission(loc("Wall to wall"), loc("Please wait …"), "", 2, 300000)
+ UpdateMenu()
+ else
+ if GetGameFlag(gfPlaceHog) then
+ roundN = 2
+ FinalizeMenu()
+ else
+ allowCrate = false
+ roundN = 100
+ FinalizeMenu()
+ end
+ end
end
function onNewTurn()
+ turnsCount = turnsCount + 1
+
+ if roundN == 0 then
+ roundN = 1
+ end
+
+ if GetGameFlag(gfPlaceHog) then
+ if roundN < 2 then
+ SetWeapon(amSkip)
+ AddAmmo(CurrentHedgehog, amTeleport, 0)
+ TurnTimeLeft = -1
+ SetInputMask(0)
+ end
+ if roundN == 2 then
+ if turnsCount > hogCount then
+ roundN = 100
+ end
+ end
+ end
wallsLeft = #wTouched
@@ -573,88 +1283,317 @@
wTouched[i] = false
end
- allowCrate = true
-
hasSurfed = false
allWallsHit = false
+ crateCollected = false
- crateG = nil
+ crateSpawned = false
+
+ if roundN == 100 then
+ allowCrate = crateGearsInGame < maxCrates
+
+ local teamName = GetHogTeamName(CurrentHedgehog)
+
+ -- Restore team's radar mode
+ radarMode = getTeamValue(teamName, "radarMode")
+
+ if not AreCratesUnlocked() then
+ FreezeCrates()
+ end
+
+ -- Check the attack rule violation of the *previous* team and apply penalties
+ -- This function will do nothiong in the first turn since previousTeam is still nil
+ CheckAttackRuleViolation(previousTeam)
- -- new config stuff
- roundN = roundN + 1
- if roundN < 2 then
+ previousTeam = teamName
+
+ -- Update attack rule information for this turn
+ UpdateLastAndLeaderTeams()
+ teamsAttacked = {}
+
+ -- Was the team violating the attackRule the last time?
+ if getTeamValue(teamName, "skipPenalty") then
+ -- Then take away this turn
+ AddCaption(string.format(loc("%s must skip this turn for rule violation ."), teamName), msgColorWarn, capgrpMessage)
+ TurnTimeLeft = 0
+ setTeamValue(teamName, "skipPenalty", false)
+ end
+
+ else
+ allowCrate = false
+ end
+
+ if roundN == 1 then
TurnTimeLeft = -1
SetInputMask(0)
allowCrate = false
- HandleStartingStage() -- new
+ UpdateMenu()
+ AddCaption(string.format(loc("%s may choose the rules."), GetHogTeamName(CurrentHedgehog)), msgColorTech, capgrpGameState)
+ HandleStartingStage()
end
end
+function CanSurf()
+ if mapID ~= nil then
+ if GetGameFlag(gfBottomBorder) and WaterRise == 0 then
+ return false
+ else
+ return MapList[mapID][2]
+ end
+ else
+ return nil
+ end
+end
+
function UpdateMenu()
+ local teamInfo
+ if roundN == 1 and CurrentHedgehog ~= nil then
+ teamInfo = string.format(loc("%s, you may choose the rules."), GetHogTeamName(CurrentHedgehog))
+ else
+ teamInfo = ""
+ end
+ preMenuCfg = teamInfo .. "|" ..
+ loc("Press [Up] and [Down] to move between menu items.|Press [Attack], [Left], or [Right] to toggle.") .. "|"
+ if GetGameFlag(gfPlaceHog) then
+ postMenuCfg = loc("Press [Long jump] to accept this configuration and begin placing hedgehogs.")
+ else
+ postMenuCfg = loc("Press [Long jump] to accept this configuration and start the game.")
+ end
+
+ -- This table contains the menu strings and functions to be called when the entry is activated.
+ menu = {}
- preMenuCfg = loc("Spawn the crate, and attack!") .. "|"
- postMenuCfg = loc("Press [Enter] to accept this configuration.")
+ -- Walls required (hidden if the current settings don't allow for any walls)
+ if #wallSets > 0 then
+ local line
+ if #wTouched > 0 then
+ if wallSets[wallSetID].custom then
+ line = string.format(loc("Wall set: %s (%d walls)"), wallSets[wallSetID].desc, #wTouched) .. "|"
+ else
+ line = string.format(loc("Wall set: %s"), wallSets[wallSetID].desc) .. "|"
+ end
+ else
+ line = loc("Wall set: No walls") .. "|"
+ end
+ table.insert(menu, {
+ line = line,
+ doNext = function()
+ wallSetID = wallSetID + 1
+ if wallSetID > #wallSets then
+ wallSetID = 0
+ end
+ LoadConfig(wallSetID)
+ end,
+ doPrev = function()
+ wallSetID = wallSetID - 1
+ if wallSetID < 0 then
+ wallSetID = #wallSets
+ end
+ LoadConfig(wallSetID)
+ end,
+ })
+ end
+
+ -- Surf Before Crate (hidden if map disabled it)
+ if CanSurf() == true or CanSurf() == nil then
+ local toggleSurf = function() requireSurfer = not(requireSurfer) end
+ table.insert(menu, {
+ line = string.format(loc("Surf Before Crate: %s"), BoolToCfgTxt(requireSurfer)) .. "|",
+ activate = function() requireSurfer = not requireSurfer end,
+ })
+ end
+
+ -- Attack From Rope
+ table.insert(menu, {
+ line = string.format(loc("Attack From Rope: %s"), BoolToCfgTxt(AFR)) .. "|",
+ activate = function() AFR = not AFR end,
+ })
+
+ -- Crate Before Attack
+ table.insert(menu, {
+ line = string.format(loc("Crate Before Attack: %s"), BoolToCfgTxt(CBA)) .. "|",
+ activate = function() CBA = not CBA end,
+ })
- menu = {
- loc("Walls Required") .. ": " .. #wTouched .. "|",
- loc("Surf Before Crate") .. ": " .. BoolToCfgTxt(requireSurfer) .. "|",
- loc("Attack From Rope") .. ": " .. BoolToCfgTxt(AFR) .. "|",
- loc("Super Weapons") .. ": " .. BoolToCfgTxt(allowCrazyWeps) .. "|"
- }
+ if TeamsCount >= 3 then
+ -- Attack rule (Disabled / All But Last / Kill The Leader)
+ table.insert(menu, {
+ line = string.format(loc("Attack rule: %s"), AttackRuleToCfgTxt(attackRule)) .. "|",
+ doNext = function()
+ if attackRule == nil then
+ attackRule = "ABL"
+ elseif attackRule == "ABL" then
+ attackRule = "KTL"
+ elseif attackRule == "KTL" then
+ attackRule = nil
+ end
+ end,
+ doPrev = function()
+ if attackRule == nil then
+ attackRule = "KTL"
+ elseif attackRule == "ABL" then
+ attackRule = nil
+ elseif attackRule == "KTL" then
+ attackRule = "ABL"
+ end
+ end,
+ })
+ end
+
+ -- Super weapons
+ table.insert(menu, {
+ line = string.format(loc("Super weapons: %s"), BoolToCfgTxt(allowCrazyWeps)) .. "|",
+ activate = function() allowCrazyWeps = not allowCrazyWeps end,
+ })
+
+ -- Number of crates which appear per turn
+ if maxCrates > 1 then
+ table.insert(menu, {
+ line = string.format(loc("Crates per turn: %d"), cratesPerTurn) .. "|",
+ doNext = function()
+ cratesPerTurn = cratesPerTurn + 1
+ if cratesPerTurn > maxCrates then
+ cratesPerTurn = 1
+ end
+ end,
+ doPrev = function()
+ cratesPerTurn = cratesPerTurn - 1
+ if cratesPerTurn < 1 then
+ cratesPerTurn = maxCrates
+ end
+ end,
+ })
+ end
+end
+
+function FinalizeMenu()
+ local text = ""
+ local showTime = 3000
+ if #wTouched == 0 and not requireSurfer then
+ text = text .. loc("Collect the crate and attack!") .. "|"
+ else
+ text = text .. loc("Spawn the crate and attack!") .. "|"
+ end
+
+ -- Expose a few selected game flags
+ if GetGameFlag(gfPlaceHog) then
+ text = text .. loc("Place hedgehogs: Place your hedgehogs at the start of the game.") .. "|"
+ showTime = 6000
+ end
+ if GetGameFlag(gfResetWeps) then
+ text = text .. loc("Weapons reset: The weapons are reset after each turn.") .. "|"
+ end
+
+ -- Show the WxW rules
+ if #wTouched == 1 then
+ text = text .. loc("Wall Before Crate: You must touch the marked wall before you can get crates.") .. "|"
+ elseif #wTouched > 0 then
+ text = text .. string.format(loc("Walls Before Crate: You must touch the %d marked walls before you can get crates."), #wTouched) .. "|"
+ end
+
+ if requireSurfer then
+ text = text .. loc("Surf Before Crate: You must bounce off the water once before you can get crates.") .. "|"
+ end
+
+ if AFR then
+ text = text .. loc("Attack From Rope: You may only attack from a rope.") .. "|"
+ end
+
+ if CBA then
+ text = text .. loc("Crate Before Attack: You must collect a crate before you can attack.") .. "|"
+ end
+
+ if attackRule == "ABL" then
+ text = text .. loc("All But Last: You must not solely attack the team with the least health") .. "|"
+ elseif attackRule == "KTL" then
+ text = text .. loc("Kill The Leader: You must also hit the team with the most health.") .. "|"
+ end
+ if attackRule ~= nil then
+ text = text .. loc("Penalty: If you violate above rule, you have to skip in the next turn.") .. "|"
+ end
+
+ if allowCrazyWeps then
+ text = text .. loc("Super weapons: A few crates contain very powerful weapons.") .. "|"
+ end
+
+ ShowMission(loc("Wall to wall"), loc("A Shoppa minigame"), text, 1, showTime)
end
function HandleStartingStage()
- temp = menu[menuIndex]
- menu[menuIndex] = "--> " .. menu[menuIndex]
+ temp = menu[menuIndex].line
+ menu[menuIndex].line = "--> " .. menu[menuIndex].line
missionComment = ""
for i = 1, #menu do
- missionComment = missionComment .. menu[i]
+ missionComment = missionComment .. menu[i].line
end
ShowMission (
- loc("WALL TO WALL") .. " 0.4",
- loc("a shoppa minigame"),
+ loc("Wall to wall"),
+ loc("Configuration phase"),
preMenuCfg..
missionComment ..
postMenuCfg ..
- --" " .. "|" ..
- "", 4, 300000
+ "", 2, 300000
)
- menu[menuIndex] = temp
+ menu[menuIndex].line = temp
end
function onGameTick()
- if CurrentHedgehog ~= nil then
-
- --AddCaption(Map)
- --AddCaption(RightX ..";" .. GetX(CurrentHedgehog))
+ if CurrentHedgehog ~= nil and roundN >= 0 then
gTimer = gTimer + 1
if gTimer == 25 then
gTimer = 1
- CheckForWallCollision()
- CheckCrateConditions()
+ if roundN == 100 then
+ CheckForWallCollision()
+ CheckCrateConditions()
- if (crateG == GetFollowGear()) and (crateG ~= nil) then
- FollowGear(CurrentHedgehog)
- end
-
- -- if attackfromrope is set, forbid firing unless using rope
- if (AFR == true) and (roundN >= 2) then
- if (GetCurAmmoType() == amRope) or
+ if (GetGearType(GetFollowGear()) == gtCase) then
+ FollowGear(CurrentHedgehog)
+ end
+
+ -- AFR and CBA handling
+ local allowAttack = true
+ local shootException
+ shootException = (GetCurAmmoType() == amRope) or
(GetCurAmmoType() == amSkip) or
(GetCurAmmoType() == amNothing)
- then
- SetInputMask(0xFFFFFFFF)
- elseif ropeG == nil then
- SetInputMask(bnot(gmAttack))
+ -- If Attack From Rope is set, forbid firing unless using rope
+ if AFR then
+ if ropeG == nil then
+ allowAttack = false
+ end
+ end
+ -- If Crate Before Attack is set, forbid firing if crate is not collected
+ if CBA then
+ if not crateCollected then
+ allowAttack = false
+ end
+ end
+ if allowAttack or shootException then
+ SetInputMask(bor(GetInputMask(), gmAttack))
+ if CBA then
+ SetInputMask(bor(GetInputMask(), gmLJump))
+ end
+ else
+ if CBA then
+ if ropeG == nil then
+ SetInputMask(band(GetInputMask(), bnot(gmAttack)))
+ SetInputMask(bor(GetInputMask(), gmLJump))
+ else
+ SetInputMask(bor(GetInputMask(), gmAttack))
+ SetInputMask(band(GetInputMask(), bnot(gmLJump)))
+ end
+ else
+ SetInputMask(band(GetInputMask(), bnot(gmAttack)))
+ end
end
end
@@ -673,7 +1612,9 @@
ropeG = gear
elseif GetGearType(gear) == gtCase then
- crateG = gear
+ crates[gear] = true
+ crateGearsInGame = crateGearsInGame + 1
+
trackGear(gear)
table.insert(rCirc, AddVisualGear(0,0,vgtCircle,0,true) )
@@ -683,24 +1624,38 @@
SetVisualGearValues(rCirc[#rCirc], 0, 0, 100, 255, 1, 10, 0, 40, 3, 0xff00ffff)
allowCrate = false
+ crateSpawned = true
rPingTimer = 0
rAlpha = 0
+ elseif GetGearType(gear) == gtHedgehog then
+ trackGear(gear)
+ local teamName = GetHogTeamName(gear)
+ -- Initialize radar mode to “on” and set other team values
+ setTeamValue(teamName, "radarMode", 0)
+ setTeamValue(teamName, "skipPenalty", false)
+
+ if getTeamValue(teamName, "hogs") == nil then
+ setTeamValue(teamName, "hogs", 1)
+ else
+ increaseTeamValue(teamName, "hogs")
+ end
+ hogCount = hogCount + 1
+ teamNames[GetHogTeamName(gear)] = true
end
end
function onGearDelete(gear)
- if gear == ropeG then
+ local gt = GetGearType(gear)
+ if gt == gtRope then
ropeG = nil
- elseif GetGearType(gear) == gtCase then
+ elseif gt == gtCase then
- if gear == crateG then
- crateG = nil
- -- rAlpha = 255
- end
+ crates[gear] = nil
+ crateGearsInGame = crateGearsInGame - 1
for i = 1, #rCirc do
if rCirc[i] == getGearValue(gear,"CIRC") then
@@ -711,6 +1666,133 @@
trackDeletion(gear)
+ -- Was crate collected?
+ if band(GetGearMessage(gear), gmDestroy) ~= 0 then
+ crateCollected = true
+ end
+
+ elseif gt == gtHedgehog then
+ teamsAttacked[GetHogTeamName(gear)] = true
+ decreaseTeamValue(GetHogTeamName(gear), "hogs")
+ trackDeletion(gear)
+ end
+
+end
+
+function onGearDamage(gear)
+
+ if GetGearType(gear) == gtHedgehog then
+ teamsAttacked[GetHogTeamName(gear)] = true
+ end
+
+end
+
+-- Check which team is the last and which is the leader (used for ABL and KTL)
+function UpdateLastAndLeaderTeams()
+ local teamHealths = {}
+
+ for team, x in pairs(teamNames) do
+ UpdateTeamHealth(team)
+ local totalHealth = getTeamValue(team, "totalHealth")
+ if totalHealth > 0 then
+ table.insert(teamHealths, {name = team, health = totalHealth } )
+ end
+ end
+
+ -- Sort the table by health, lowest health comes first
+ table.sort(teamHealths, function(team1, team2) return team1.health < team2.health end)
+
+ -- ABL and KTL rules are only active at 3 teams; when there are only 2 teams left, it's “everything goes”.
+ if #teamHealths >= 3 then
+ if teamHealths[1].health == teamHealths[2].health then
+ -- ABL rule is disabled if it's a tie for “least health”
+ lastTeam = nil
+ else
+ -- Normal assignment of ABL variable
+ lastTeam = teamHealths[1].name
+ end
+ if teamHealths[#teamHealths].health == teamHealths[#teamHealths-1].health then
+ -- KTL rule is disabled if it's a tie for “most health”
+ leaderTeam = nil
+ runnerUpTeam = nil
+ else
+ -- Normal assignment of KTL variables
+ leaderTeam = teamHealths[#teamHealths].name
+ runnerUpTeam = teamHealths[#teamHealths-1].name
+ end
+ else
+ -- The KTL and ABL rules are disabled with only 2 teams left
+ lastTeam = nil
+ runnerUpTeam = nil
+ leaderTeam = nil
+ end
+end
+
+function UpdateTeamHealth(team)
+ setTeamValue(team, "totalHealth", 0)
+ runOnHogsInTeam(function(hog)
+ if(GetGearType(hog) ~= gtHedgehog) then return end
+ local h = getTeamValue(GetHogTeamName(hog), "totalHealth")
+ setTeamValue(GetHogTeamName(hog), "totalHealth", h + GetHealth(hog))
+ end, team)
+end
+
+-- Check if the ABL or KTL rule (if active) has been violated by teamToCheck
+function CheckAttackRuleViolation(teamToCheck)
+
+ if teamToCheck == nil then return end
+
+ local violated = false
+ if attackRule == "ABL" then
+ -- We don't care if the last team hurts itself
+ if lastTeam ~= nil and lastTeam ~= teamToCheck then
+ local lastAttacked = false
+ local attackNum = 0 -- count the attacked teams but we'll ignore the attacking team
+ for team, wasAttacked in pairs(teamsAttacked) do
+ -- Ignore the attacking team
+ if team ~= teamToCheck then
+ attackNum = attackNum + 1
+ if team == lastTeam then
+ lastAttacked = true
+ end
+ end
+ end
+ -- Rule is violated iff only the last team is attacked (damage to attacking team is ignored)
+ if attackNum == 1 and lastAttacked then
+ violated = true
+ end
+ end
+ if violated then
+ AddCaption(string.format(loc("%s violated the “All But Last” rule and will be penalized."), teamToCheck), msgColorWarn, capgrpGameState)
+ end
+ elseif attackRule == "KTL" then
+ local leaderAttacked = false
+ if leaderTeam ~= nil then
+ local attackNum = 0
+ local selfHarm = false
+ for team, wasAttacked in pairs(teamsAttacked) do
+ attackNum = attackNum + 1
+ if team == teamToCheck then
+ selfHarm = true
+ end
+ -- The leader must attack the runner-up, everyone else must attack the leader
+ if (teamToCheck ~= leaderTeam and team == leaderTeam) or (teamToCheck == leaderTeam and team == runnerUpTeam) then
+ leaderAttacked = true
+ break
+ end
+ end
+ -- If teams were attacked but not the leader, it is a violation,
+ -- but we don't care if the team *only* harmed itself.
+ if (attackNum >= 2 and not leaderAttacked) or (attackNum == 1 and not selfHarm and not leaderAttacked) then
+ violated = true
+ end
+ end
+ if violated then
+ AddCaption(string.format(loc("%s violated the “Kill The Leader” rule and will be penalized."), teamToCheck), msgColorWarn, capgrpGameState)
+ end
+ end
+ if violated then
+ setTeamValue(teamToCheck, "skipPenalty", true)
end
end