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