---------------------- -- WALL TO WALL 0.7 ---------------------- -- a shoppa minigame -- by mikade -- feel free to add map specific walls to LoadConfig, or post additional -- wall suggestions on our forum at: http://www.hedgewars.org/forum ---------------- --0.1 ---------------- -- concept test ---------------- --0.2 ---------------- -- unhardcoded turntimeleft, now uses shoppa default of 45s -- changed some things behind the scenes -- fixed oooooold radar bug -- added radar / script support for multiple crates -- tweaked weapons tables -- added surfing and changed crate spawn requirements a bit ---------------- --0.3 ---------------- -- stuffed dirty clothes into cupboard -- improved user feedback -- added/improved experimental config system, input masks included :D ---------------- --0.4 ---------------- -- for version 0.9.18, now detects border in correct location -- fix 0.3 config constraint -- remove unnecessary vars -- oops, remove hardcoding of minesnum,explosives -- ... and unhardcode turntime (again)... man, 30s is hard :( -- move some initialisations around -- numerous improvements to user feedback -- walls disappear after being touched -- added backwards compatibility with 0.9.17 ---------------- --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! ----------------------------- HedgewarsScriptLoad("/Scripts/Locale.lua") HedgewarsScriptLoad("/Scripts/Tracker.lua") HedgewarsScriptLoad("/Scripts/Utils.lua") HedgewarsScriptLoad("/Scripts/Params.lua") -- 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 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 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 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 local m2Count = 0 local weapons = {} local crazyWeps = {amWatermelon, amHellishBomb, amBallgun, amRCPlane} local groundWeps = {amBee, amShotgun,amDEagle,amFirePunch, amWhip, amPickHammer, amBaseballBat, amCake,amBallgun, amRCPlane, amSniperRifle, amBirdy, amBlowTorch, amFlamethrower, amMortar, amHammer} local ropeWeps = {amGrenade, amClusterBomb, amBazooka, amMine, amDynamite, 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 {"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, true, true}, {"RopeLikeAKingInHellWithNeon", false, true, true}, {"Ropes Flipped", false, false, true}, {"Ropes Rearranged", false, false, true}, {"RopesRevenge Flipped", 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") else return loc("Enabled") end end 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 -- 0.9.17 if Map == "CHANGE_ME" then AddCaption(loc("For improved features/stability, play 0.9.18+")) AddWall(10,10,margin,2025) AddWall(4085-margin,10,margin,2025) end --0.9.18+ for i = 1, #MapList do if Map == MapList[i][1] then mapID = i end end -- Border conditions -- Just a wrapper for MapHasBorder() local border = MapHasBorder() == true -- Left and right walls are available local leftRight = (WorldEdge == weBounce) or (WorldEdge == weNone and border) local left, right, roof local startY, height if (not border) and (WorldEdge == weBounce) then -- Higher left/right walls for bouncy world edge without roof local h = math.max(1024, LAND_HEIGHT) height = h * 2 startY = TopY - h else -- Standard left/right wall height height = WaterLine startY = TopY + 10 end left = {LeftX+10, startY, margin, height} right = {RightX-10-margin, startY, margin, height} roof = {LeftX+10, TopY+10, RightX-LeftX-20, margin} if mapID ~= nil then if border and MapList[mapID][3] == true then NewWallSet({roof, desc=loc("Roof")}, "roof") wallSetID = #wallSets end if leftRight and MapList[mapID][4] == true then NewWallSet({left, right, desc=loc("Left and right")}, "leftright") wallSetID = #wallSets end if leftRight and border and MapList[mapID][3] == true and MapList[mapID][4] == true then NewWallSet({left, right, roof, desc=loc("Left, right and roof")}, "leftrightroof") end -- add map specific walls 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 and leftRight and walls.add == "all" then table.insert(newwallset, roof) table.insert(newwallset, left) table.insert(newwallset, right) elseif walls.add == "default" then if border and MapList[mapID][3] == true then table.insert(newwallset, roof) end if leftRight and MapList[mapID][4] == true then table.insert(newwallset, left) table.insert(newwallset, right) end elseif border and walls.add == "roof" then table.insert(newwallset, roof) elseif leftRight and walls.add == "leftright" then table.insert(newwallset, left) table.insert(newwallset, right) end for w=1,#walls do table.insert(newwallset, walls[w]) end if border and leftRight 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") wallSetID = #wallSets end if leftRight then NewWallSet({left, right, desc=loc("Left and right")}, "leftright") wallSetID = #wallSets end if leftRight and border then NewWallSet({left, right, roof, desc=loc("Left, right and roof")}, "leftrightroof") 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 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) table.insert(wX, zXMin) table.insert(wY, zYMin) table.insert(wWidth, zWidth) table.insert(wHeight, zHeight) table.insert(wTouched, false) end function ClearWalls() wX = {} wY = {} wWidth = {} wHeight = {} wTouched = {} end -- Draw a single point for the crate radar function DrawBlip(gear) 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) g2X, g2Y = GetX(gear), GetY(gear) q = g1X - g2X w = g1Y - g2Y r = math.sqrt( (q*q) + (w*w) ) --alternate RCX = getGearValue(gear,"RX") RCY = getGearValue(gear,"RY") rCircDistance = r -- distance to circle opp = w if opp < 0 then opp = opp*-1 end -- work out the angle (theta) to the target t = math.deg ( math.asin(opp / r) ) -- based on the radius of the radar, calculate what x/y displacement should be NR = 150 -- radius at which to draw circs NX = math.cos( math.rad(t) ) * NR NY = math.sin( math.rad(t) ) * NR if rCircDistance < NR then RCX = g2X elseif q > 0 then RCX = g1X - NX else RCX = g1X + NX end if rCircDistance < NR then RCY = g2Y elseif w > 0 then RCY = g1Y - NY else RCY = g1Y + NY end setGearValue(gear, "RX", RCX) setGearValue(gear, "RY", RCY) end function HandleCircles() 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 elseif radarMode == 2 then rAlpha = 255 end runOnGears(DrawBlip) m2Count = m2Count + 1 if m2Count == 25 then m2Count = 0 if (CurrentHedgehog ~= nil) and (rAlpha ~= 255) then runOnGears(TrackRadarBlip) end end end -- Returns true if crates are allowed to be accessed right now (used for unfreezing and spawning) function AreCratesUnlocked() local crateSpawn = true if requireSurfer == true then if hasSurfed == false then crateSpawn = false end end if #wTouched > 0 then if allWallsHit == false then crateSpawn = false end end 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 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 if toSpawn > 0 then PlaySound(sndWarp) end end end end function onGearWaterSkip(gear) if gear == CurrentHedgehog then hasSurfed = true AddCaption(loc("Surfer!"), 0xFFFFFFFF, capgrpMessage2) end end function WallHit(id, zXMin,zYMin, zWidth, zHeight) if wTouched[id] == false then tempE = AddVisualGear(GetX(CurrentHedgehog), GetY(CurrentHedgehog), vgtBigExplosion, 0, false) PlaySound(sndExplosion) wallsLeft = wallsLeft - 1 if wallsLeft == 0 then AddCaption(loc("All walls touched!")) allWallsHit = true if (requireSurfer == true) and (hasSurfed == false) then AddCaption(loc("Go surf!"), 0xFFFFFFFF, capgrpMessage2) end else AddCaption(string.format(loc("Walls left: %d"), wallsLeft)) end end wTouched[id] = true if #wTouched > 0 then tempE = AddVisualGear(GetX(CurrentHedgehog), GetY(CurrentHedgehog), vgtSmoke, 0, false) end end function CheckForWallCollision() for i = 1, #wTouched do if gearIsInBox(CurrentHedgehog, wX[i],wY[i],wWidth[i],wHeight[i]) then WallHit(i, wX[i],wY[i],wWidth[i],wHeight[i]) end end end function BorderSpark(zXMin,zYMin, zWidth, zHeight, bCol) local size = zWidth * zHeight -- Add multiple sparks for very large walls sparkRuns = math.min(10, math.max(1, div(size, 10240))) for i=1, sparkRuns do local eX = zXMin + GetRandom(zWidth+10) local eY = zYMin + GetRandom(zHeight+10) local tempE = AddVisualGear(eX, eY, vgtDust, 0, false) if tempE ~= 0 then local g1, g2, g3, g4, g5, g6, g7, g8, g9, g10 = GetVisualGearValues(tempE) SetVisualGearValues(tempE, eX, eY, g3, g4, g5, g6, g7, 1, g9, bCol ) end end end function HandleBorderEffects() effectTimer = effectTimer + 1 if effectTimer > 15 then --25 effectTimer = 1 for i = 1, #wTouched do if wTouched[i] == false then bCol = GetClanColor(GetHogClan(CurrentHedgehog)) BorderSpark(wX[i],wY[i],wWidth[i],wHeight[i], bCol) end end end end function PlaceWarn() PlaySound(sndDenied) AddCaption(loc("Please place your hedgehog first!"), msgColorWarn, capgrpMessage2) end function onLJump() if roundN == 1 then PlaySound(sndPlaced) SetInputMask(0xFFFFFFFF) 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 == 1 then if menu[menuIndex].activate ~= nil then menu[menuIndex].activate() else menu[menuIndex].doNext() end UpdateMenu() configureWeapons() HandleStartingStage() PlaySound(sndSwitchHog) 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 == 1 then PlaySound(sndSteps) menuIndex = menuIndex +1 if menuIndex > #menu then menuIndex = 1 end HandleStartingStage() end end function onUp() if roundN == 1 then PlaySound(sndSteps) menuIndex = menuIndex -1 if menuIndex == 0 then menuIndex = #menu end HandleStartingStage() 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() HealthCaseProb = 0 CaseFreq = 0 SetAmmoDescriptionAppendix(amRope, loc("Switch: Toggle crate radar")) end function configureWeapons() -- reset wep array for i = 1, #weapons do weapons[i] = nil end -- add rope weps for i, w in pairs(ropeWeps) do table.insert(weapons, w) end -- add ground weps for i, w in pairs(groundWeps) do table.insert(weapons, w) end -- remove ground weps if attacking from rope is mandatory if AFR == true then for i = 1, #weapons do for w = 1, #groundWeps do if groundWeps[w] == weapons[i] then table.remove(weapons, i) end end end end -- remove crazy weps is crazy weps aren't allowed if allowCrazyWeps == false then for i = 1, #weapons do for w = 1, #crazyWeps do if crazyWeps[w] == weapons[i] then table.remove(weapons, i) end end end end end function onGameStart() trackTeams() MapsInit() LoadConfig(wallSetID) configureWeapons() -- 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 for i = 1, #wTouched do wTouched[i] = false end hasSurfed = false allWallsHit = false crateCollected = false 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) 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 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 = {} -- 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, }) 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].line menu[menuIndex].line = "--> " .. menu[menuIndex].line missionComment = "" for i = 1, #menu do missionComment = missionComment .. menu[i].line end ShowMission ( loc("Wall to wall"), loc("Configuration phase"), preMenuCfg.. missionComment .. postMenuCfg .. "", 2, 300000 ) menu[menuIndex].line = temp end function onGameTick() if CurrentHedgehog ~= nil and roundN >= 0 then gTimer = gTimer + 1 if gTimer == 25 then gTimer = 1 if roundN == 100 then CheckForWallCollision() CheckCrateConditions() 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) -- 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 end HandleBorderEffects() HandleCircles() end end function onGearAdd(gear) if GetGearType(gear) == gtRope then ropeG = gear elseif GetGearType(gear) == gtCase then crates[gear] = true crateGearsInGame = crateGearsInGame + 1 trackGear(gear) table.insert(rCirc, AddVisualGear(0,0,vgtCircle,0,true) ) setGearValue(gear,"CIRC",rCirc[#rCirc]) setGearValue(gear,"RX",0) setGearValue(gear,"RY",0) 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) local gt = GetGearType(gear) if gt == gtRope then ropeG = nil elseif gt == gtCase then crates[gear] = nil crateGearsInGame = crateGearsInGame - 1 for i = 1, #rCirc do if rCirc[i] == getGearValue(gear,"CIRC") then DeleteVisualGear(rCirc[i]) table.remove(rCirc, i) end end 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 function onAmmoStoreInit() for i, w in pairs(ropeWeps) do SetAmmo(w, 0, 0, 0, 1) end for i, w in pairs(groundWeps) do SetAmmo(w, 0, 0, 0, 1) end for i, w in pairs(crazyWeps) do SetAmmo(w, 0, 0, 0, 1) end SetAmmo(amRope, 9, 0, 0, 0) SetAmmo(amSkip, 9, 0, 0, 0) end