antonsavov

WIP play

Dec 20th, 2017
400
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 84.74 KB | None | 0 0
  1. VERSION_NUMBER = '2.8.0'
  2. VERSION_NAME = 'The Refactoring Conundrum'
  3.  
  4. os.loadAPI('loader')
  5. loader.loadPackages()
  6.  
  7.  
  8. --restore creative to those who had it
  9. --clear experience points upon using the homecomer or reseting
  10. --make game to autostart
  11.  
  12. --TODO move these to the Game object
  13. --contains a list of all posible Buildzones (built a bit later)
  14. local LOCS = {}
  15. local cleanbuildzone = false
  16. local catalogue_elements = {} --keep the initialized catalogue elements here instead of in the game object to avoid recursive containment in the table "game"
  17. local buildzones = {} --keep the initialized buildzones here instead of in the game object to avoid recursive containment in the table "game"
  18. local number_of_players_adventure --will keep the number of players in adventure mode on the server
  19. local number_of_players_creative --will keep the number of players in creative mode on the server
  20.  
  21.  
  22. --creates a grid of absolute references (0,0) (1,0)
  23. --rename to buildGameField
  24. function buildGrid(w,h)
  25.     debug.log("creating the LOCS table with all buildzones in this game...")
  26.     local grid = readGridFromFile()
  27.     if grid then
  28.         -- grid was read from the file successfully
  29.         debug.log("LOCS table read from the local file")
  30.     else
  31.         --generate file from scratch
  32.        
  33.         grid = {}
  34.         for z=0,h-1 do
  35.             for x=0,w-1 do
  36.                 table.insert(grid,{x=x,z=z,played=false})
  37.             end
  38.         end
  39.         debug.log("LOCS table created from scratch")
  40.     end
  41.    
  42.     return grid
  43. end
  44.  
  45. function readGridFromFile()
  46.     local result = nil
  47.     --fs.makeDir("/records")
  48.     --local filename = "/records/_buildzones-list.json"
  49.     --local filename = "_buildzones-list.json"
  50.     --local filename_with_path = registry.RECORDS_FOLDER.."/"..filename
  51.    
  52.    
  53.     if file_exists(registry.BUILDZONES_LIST_FILE) then
  54.         ---read from file
  55.         result = json.decodeFromFile(registry.BUILDZONES_LIST_FILE)
  56.     end
  57.     return result  
  58. end
  59.  
  60. function writeGridToFile()
  61.     fs.makeDir(registry.RECORDS_FOLDER)
  62.     --local filename = "_buildzones-list.json"
  63.     --local filename_with_path = registry.RECORDS_FOLDER.."/"..filename
  64.  
  65.     local file = fs.open(registry.BUILDZONES_LIST_FILE,"w")
  66.     file.write(json.encodePretty(LOCS))
  67.     file.close()
  68. end
  69.  
  70. --- Constructor for playerdata object
  71. -- @param name the player minecraft name as a string
  72. -- @param x the players current X coord
  73. -- @param y the players current Y coord
  74. -- @param z the players current Z coord
  75. -- @table p The new player object
  76. -- @return p The new player object
  77. function newPlayerData(name,x,y,z)
  78.     local p = {
  79.         name = name, -- the players minecraft name (String)
  80.         x = x, -- the players last X coord
  81.         y = y, -- the players last Y coord
  82.         z = z -- the players last Z coord
  83.     }
  84.     return p
  85. end
  86.  
  87. --return a list of all players in the game world as player objects
  88. local function getAllPos(selector)
  89.     --debug.log("starting getAllPos with selector: "..selector)
  90.     --commands.gamerule("logAdminCommands",true) --it seems we need this to be on for successful tp message back
  91.     --os.sleep(2)
  92.     local result, message = commands.tp("@a["..selector.."]","~ ~ ~")
  93.     --commands.gamerule("logAdminCommands",false) --and then disable back off to prevent the game from flooding the chat log
  94.     --local result, message = commands.exec("tp @a["..selector.."] ~ ~ ~")
  95.     local names = {}
  96.     if result == true then
  97.         for i,result in ipairs(message) do
  98.             --debug.log("starting message decoding: "..result)
  99.             local wordpattern = "[^, ]+"
  100.             local numberpattern = "[%-% ]%d+[%.]%d+"
  101.             local words,numbers = {},{}
  102.             --debug.log("finding words: "..result)
  103.             for word in string.gmatch(result, wordpattern) do
  104.                 table.insert(words,word)
  105.             end
  106.             --debug.log("finding numbers: "..result)
  107.             for number in string.gmatch(result, numberpattern) do
  108.                 table.insert(numbers,number)
  109.             end
  110.  
  111.             local coords = {
  112.                 x = math.floor(numbers[1]),
  113.                 y = math.floor(numbers[2]),
  114.                 z = math.floor(numbers[3])
  115.                 }
  116.             local name = words[2]
  117.             --debug.log("inserting into names list: "..name)
  118.             table.insert(names,newPlayerData(name,coords.x,coords.y,coords.z))
  119.         end
  120.     end
  121.     --debug.log("getAllPos completed")
  122.     return names
  123. end
  124.  
  125.  
  126. --returns a list of player objects containing all players who are standing on the given block, and who also are in the given selection
  127. --TODO this must support blocks with variants too
  128. local function getAllOnBlockType(block,selector)
  129.     local result, message = commands.exec("execute @a["..selector.."] ~ ~ ~ detect ~ ~-1 ~ "..block.." -1 tp @p[r=1] ~ ~ ~")
  130.     local names = {}
  131.     if result == true then
  132.         for i,result in ipairs(message) do
  133.             local wordpattern = "[^, ]+"
  134.             local numberpattern = "[%-% ]%d+[%.]%d+"
  135.             local words,numbers = {},{}
  136.  
  137.             for word in string.gmatch(result, wordpattern) do
  138.                 table.insert(words,word)
  139.             end
  140.             for number in string.gmatch(result, numberpattern) do
  141.                 table.insert(numbers,number)
  142.             end
  143.            
  144.             if numbers[1] and numbers[2] and numbers[3] then
  145.                 local coords = {
  146.                         x = math.floor(numbers[1]),
  147.                         y = math.floor(numbers[2]),
  148.                         z = math.floor(numbers[3])
  149.                     }
  150.                 local name = words[2]
  151.                 table.insert(names,newPlayerData(name,coords.x,coords.y,coords.z))
  152.                 --print("Found a player - getOnBlock")
  153.                 debug.log("player "..name.." is on "..block.." at: "..coords.x..", "..coords.y..", "..coords.z)
  154.             else
  155.                 debug.log("Error in getAllOnBlockType: Coordinate Numbers were missing")
  156.             end
  157.         end
  158.     end
  159.     return names
  160. end
  161.  
  162. ---BEGIN HOMECOMER RELATED FUNCTIONS
  163.  
  164. --gives a player a HOMECOMER egg with their name on it. Removes all spawn eggs first
  165. local function giveHomecomer(name)
  166.     commands.clear(name,'spawn_egg')
  167.    
  168.     commands.give(name,'spawn_egg 1 '..registry.HOMECOMER_VALUE..' {CanPlaceOn:["'..registry.BLOCKS.PHVFLOOR.block..'","'..registry.BLOCKS.CAMP_FLOOR.block..'","'..registry.BLOCKS._matWhiteSmooth.block..'","'..registry.BLOCKS._matWhiteSmooth2.block..'","'..registry.BLOCKS._matYellowSmooth.block..'","'..registry.BLOCKS._matPinkSmooth.block..'","'..registry.BLOCKS._matBlueSmooth.block..'","'..registry.BLOCKS._matPurpleSmooth.block..'","'..registry.BLOCKS._matWhiteTextured.block..'","'..registry.BLOCKS._matYellowTextured.block..'","'..registry.BLOCKS._matPinkTextured.block..'","'..registry.BLOCKS._matBlueTextured.block..'","'..registry.BLOCKS._matPurpleTextured.block..'"],display:{Name:"HOMECOMER - '..name..'",Lore:[Use this on the floor to return to spawn]}}')
  169. end
  170.  
  171. --returns a list of HOMECOMER entities and their names  
  172. local function getHomecomers()
  173.     local result, message = commands.exec("execute @e[type="..registry.HOMECOMER_TYPE.."] ~ ~ ~ tp @e[r=1] ~ ~ ~")
  174.     local names = {}
  175.     if result == true then
  176.         for i,result in ipairs(message) do
  177.             local wordpattern = "[^, ]+"
  178.             local numberpattern = "[%-% ]%d+[%.]%d+"
  179.             local words,numbers = {},{}
  180.  
  181.             for word in string.gmatch(result, wordpattern) do
  182.                 table.insert(words,word)
  183.                 --print(word)
  184.             end
  185.             for number in string.gmatch(result, numberpattern) do
  186.                 table.insert(numbers,number)
  187.             end
  188.            
  189.             if numbers[1] and numbers[2] and numbers[3] then
  190.                 local coords = {
  191.                     x = math.floor(numbers[1]),
  192.                     y = math.floor(numbers[2]),
  193.                     z = math.floor(numbers[3])
  194.                     }
  195.                 local name = words[4]
  196.                 table.insert(names,newPlayerData(name,coords.x,coords.y,coords.z))
  197.                 debug.log("homecomer '"..name.."' is at: "..coords.x..", "..coords.y..", "..coords.z)
  198.             else
  199.                 --print("Error: Coordinate Numbers were missing")
  200.             end
  201.         end
  202.     end
  203.     return names
  204. end
  205.  
  206. --- Counts active players
  207. -- Now that we changed how player lists work, just counting the length
  208. -- does not reveal how many players are currently playing
  209. -- we need to count based on status
  210. -- @param kew A Kew object
  211. -- @return result The playercount as an Int
  212. local function countActivePlayers(kew)
  213.     local result = 0
  214.     for name, playerdata in pairs(kew.playerlist) do
  215.         if playerdata.status == registry.PLAYER_STATUS.IN_GAME then
  216.             result = result + 1
  217.         end
  218.     end
  219.     return result
  220. end
  221.  
  222. --- Removes a player from a game queue
  223. -- As playerlists are now playername indexed, we can just check if the
  224. -- player is in the list. If the player is in the list, we want to change
  225. -- his status to 1 (left with Homecomer). End the phase if this was the
  226. -- last player.
  227. -- @param game The game object
  228. -- @param playername The players name as a string
  229. local function removePlayerFromKew(game,playername)
  230.     for _,kew in pairs(game.queues) do
  231.         if (kew.playerlist[playername]) then
  232.             debug.log("changing status for player '"..playername.."' at buildzone "..kew.buildzone.locid.." to 1 - left-with-homecomer")
  233.             kew.playerlist[playername].status = registry.PLAYER_STATUS.LEFT_WITH_HOMECOMER
  234.         end
  235.         local number_of_active_players = countActivePlayers(kew)
  236.         debug.log("players left in buildzone "..kew.buildzone.locid..": "..number_of_active_players)
  237.         if number_of_active_players == 0 and kew.phase == registry.QUEUE_PHASE.PLAYING then
  238.             --game can be ended as no players are left
  239.             kew.timer = 0
  240.         end
  241.     end
  242. end
  243.  
  244. --teleports a player ot the spawn area
  245. local function movePlayerToSpawn(playername)
  246.     resetPlayer(playername)
  247.     --print("before TP")
  248.     commands.tp(playername,registry.SPAWN.x,registry.SPAWN.y,registry.SPAWN.z,registry.SPAWN.a1,registry.SPAWN.a2)
  249.     --print("after TP")
  250.     if registry.ANNOUNCE_ENGLISH then
  251.         commands.async.tellraw(playername,'["",{"text":"You used your HOMECOMER and TELEPORTED BACK TO SPAWN","color":"white"}]')
  252.     end
  253.     if registry.ANNOUNCE_GERMAN then
  254.         commands.async.tellraw(playername,'["",{"text":"Du hast den HEIMKEHRER benutzt um dich zurück zum Anfang zu teleportieren.","color":"gold"}]')
  255.     end
  256. end
  257.  
  258. --takes a list of homecomers and deals with all those players, moving them back to spawn and removing them from game
  259. --also gives them a new homecomer
  260. local function dealWithHomecomers(game, homecomers)
  261.     for _,homecomer in pairs(homecomers) do
  262.         --remove player from current game if in game
  263.         removePlayerFromKew(game,homecomer.name)
  264.         --teleport them back to spawn
  265.         movePlayerToSpawn(homecomer.name)
  266.         --give a new homecomer
  267.         giveHomecomer(homecomer.name)
  268.         --particle effects
  269.         --teleport message
  270.     end
  271.     --kill all homecomers
  272.     if #homecomers>0 then
  273.         commands.tp("@e[type="..registry.HOMECOMER_TYPE.."]",100000,200,100000)
  274.         commands.kill("@e[type="..registry.HOMECOMER_TYPE.."]")
  275.         os.sleep(#homecomers*0.2)
  276.     end
  277. end
  278.  
  279. ---
  280. function checkForHomecomers(game)
  281.     --execute on all homecomers
  282.     local homecomers = getHomecomers()
  283.     dealWithHomecomers(game,homecomers)
  284. end
  285.  
  286. --- END OF HOMECOMER FUNCTIONS
  287.  
  288.  
  289.  
  290.  
  291.  
  292.  
  293.  
  294. --creates a villager with special items
  295. local function spawnVillager(x,y,z)
  296.     commands.summon("Villager",x,y,z,'{Invulnerable:1,CustomName:Wool_Seller,Profession:2,Career:1,CareerLevel:6,Offers:{Recipes:[ {buy:{id:emerald,Count:1},sell:{id:wool,Count:'..registry.WOOL_PER_EMERALD..',tag:{CanPlaceOn:["minecraft:diamond_block","minecraft:clay","minecraft:wool","minecraft:stained_hardened_clay"]}}}, {buy:{id:emerald,Count:1},sell:{id:stone_pickaxe,Count:1,Damage:'..131-registry.PICKAXE_USES..',tag:{CanDestroy:["minecraft:wool"]}}}  ]}}')
  297. end
  298.  
  299.  
  300. --displays a time as experience points to a selection of players
  301. local function displayTime(selector,minutes,seconds)
  302.     --commands.title("@a["..selector.."]","subtitle",'{text:"Time left:  '..minutes..":"..seconds..'",color:red,bold:false,underlined:false,italic:false,strikethrough:false,obfuscated:false}')
  303.     commands.async.xp("-1000000L","@a["..selector.."]")
  304.     local secondstot = (minutes * 60) + seconds
  305.     commands.async.xp(tostring(secondstot).."L","@a["..selector.."]")
  306. end
  307.  
  308. --simply runs displayTime on a list of players
  309. local function displayTimeToGroup(playerlist,minutes,seconds)
  310.     for name,player in pairs(playerlist) do
  311.         displayTime("name="..player.name,minutes,seconds)
  312.     end
  313. end
  314.  
  315. --- Updates the Time (XP bar) for players in a buildzone
  316. --Uses a new playerlist object to only send the time to the active players
  317. -- @param A new playerlist object
  318. -- @param minutes How many minutes left
  319. -- @param seconds How many seconds are left
  320. local function displayTimeToPlayers(playerlist,minutes,seconds)
  321.   local activeList = {}
  322.   for name,player in pairs(playerlist) do
  323.     if player.status == registry.PLAYER_STATUS.IN_GAME then
  324.       table.insert(activeList,player)
  325.     end
  326.   end
  327.   displayTimeToGroup(activeList,minutes,seconds)
  328. end
  329.  
  330. local function playerListToString(playerlist)
  331.     --debug.log("converting list to string: "..json.encode(playerlist))
  332.     local s = ""
  333.     if tableLength(playerlist) > 0 then
  334.         --debug.log("it comes to the for loop")
  335.         for name, player in pairs(playerlist) do
  336.             --debug.log("adding name: "..player.name)
  337.             if (player.status) then
  338.                 s = s..", "..player.name.."["..player.status.."]"
  339.             else
  340.                 s = s..", "..player.name
  341.             end
  342.         end
  343.     end
  344.     --if
  345.     --string.sub(m, 4)
  346.     return s:sub(3)
  347. end
  348.  
  349. --displays a title to a selection of players
  350. local function displayTitle(selector,text,subtext)
  351.     commands.async.title("@a["..selector.."]","subtitle",'{text:"'..subtext..'"}')
  352.     commands.async.title("@a["..selector.."]","title",'{text:"'..text..'"}')
  353. end
  354.  
  355. --simply runs displayTitle on a list of players
  356. local function displayTitleToGroup(playerlist,text,subtext)
  357.     debug.log("displayTitleToGroup: "..text.."-"..subtext)
  358.     for name,player in pairs(playerlist) do
  359.         displayTitle("name="..player.name,text,subtext)
  360.     end
  361. end
  362.  
  363. --- Sends a title message to active players in a buildzone
  364. --Uses a new playerlist object to only send the title to the active players
  365. -- @param A new playerlist object
  366. -- @param text The message to send
  367. local function displayTitleToPlayers(playerlist,text,subtext)
  368.     debug.log("displayTitleToPlayers: "..text.."-"..subtext)
  369.     local activeList = {}
  370.     for name,player in pairs(playerlist) do
  371.         if player.status == registry.PLAYER_STATUS.IN_GAME then
  372.             table.insert(activeList,player)
  373.         end
  374.     end
  375.     displayTitleToGroup(activeList,text,subtext)
  376. end
  377.  
  378. --teleports a list of players to an exact place and sends them a message about it
  379. local function teleportToPoint(x,y,z,playerlist,clear,textEN, textDE)
  380.     for name,player in pairs(playerlist) do
  381.         player.x = x
  382.         player.y = y
  383.         player.z = z
  384.         --commands.async.gamemode(2,player.name)
  385.         if clear then
  386.             commands.async.clear(player.name,"wool")
  387.             commands.async.clear(player.name,"stone_pickaxe")
  388.         end
  389.         commands.tp("@a[name="..player.name.."]",x,y,z)
  390.         if registry.ANNOUNCE_ENGLISH then
  391.             --print("text is: ", textEN)
  392.             commands.async.tellraw(player.name,'["",{"text":"'..textEN..'","color":"white"}]')
  393.         end
  394.         if registry.ANNOUNCE_GERMAN then
  395.             commands.async.tellraw(player.name,'["",{"text":"'..textDE..'","color":"gold"}]')
  396.         end
  397.     end
  398. end
  399.  
  400. --- Teleports a list of players randomly about a buildzone
  401. -- @param buildzone the target buildzone object
  402. -- @param playerlist a name or number indexed playerlist
  403. -- @param textEN An english message to send to english players
  404. -- @param textEN An german message to send to german players
  405. --TODO make this take a box instead of buildzone and move to the mcset package or player package
  406. local function scatterIntoZone(buildzone,playerlist,textEN, textDE)
  407.     debug.log("teleporting to buildzone #"..buildzone.locid.." the players: "..playerListToString(playerlist))
  408.     local minX = buildzone.x
  409.     local minZ = buildzone.z
  410.     local maxX = buildzone.x+buildzone.w
  411.     local maxZ = buildzone.z+buildzone.w
  412.     for name,player in pairs(playerlist) do
  413.         local targetX = math.floor(math.random(minX,maxX))
  414.         local targetZ = math.floor(math.random(minZ,maxZ))
  415.         --print("targetX", targetX)
  416.         --print("targetZ", targetZ)
  417.         --print("buildzone.y+5", (buildzone.y+5))
  418.         --print("player ",player)
  419.         --print(json.encodePretty(player))
  420.         --print("textEN", textEN)
  421.         --print("textDE", textDE)
  422.         --debug.log("teleport from scatterIntoZone")
  423.         teleportToPoint(targetX, buildzone.y+5, targetZ, {player}, false, textEN, textDE)
  424.     end
  425.     debug.log("teleporting players to buildzone #"..buildzone.locid.." complete")
  426. end
  427.  
  428. --- Teleports a list of players to the centre of a given buildzone
  429. -- @param buildzone the target buildzone object
  430. -- @param playerlist a name or number indexed playerlist
  431. -- @param textEN An english message to send to english players
  432. -- @param textEN An german message to send to german players
  433. local function teleportToZone(buildzone,playerlist,textEN, textDE)
  434.     teleportToPoint(buildzone.x+2+(buildzone.w/2),buildzone.y+5,buildzone.z+2+(buildzone.w/2),playerlist,true,textEN, textDE)
  435.  
  436. end
  437.  
  438. --gives the same list of items to a list of players
  439. local function giveItems(playerlist,itemlist)
  440.     debug.log("Giving initial inventory items to players: "..playerListToString(playerlist))
  441.     local given = 0
  442.     for name,player in pairs(playerlist) do
  443.         --commands.async.clear(player.name)
  444.         for j,item in ipairs(itemlist) do
  445.             debug.log("giving item >"..item.."<")
  446.             commands.async.give("@a[name="..player.name.."]",item)
  447.             given = given +1
  448.         end
  449.         giveHomecomer(player.name)
  450.         debug.log("Gave initial inventory items to '"..player.name.."'")
  451.     end
  452.     debug.log("Giving initial inventory items to players...DONE!")
  453.     return given
  454. end
  455.  
  456. --- Finds the next free location for a buildzone in a given list of spots.
  457. -- Searches each zone in zones for an empty location.
  458. -- Zone are absolute positions (0,0),(0,1) ect.
  459. -- This uses the coords of FIRST_ZONE as given in the registry to determine
  460. -- the position of the zones for checking.
  461. -- @param zones A list of zones from BuildLocs()
  462. -- @param force Boolean. True will mean it returns the first location no matter what
  463. -- @return x The X value of the next unused location in world-coordinates
  464. -- @return y The Y value of the next unused location in world-coordinates
  465. -- @return z The Z value of the next unused location in world-coordinates
  466. -- @return locid The index of the returned zone
  467. local function findNextLoc(zones,force)
  468.     local x,y,z,locid = 0,0,0,1
  469.     for i,loc in ipairs(LOCS) do
  470.         locid = i
  471.         -- these are the coordinates of this LOC in the minecraft world
  472.         x = registry.FIRST_ZONE.x+(loc.x*( registry.BUILDZONE_WIDTH + registry.BUILDZONE_OFFSET*registry.GRIDCELL_SIZE))
  473.         y = registry.FIRST_ZONE.y
  474.         z = registry.FIRST_ZONE.z+(loc.z*( registry.BUILDZONE_WIDTH + registry.BUILDZONE_OFFSET*registry.GRIDCELL_SIZE))
  475.         --print("testing for available zone at: "..x..", "..y..", "..z)
  476.         --print("which is at grid cell at: "..loc.x..", "..loc.z)
  477.         --local result,message = commands.testforblock(x,y+registry.BUILDZONE_FLOOR_HEIGHT,z,"minecraft:air") -- this was used for the testing based on the minecraft model
  478.         local result = true
  479.         if loc.played then
  480.             result = false
  481.             --print("zone has been played")
  482.         end
  483.         --print("testing done")
  484.         --force the first zone to be selected unless it is taken in the "zones" parameter
  485.         if force then result = true end
  486.         --checks if the zone is already in the list of unavailable zones passed as parameter
  487.         local zonefree = true
  488.         for i,zone in ipairs(zones) do
  489.             if zone.x == x and zone.z == z then
  490.                 zonefree = false
  491.             end
  492.         end
  493.         --print("next position free is ",loc.x*width,oy,loc.z*width)
  494.         --if result then print("true") else print("false") end
  495.         if result and zonefree then
  496.             debug.log("Using loc at: "..x..", "..y..", "..z.." with locid: "..locid)
  497.             return x,y,z,locid --returns the coordinates of the new zone, plus its id in the LOCS table
  498.         end
  499.     end
  500.     debug.log("ERROR: findNextLoc failed")
  501.     return nil,nil,nil, nil --returns empty if no zone is available
  502.    
  503. end
  504.  
  505. --[[ looks like the moveBuildzone is not used anymore
  506. --- Assigns the next unused available play area to an abstract buildzone object
  507. -- This takes a Buildzone and an array of Absolute references
  508. -- from BuildGrid(). It finds the next area by using FindNextLoc().
  509. -- When we change a buildzones coords it is important to update locid and
  510. -- the selector so that all systems know where the buildzone is.
  511. -- @param buildzone the buildzone object to be moved
  512. -- @param zones a list of zones to choose from
  513. -- @return a boolean result. True if the buildzone was moved
  514. function moveBuildzone(buildzone,zones)
  515.     local x,y,z,locid = findNextLoc(zones)
  516.     if x and y and z and locid then
  517.         --print("moved buildzone from "..buildzone.x..","..buildzone.z.." to "..x..","..y)
  518.         local w = buildzone.w
  519.         buildzone.x,buildzone.y,buildzone.z = x,y,z
  520.         buildzone.locid = locid --reassign the location id corresponding to the LOCS item for the grid cell of the moved zone
  521.         buildzone.selector = "x="..x..",y="..tostring(y-1)..",z="..z..",dx="..w..",dy=256,dz="..w
  522.         ---buildzone.structures = {} --a list of all vocabularies which have been contructed
  523.     return true
  524.     end
  525.   return false
  526. end
  527. --]]
  528.  
  529. --multi builder to create sets of buildzones using the buildzone constructor
  530. local function initBuildzones(quant,elements)--,width)--,floorHeight)
  531.     local result = {}
  532.     for i=1,quant do
  533.         --print("locating available slot")
  534.         local x,y,z,locid = findNextLoc(result)
  535.         if x and y and z and locid then    
  536.            
  537.             table.insert(result,newBuildZone(x,y,z,elements,locid))
  538.             debug.log("Created a new Buildzone #"..locid.." at:"..x..", "..y..", "..z)
  539.         else
  540.             --print("failed to make new buildzone")
  541.         end
  542.     end
  543.    
  544.     local remaining = registry.NUMBER_OF_BUILDZONES - #result
  545.     --print("doing this remaining thing")
  546.     for i=1, remaining do
  547.         local x,y,z,locid = findNextLoc(result,true)
  548.         if x and y and z and locid then    
  549.             debug.log("forced new buildzone #"..locid.." at:"..x..", "..y..", "..z)
  550.             table.insert(result,newBuildZone(x,y,z,elements,locid))
  551.         else
  552.             debug.log("failed to force init a new buildzone")
  553.         end
  554.     end
  555.    
  556.    
  557.     return result
  558. end
  559.  
  560.  
  561.  
  562. --- Buildzone constructor. Enforces some data structure
  563. -- Buildzones record information about the structures which are built
  564. -- inside them, as well as which elements of the catalogue are
  565. -- available to be built in them. Buildzones record where they are in
  566. -- three ways (locid, selector and coords). Buildzones do not know
  567. -- who is playing in them right now, or how long is left in a game
  568. -- @param x Where you first want to place the buildzone
  569. -- @param y Where you first want to place the buildzone
  570. -- @param z Where you first want to place the buildzone
  571. -- @param elementZones An array of catalogue items that can be built here
  572. -- @param locid The buildzones starting location in the buildLocs() array
  573. function newBuildZone(x,y,z,elementZones,locid)
  574.     local nbz = {}
  575.     nbz.x ,nbz.y ,nbz.z ,nbz.w = x,y,z,registry.BUILDZONE_WIDTH
  576.     nbz.selector = "x="..(nbz.x-2)..",y="..(nbz.y-3)..",z="..(nbz.z-2)..",dx="..(nbz.w+4)..",dy=256,dz="..(nbz.w+4)
  577.     nbz.structures = {} --a list of all vocabularies names which have been contructed
  578.     nbz.buildings = {} --a list of all vocabularies with full data (x,y,z,id,name) which have been contructed
  579.     nbz.filledSlots = 0 --to count how many slots have been filled with buildings. the matrix is 7x7x20 slots. one slot is 9x9x9 blocks big
  580.     nbz.greenSlots = 0 --to count how many of the slots are green. the matrix is 7x7x20 slots. one slot is 9x9x9 blocks big
  581.     --nbz.variety = {} -- this stores how many buildings of each type are there. it is indexed on vocab.id and the value is the number of buildings from type vocab.id
  582.     nbz.waitingForCheck = {}
  583.     nbz.highest = 0
  584.     nbz.elements = elementZones
  585.     nbz.locid = locid
  586.     return nbz
  587. end
  588.  
  589. --[[
  590. --- Kew constructor. Enforces some data structure
  591. -- Kews are used for timekeeping and to record which players are
  592. -- interacting where. Each queue can only have 1 buildzone, but may have
  593. -- many players. Buildzones may move location, but Kews do not.
  594. -- @param buildzone The buildzone object to associate with this Kew
  595. -- @param maxplayers How many players are allowed to play in the buildzone
  596. -- @param portal A portal object created with newPortal()
  597. -- @return The constructed Kew Object
  598. function newQueue(buildzone,maxplayers,portal)
  599.     debug.log("creating a new game queue for buildzone #"..buildzone.locid)
  600.     local q = {}
  601.     q.timer = 1
  602.     q.phase = registry.QUEUE_PHASE.DORMANT
  603.    
  604.     q.victory = false
  605.     q.phases = {
  606.         {
  607.             name = "Selecting Players",
  608.             length = registry.PHASE_LENGTH_WAIT
  609.             --displaylength = 15 --this field is not used
  610.         },
  611.         {
  612.             name = "Game In Progress",
  613.             length = registry.PHASE_LENGTH_GAME
  614.             --displaylength = 70 --this field is not used
  615.         },
  616.         {
  617.             name = "Round Complete",
  618.             length = registry.PHASE_LENGTH_OVER
  619.             --displaylength = 5 --this field is not used
  620.         }
  621.     }
  622.     q.playerlist = {} --needs a metatable to know how to convert to string
  623.     setmetatable(q.playerlist, {__tostring = playerListToString}) -- TODO put this as playerlist:new() in the player package
  624.    
  625.     q.maxplayers = maxplayers
  626.     q.portal = portal
  627.     q.buildzone = buildzone
  628.     local timestamp = math.floor(os.clock())
  629.     q.filename = timestamp.."at"..q.buildzone.x.."_"..q.buildzone.z --TODO this needs to be set and reset in onEnter_play
  630.     return q
  631. end
  632.  
  633. --]]
  634.  
  635. --[[
  636. function setGameSettings(game)
  637.     -- assemble a table of the current settings that affect the world design
  638.     -- TODO: make the following read first from the settings file and then load the defaults from registry
  639.     local settings = {}
  640.     settings.grid_cell_size         = registry.GRIDCELL_SIZE
  641.     settings.grid_cell_count        = registry.GRIDCELL_COUNT
  642.     settings.catalogue_slot_count   = registry.CATALOGUE_SLOTCOUNT
  643.     settings.play_area_offset       = registry.PLAY_AREA_OFFSET
  644.     settings.trenches               = registry.TRENCHES
  645.     --settings.game_field           = registry.GAME_FIELD
  646.     --settings.game_field_max           = registry.GAME_FIELD_MAX
  647.     settings.buildzone_offset       = registry.BUILDZONE_OFFSET
  648.     settings.catalogue_slot_size    = registry.CATALOGUE_SLOTSIZE
  649.     settings.catalogue_slot_offset  = registry.CATALOGUE_SLOT_OFFSET
  650.     settings.spawnzone_size     = registry.SPAWNZONE_SIZE
  651.     settings.element_height         = registry.ELEMENT_HEIGHT
  652.     settings.portal_count           = registry.PORTAL_COUNT
  653.     settings.buildzone_count        = registry.BUILDZONE_COUNT
  654.     settings.portal_offset          = registry.PORTAL_OFFSET
  655.     settings.portal_size            = registry.PORTAL_SIZE
  656.     settings.portal_height          = registry.PORTAL_HEIGHT
  657.    
  658.     settings.computer = mcget.computerPosition()
  659.    
  660.     game.settings = settings -- store them into the game object
  661.     game.last_settings = nil
  662.    
  663.     local filename = "last_settings.json"
  664.     local filename_with_path = registry.RECORDS_FOLDER..filename
  665.    
  666.     if file_exists(filename_with_path) then
  667.         debug.log("game has been run before - will store the last settings in the game object")
  668.         -- read a table of the settigns used last time from the file
  669.         local last_settings = json.decodeFromFile(filename_with_path)
  670.         game.last_settings = last_settings -- return the last settigns as well so that catalogue slots can adapt
  671.     end
  672.    
  673.     --update the last settings file it
  674.     debug.log("saving current settgins as last_settings_file for next time the 'play' script is run")
  675.     file_write(json.encodePretty(settings), filename, registry.RECORDS_FOLDER)
  676.     --fs.makeDir(registry.RECORDS_FOLDER)
  677.     --local file = fs.open(filename_with_path,"w")
  678.     --file.write(json.encodePretty(settings))
  679.     --file.close()
  680. end
  681.  
  682. function setGameSpecialBlocks(game)
  683.     game.blocks = registry.BLOCKS
  684. end
  685. --]]
  686.  
  687. --- Checks if there is a change of the parameters that define the game area geometry since the last run
  688. -- Currently if there is a change it returns the redo_everything result
  689. -- TODO if game has been run and there is change affecting the catalogue area add 8 to result for partial rebuild only the catalogue areas
  690. -- TODO if game has been run and there is change affecting the play area add 4 to result for partial rebuild only the play areas
  691. -- TODO if game has been run and there is change affecting the spawn area add 2 to result for partial rebuild only the spawn areas
  692. function checkForGameAreaParametersChanges(game)
  693.     --local redo_nothing = 0
  694.     --local redo_spawn_area = 2
  695.     --local redo_play_area = 4
  696.     --local redo_catalogue = 8
  697.     --local redo_everything = redo_spawn_area + redo_play_area + redo_catalogue
  698.     --
  699.     --local result = redo_nothing
  700.     local result = {}
  701.     result.redo_everything = false
  702.     result.redo_spawn_area = false
  703.     result.redo_play_area = false
  704.     result.redo_catalogue = false          
  705.     --
  706.     -- assemble a table of the current settings that affect the world design
  707.     local settings = game.settings
  708.     --[[
  709.     settings.grid_cell_size         = registry.GRIDCELL_SIZE
  710.     settings.grid_cell_count        = registry.GRIDCELL_COUNT
  711.     settings.catalogue_slot_count   = registry.CATALOGUE_SLOTCOUNT
  712.     settings.play_area_offset       = registry.PLAY_AREA_OFFSET
  713.     settings.trenches               = registry.TRENCHES
  714.     settings.game_field             = registry.GAME_FIELD
  715.     settings.buildzone_offset       = registry.BUILDZONE_OFFSET
  716.     settings.catalogue_slot_size    = registry.CATALOGUE_SLOTSIZE
  717.     settings.catalogue_slot_offset  = registry.CATALOGUE_SLOT_OFFSET
  718.     --]]
  719.    
  720.    
  721.    
  722.     --local filename = "last_settings.json"
  723.     --local filename_with_path = registry.RECORDS_FOLDER.."/"..filename
  724.    
  725.     --if file_exists(filename_with_path) then
  726.     if game.last_settings then
  727.         debug.log("game has been run before - will compare last with current settings")
  728.         -- read a table of the settigns used last time from the file
  729.         local last_settings = game.last_settings
  730.         --result.last_settings = last_settings -- return the last settigns as well so that catalogue slots can adapt
  731.         --compare the two tables
  732.         if settings.game_field.countX ~= last_settings.game_field.countX
  733.             or settings.game_field.countZ ~= last_settings.game_field.countZ
  734.             then
  735.                 --delete the buildzones.json file
  736.                 debug.log("deleting buildzone list file because settings have changed")
  737.                 fs.delete(registry.BUILDZONES_LIST_FILE)
  738.                 result.redo_play_area = true
  739.             end
  740.        
  741.         if settings.grid_cell_size ~= last_settings.grid_cell_size then result.redo_everything = true end
  742.        
  743.         if settings.grid_cell_count ~= last_settings.grid_cell_count then result.redo_play_area = true end
  744.        
  745.         if settings.catalogue_slot_count.x ~= last_settings.catalogue_slot_count.x or
  746.             settings.catalogue_slot_count.z ~= last_settings.catalogue_slot_count.z
  747.             then result.redo_catalogue = true end
  748.            
  749.         if settings.play_area_offset.x ~= last_settings.play_area_offset.x or
  750.             settings.play_area_offset.z ~= last_settings.play_area_offset.z
  751.             then
  752.                 result.redo_play_area = true
  753.                 result.redo_catalogue = true
  754.             end
  755.        
  756.         if settings.trenches.WIDTH ~= last_settings.trenches.WIDTH or
  757.             settings.trenches.DEPTH ~= last_settings.trenches.DEPTH
  758.             then result.redo_everything = true  end
  759.        
  760.         if settings.buildzone_offset ~= last_settings.buildzone_offset then result.redo_play_area = true end
  761.        
  762.         if settings.catalogue_slot_size.x ~= last_settings.catalogue_slot_size.x or
  763.             settings.catalogue_slot_size.z ~= last_settings.catalogue_slot_size.z
  764.             then result.redo_catalogue = true end
  765.            
  766.         if settings.catalogue_slot_offset ~= last_settings.catalogue_slot_offset then
  767.             result.redo_play_area = true
  768.             result.redo_catalogue = true
  769.         end
  770.     else       
  771.         debug.log("game has not been run before - will generate world")
  772.         -- delete any buildzones list file that might be left there
  773.         debug.log("deleting buildzone list file in case it exists")
  774.         fs.delete(registry.BUILDZONES_LIST_FILE)
  775.         --end return 2+4+8 for full regeneration
  776.         result.redo_everything = true
  777.     end
  778.     --last used settings file doesn't exist
  779.     --create it
  780.     --debug.log("saving last settings file")
  781.     --fs.makeDir(registry.RECORDS_FOLDER)
  782.     --local file = fs.open(filename_with_path,"w")
  783.     --file.write(json.encodePretty(settings))
  784.     --file.close()
  785.     --
  786.     return result
  787. end
  788.  
  789.  
  790.  
  791. --- Creates a game object and initializes game management functions
  792. -- This function is a bit large, and should be placed in its own module
  793. -- at some point.
  794. -- The game object creates and stores the complete Catalogue
  795. -- The game object creates and stores a complete list of all Buildzones
  796. -- The game object creates and stores a complete list of all Kews
  797. -- The game object only tracks players who are NOT in a Kew
  798. -- The game object knows where the spawn is
  799. -- This function rebuilds the Catalogue and Buildzones in the gameworld
  800. -- if any settings have changed.
  801. -- This function adds scoreboards to your Minecraft world so that 20kb
  802. -- operates correctly.
  803. -- This function runs GameRule commands to set up your Minecraft world.
  804. -- If DEBUG_MODE is on, then Kews are fixed to Play Phase with a large
  805. -- time limit
  806. -- This function kills all Villagers; it is the only way to be sure.
  807. -- @return game The Game container object.
  808. function setup()
  809.     debug.log("Starting Setup function.")
  810.    
  811.     local game = newGameManager()
  812.  
  813.     game.version_number = VERSION_NUMBER
  814.     game.version_name = VERSION_NAME
  815.    
  816.     game.settings, game.last_settings = loader.getGameSettings()
  817.     checkGameSettings( game.settings )
  818.    
  819.     game.settings.computer = mcget.computerPosition()
  820.    
  821.     debug.log('getting starting items')
  822.     game.settings.starting_items = loader.getStartingItems(game.settings)
  823.     debug.log("Starting items are: "..json.encode(game.settings.starting_items))
  824.    
  825.     -- put the settigns into the game object
  826.     -- game.settings, game.lastsettings =
  827.     --setGameSettings(game)
  828.     --setGameSpecialBlocks(game)
  829.     ---SETTINGS = registry.loadCustomSettings()
  830.    
  831.     debug.log("Game object created")
  832.    
  833.     --SET UP THE GAME AREA
  834.     debug.log("Setting up game area...")
  835.     --do a file check to see if setting up the game area is needed
  836.     debug.log("Checking for changed settings...")
  837.     local rebuild = checkForGameAreaParametersChanges(game)
  838.     debug.log("Need to recreate: "..json.encode(rebuild))
  839.    
  840.     game.spawnzone_box = getSpawnAreaLocation(game.settings)
  841.    
  842.     --check if spawn area needs rebuilding
  843.     if rebuild.redo_everything or rebuild.redo_spawn_area then 
  844.         portal.drawSpawnZone(game.settings)
  845.     end
  846.    
  847.     if rebuild.redo_everything or rebuild.redo_play_area then  
  848.         debug.log("building play area...")
  849.         --debug.log("fillBox from inside build play area")
  850.         mcset.fillBox(
  851.             box_offset( box ( registry.PLAY_AREAS.x ,
  852.             registry.PLAY_AREAS.y,
  853.             registry.PLAY_AREAS.z,
  854.             registry.PLAY_AREAS.dx,
  855.             -registry.TRENCHES.DEPTH,
  856.             registry.PLAY_AREAS.dz),
  857.             registry.TRENCHES.WIDTH),
  858.             registry.BLOCKS.AIR
  859.         )
  860.         mcset.fillBox(
  861.             box(registry.PLAY_AREAS.x ,
  862.             registry.PLAY_AREAS.y,
  863.             registry.PLAY_AREAS.z,
  864.             registry.PLAY_AREAS.dx,
  865.             -registry.PLAY_AREAS.y,
  866.             registry.PLAY_AREAS.dz),
  867.             registry.BLOCKS.DARK_GRID
  868.         )
  869.         local x,y,z = registry.FIRST_ZONE.x, registry.FIRST_ZONE.y, registry.FIRST_ZONE.z
  870.         for i=0,registry.GAME_FIELD.countZ - 1 do
  871.             for k=0,registry.GAME_FIELD.countX - 1 do
  872.                 local xpos = x + k*( registry.BUILDZONE_WIDTH + registry.BUILDZONE_OFFSET*registry.GRIDCELL_SIZE)
  873.                 local ypos = y
  874.                 local zpos = z + i*( registry.BUILDZONE_WIDTH + registry.BUILDZONE_OFFSET*registry.GRIDCELL_SIZE)
  875.                 mcset.fillRing(box(xpos,ypos,zpos,registry.BUILDZONE_WIDTH,1,registry.BUILDZONE_WIDTH),registry.BLOCKS.WHITE_GRID)
  876.             end
  877.         end
  878.         mcset.fillGrid(
  879.             box( registry.PLAY_AREAS.x ,
  880.             registry.PLAY_AREAS.y,
  881.             registry.PLAY_AREAS.z,
  882.             registry.PLAY_AREAS.dx,
  883.             1,
  884.             registry.PLAY_AREAS.dz),
  885.             registry.computer.x,
  886.             registry.computer.y,
  887.             registry.computer.z,
  888.             registry.GRIDCELL_SIZE,
  889.             registry.BLOCKS.WHITE_GRID
  890.         )
  891.     end
  892.    
  893.     --catalogue slots creation
  894.     debug.log("initializing catalogue elements...")
  895.     game.catalogue = catalogue.initCatalogue(game.settings)
  896.     --draw the catalogue slots in Minecraft
  897.     catalogue.drawCatalogue(game)  
  898.     debug.log("Catalogue elements successfully initialized!")
  899.    
  900.    
  901.    
  902.    
  903.    
  904.    
  905.     --kill all villagers
  906.     mcset.killAllEntities("Villager")
  907.     debug.log("Villagers destroyed")
  908.    
  909.     -- init the portals
  910.     debug.log("Making Portal objects.")
  911.     game.portals = portal.initPortals(game.settings)
  912.    
  913.     debug.log("starting the building of portals...")
  914.     for i, p in ipairs(game.portals) do
  915.         debug.log("building of portal:",i)
  916.         portal.drawPortal( p )
  917.     end
  918.     debug.log("building of portals DONE!")
  919.    
  920.     --buildzone creation
  921.     debug.log("Making building zone objects.")
  922.     --game.builds appears to store the games currently in progress
  923.     --game.builds = initBuildzones(registry.NUMBER_OF_BUILDZONES,game.elements,registry.BUILDZONE_WIDTH)--,registry.BUILDZONE_FLOOR_HEIGHT)
  924.     buildzones = initBuildzones(registry.NUMBER_OF_BUILDZONES,game.catalogue,registry.BUILDZONE_WIDTH)--,registry.BUILDZONE_FLOOR_HEIGHT)
  925.     --for i,build in ipairs(game.builds) do
  926.     for i,build in ipairs(buildzones) do
  927.         --print("portal "..i.." is "..tostring(game.portals[i]))
  928.         table.insert(game.queues,newQueue(build, registry.MAX_PLAYERS_PER_GAME, game.portals[i]))
  929.     end
  930.     debug.log("updating portals to match queue states...")
  931.     for i, kew in ipairs(game.queues) do
  932.         debug.log("updating of portal:",i)
  933.         portal.drawPortal( kew.portal, kew.phase)
  934.     end
  935.     debug.log("updating of portals DONE!")
  936.  
  937.     ----
  938.     debug.log("Adding scoreboards.")
  939.         --print(#registry.VOCABS_DATA)
  940.     for i=1,#registry.VOCABS_DATA do
  941.         commands.scoreboard("objectives","add","building_"..i,"dummy")
  942.     end
  943.     commands.scoreboard("objectives","add","highscores","dummy","Best Neighbourhoods")
  944.     if registry.SHOW_HIGHSCORES then
  945.         commands.scoreboard("objectives","setdisplay","sidebar","highscores")
  946.     end
  947.     commands.scoreboard("objectives","add","VillagerLife","dummy")
  948.     commands.scoreboard("objectives","add","built","dummy", "Structures Built")
  949.     commands.scoreboard("objectives","add","highest","dummy", "Personal Highest")
  950.     commands.scoreboard("objectives","add","played","dummy","Games Played")
  951.     commands.scoreboard("objectives","setdisplay","list","played")
  952.    
  953.     commands.title("@a","times",0,30,30) -- what does this do?
  954.    
  955.     ----
  956.     debug.log("setting the needed preferences of the minecraft world.")
  957.     --TODO set world spawn point to computer location
  958.     commands.gamerule("doDaylightCycle",false) --
  959.     commands.gamerule("keepInventory",true)
  960.     commands.gamerule("doTileDrops",false) --
  961.     commands.gamerule("sendCommandFeedback", true)
  962.     commands.gamerule("logAdminCommands",true) -- it looks like this needs to be on for the tp funtion used in getAllPos to return its message properly
  963.     commands.gamerule("commandBlockOutput",false) --
  964.     commands.time("set",6000) --
  965.    
  966.     ---
  967.     math.randomseed( os.time() )
  968.     debug.log("Computer clock is: "..os.clock())
  969.     debug.log("20.000 BLOCKS is ready to run!")
  970.    
  971.     return game
  972. end
  973.  
  974. --- Runs a single step of the game loop
  975. -- Runs the game object through each of these update steps in order.
  976. -- See the comments on each of the steps.
  977. -- @param game The game object as created in setup()
  978. function update(game)
  979.     --debug.log("updating game state")
  980.     local elapsed = updateClock(game)
  981.     --update players
  982.     --debug.log("update: will checkPlayers")
  983.     checkPlayers(game)
  984.     --debug.log("update: will doTimerUpdates")
  985.     doTimerUpdates(game,elapsed)
  986.     --debug.log("update: will doPhaseUpdates")
  987.     doPhaseUpdates(game)
  988.     --debug.log("update: will doPhaseEnds")
  989.     doPhaseEnds(game)
  990.     --debug.log("update: will checkBoundaries")
  991.     checkBoundaries(game)
  992.     --debug.log("update: will checkForHomecomers")
  993.     checkForHomecomers(game)
  994.     --debug.log("update: will allocateWaiters")
  995.     allocateWaiters(game)
  996. end
  997.  
  998. --- Calculates elapsed time during a game tick
  999. -- Updates the given game objects clock using the operating system time
  1000. -- @param game The game object as created in setup()
  1001. -- @return elapsed How much time has elapsed since time was updated
  1002. function updateClock(game)
  1003.     game.nowTime = os.clock()
  1004.     local elapsed = game.nowTime - game.lastClock
  1005.     game.lastClock = game.nowTime
  1006.     return elapsed
  1007. end
  1008.  
  1009. --- Updates all kews in the game object based on elapsed time
  1010. -- Since Kews do the timekeeping, let them know how much time as elapsed.
  1011. -- @param game The game object as created in setup()
  1012. -- @param elapsed Seconds as calculated by updateClock()
  1013. function doTimerUpdates(game,elapsed)
  1014.     for i,kew in ipairs(game.queues) do
  1015.         --print("updating timer for zone: ", i)
  1016.         --print("timer is : ", kew.timer)
  1017.         --print("phase is : ", kew.phase)
  1018.         kew.timer = kew.timer - elapsed
  1019.     end
  1020. end
  1021.  
  1022. --- Deal with players who should be in Buildzones
  1023. -- Checks all Kews (and therefore Buildzones). If the Kew is in Phase 2
  1024. -- (Play Phase), then check on all players. If those players are out
  1025. -- of bounds, move them back to the closest edge of the buildzone.
  1026. -- This also sends them a bi-lingual warning if there were moved.
  1027. -- @param game The game object as created in setup()
  1028. function checkBoundaries(game)
  1029.     --debug.log("begin checking boundaries")
  1030.     for i,kew in ipairs(game.queues) do
  1031.         --debug.log("checking boundaries for buildzone #"..kew.buildzone.locid)
  1032.         if kew.phase == registry.QUEUE_PHASE.PLAYING then
  1033.             --boundaries
  1034.             --debug.log("buildzone #"..kew.buildzone.locid.." is in phase 2")
  1035.             local x_min = kew.buildzone.x-2
  1036.             local x_max = kew.buildzone.x+kew.buildzone.w+4
  1037.             local z_min = kew.buildzone.z-2
  1038.             local z_max = kew.buildzone.z+kew.buildzone.w+4
  1039.  
  1040.             --local toBeCorrected = {}
  1041.  
  1042.             for name,player in pairs(kew.playerlist) do
  1043.                 --debug.log("checking boundaries for player "..player.name.." indexed as "..name)
  1044.                 if player.status == registry.PLAYER_STATUS.IN_GAME then
  1045.                     --debug.log("player "..name.." is active")
  1046.                     --debug.log("calling getAllPos for player "..player.name)
  1047.                     local listOfOne = getAllPos('m=2,name='..player.name)
  1048.                     --debug.log("getAllPos for player "..player.name.." completed")
  1049.                     if listOfOne and listOfOne[1] then
  1050.                         --debug.log("getAllPos for player "..player.name.." returned a value")
  1051.                         player.x = listOfOne[1].x
  1052.                         player.y = listOfOne[1].y
  1053.                         player.z = listOfOne[1].z
  1054.                         local changed = false
  1055.                         if player.x > x_max then
  1056.                             changed = true
  1057.                             player.x = x_max-4
  1058.                         end
  1059.                         if player.x < x_min then
  1060.                             changed = true
  1061.                             player.x = x_min+4
  1062.                         end
  1063.                         if player.z > z_max then
  1064.                             changed = true
  1065.                             player.z = z_max-4
  1066.                         end
  1067.                         if player.z < z_min then
  1068.                             changed = true
  1069.                             player.z = z_min+4
  1070.                         end
  1071.                         if changed then
  1072.                             teleportToPoint(player.x,kew.buildzone.y+2,player.z,{player},false,
  1073.                             "TELEPORTED BACK TO GAME: Please stay inside the building zone or use HOMECOMER to leave the game!",
  1074.                             "Zurück ins Spiel teleportiert: Bitte bleib innerhalb des Baufeldes oder nutze den HEIMKEHRER um das Spiel zu verlassen!")
  1075.                         end
  1076.                     end
  1077.                 end
  1078.                 --debug.log("Completed: checking boundaries for player "..player.name.." indexed as "..name)
  1079.             end
  1080.         end
  1081.     end
  1082.     --debug.log("end checking boundaries")
  1083. end
  1084.  
  1085.  
  1086. --- Check if a Buildzone is worth saving
  1087. -- Many plays of a buildzone will be junk. Here you can set the logic
  1088. -- which determines if a buildzone was valuable.
  1089. -- Currently always returns that the zone is valuable.
  1090. -- @param buildzone The buildzone to check.
  1091. -- @return True is the buildzone was junk
  1092. function checkIfBuildzoneIsCrap(buildzone)
  1093.     debug.log("Buildzone was ok")
  1094.     return false
  1095. end
  1096.  
  1097.  
  1098. --- Everything that happens when the Play Phase finishes due to time limit.
  1099. -- Removes the ring which denotes a game in progress.
  1100. -- Removes Detector Blocks and then fills the floor in.
  1101. -- Checks if the buildzone was valuable and if so it will clean it.
  1102. -- Sets the zone played state based on value. A played zone will not be
  1103. -- overwritten by future buildzones.
  1104. -- Bilingual messages are sent to players to let them know what happened.
  1105. -- @param kew A kew object that contains a Buildzone
  1106. function cleanAfterGameOver(kew)
  1107.     local buildzone = kew.buildzone
  1108.     debug.log("cleanAfterGameOver started for buildzone #".. buildzone.locid)
  1109.     commands.async.setblock(buildzone.x,buildzone.y,buildzone.z,registry.BLOCKS.VICTORY_MARKER.block)
  1110.     mcset.fillRing(box(buildzone.x,buildzone.y,buildzone.z,buildzone.w,1,buildzone.w),registry.BLOCKS.AIR)
  1111.     --for each level remove playing blocks like detectors
  1112.   for h=1,256-buildzone.y do
  1113.         commands.async.fill(buildzone.x,buildzone.y+h,buildzone.z,buildzone.x+buildzone.w,buildzone.y+h,buildzone.z+buildzone.w,"minecraft:air 0","replace",registry.BLOCKS.DETECT.block,registry.BLOCKS.DETECT.variant)
  1114.         commands.async.fill(buildzone.x,buildzone.y+h,buildzone.z,buildzone.x+buildzone.w,buildzone.y+h,buildzone.z+buildzone.w,"minecraft:air 0","replace",registry.BLOCKS.PLUG.block,registry.BLOCKS.PLUG.variant)
  1115.         commands.async.fill(buildzone.x,buildzone.y+h,buildzone.z,buildzone.x+buildzone.w,buildzone.y+h,buildzone.z+buildzone.w,"minecraft:air 0","replace",registry.BLOCKS.BUILDING_GARDEN.block,registry.BLOCKS.BUILDING_GARDEN.variant)
  1116.         commands.async.fill(buildzone.x,buildzone.y+h,buildzone.z,buildzone.x+buildzone.w,buildzone.y+h,buildzone.z+buildzone.w,"minecraft:air 0","replace",registry.BLOCKS.BUILDING_HOUSE.block,registry.BLOCKS.BUILDING_HOUSE.variant)
  1117.     end
  1118.   --replaces air on the bottom level with flooring to show the area is completed
  1119.     commands.async.fill(buildzone.x,buildzone.y,buildzone.z,buildzone.x+buildzone.w,buildzone.y,buildzone.z+buildzone.w,registry.BLOCKS.CAMP_FLOOR.block,registry.BLOCKS.CAMP_FLOOR.variant,"replace",registry.BLOCKS.PLUG.block,registry.BLOCKS.PLUG.variant)
  1120.     commands.async.fill(buildzone.x,buildzone.y+1,buildzone.z,buildzone.x+buildzone.w,buildzone.y,buildzone.z+buildzone.w,registry.BLOCKS.PHVFLOOR.block,registry.BLOCKS.PHVFLOOR.variant,"replace","minecraft:air","0")
  1121.    
  1122.     ---add here a message to the players that they can see their finished game on the webviewer OR that their game was not save because they built too little
  1123.    
  1124.     local wasCrap = checkIfBuildzoneIsCrap(buildzone)
  1125.     local gameovermessageEN
  1126.     local gameovermessageDE
  1127.     if wasCrap then
  1128.         --mark this buildzone for replacement
  1129.         --change the flag to played=false
  1130.             debug.log("cleanAfterGameOver is crap ".. buildzone.locid)
  1131.         updateZonePlayedState(buildzone,false)
  1132.         gameovermessageEN = "Thank you for playing IBA_GAME! This game will be discarded because you built less than "..registry.MIN_BUILDINGS.." buildings. Play another game or check what others have built at: www.20000blocks.com"
  1133.         gameovermessageDE = "Vielen Dank, dass du IBA_GAME gespielt hast! Diese Runde wird verworfen, da weniger als "..registry.MIN_BUILDINGS.." Gebäude gebaut wurden. Starte eine neue Runde oder schau dir die Spielergebnisse anderer Spieler an unter: www.2000blocks.com"
  1134.        
  1135.     else
  1136.         --change the flag to played=true
  1137.         debug.log("cleanAfterGameOver otherwise ".. buildzone.locid)
  1138.         updateZonePlayedState(buildzone,true)
  1139.        
  1140.         gameovermessageEN = "Thank you for playing IBA_GAME! Play another game or look for your game result at: www.20000blocks.com"
  1141.         gameovermessageDE = "Vielen Dank, dass du IBA_GAME gespielt hast! Starte eine neue Runde oder schau dir deine Spielergebnisse an unter: www.20000blocks.com"
  1142.     end
  1143.    
  1144.     for name, player in pairs(kew.playerlist) do
  1145.         if registry.ANNOUNCE_ENGLISH then
  1146.             -- announce success in English
  1147.             commands.async.tellraw(player.name,'["",{"text":"'..gameovermessageEN..'","color":"white"}]')
  1148.         end
  1149.         if registry.ANNOUNCE_GERMAN then
  1150.             -- announce success in German
  1151.             commands.async.tellraw(player.name,'["",{"text":"'..gameovermessageDE..'","color":"gold"}]')
  1152.         end
  1153.     end
  1154.    
  1155. end
  1156.  
  1157. --- Changes a given locid to played.
  1158. -- Strange that the LOCS object is a global and not in the registry.
  1159. -- That is probably bad. This function also saves the given location to
  1160. -- file.
  1161. -- @param buildzone The buildzone that was just finished
  1162. -- @param newstate Boolean Has the location been played successfully?
  1163. function updateZonePlayedState(buildzone, newstate)
  1164.     debug.log("updating buildzone's #"..buildzone.locid.." played stated to: "..tostring(newstate))
  1165.     --change the flag to played=newstate
  1166.     LOCS[buildzone.locid].played = newstate
  1167.     --and write the LOCS object to the json file
  1168.     writeGridToFile()
  1169. end
  1170.  
  1171. --- Process timekeeping and update Phases of Kews.
  1172. -- Run every game step. Iterate over all Kews and update their phase
  1173. -- based on the remaining time in kew.timer.
  1174. -- If the kew is on the Play Phase, then do the gameplay logic
  1175. -- stored in updatePlayedZone().
  1176. -- Update player timers (xp bar timer) and give players a large warning
  1177. -- title if the game is almost over.
  1178. -- @param game The game object as created by setup()
  1179. function doPhaseUpdates(game)
  1180.     for i,kew in ipairs(game.queues) do
  1181.        
  1182.         local minutes = string.format("%02d",math.floor(kew.timer/60))
  1183.         local seconds = string.format("%02d",math.floor(kew.timer - (minutes*60)))
  1184.         if kew.timer <= 0 then
  1185.             minutes = "00"
  1186.             seconds = "00"
  1187.         end
  1188.        
  1189.         if kew.phase == registry.QUEUE_PHASE.DORMANT then
  1190.             --waiting phase
  1191.             -- Never start the wait phase until someone joins
  1192.             if countActivePlayers(kew) == 0 then
  1193.                 kew.timer = kew.phases[kew.phase].length
  1194.             else
  1195.                 -- but if we have a player then start now
  1196.                 kew.timer = 0
  1197.             end
  1198.            
  1199.             --displayTitleToPlayers(kew.playerlist,"Game starting!")
  1200.             displayTimeToPlayers(kew.playerlist,minutes,seconds)
  1201.         elseif kew.phase == registry.QUEUE_PHASE.PLAYING then
  1202.             --playing phase
  1203.             --debug.log("buildzone #"..)
  1204.             local victory = updatePlayedZone(kew) --currently victory updatePlayedZone returns always false
  1205.             displayTimeToPlayers(kew.playerlist,minutes,seconds)
  1206.             if victory then
  1207.                 kew.timer = -2
  1208.             end
  1209.            
  1210.             -- unless finish if all players quit
  1211.             if countActivePlayers(kew) == 0 then
  1212.                 kew.timer = -1
  1213.             end
  1214.         elseif kew.phase == registry.QUEUE_PHASE.GAMEOVER then
  1215.             --end phase
  1216.             displayTimeToPlayers(kew.playerlist,minutes,seconds)
  1217.         end
  1218.     end
  1219. end
  1220.  
  1221. --- Update the Minecraft Scoreboards based on what players built.
  1222. -- This runs after a buildzone is completed it currently rewards all
  1223. -- participants with more played score.
  1224. -- @param kew A Kew object which has just moved from Phase PLAYING to GAMEOVER.
  1225. function processHighscores(kew)
  1226.     local buildzone = kew.buildzone
  1227.     for name,player in pairs(kew.playerlist) do
  1228.         commands.async.scoreboard("players","add",player.name,"played",1)
  1229.     end
  1230. end
  1231.  
  1232. --- Export a Kews buildzone detail once it is complete
  1233. -- Requires a kew so it can access the playerlist.
  1234. -- @param kew The Kew object which contains players and a buildzone
  1235. -- @param saveOnline Boolean true: save the game also online to the database; false: save only in a file locally
  1236. -- TODO move this to an IO package ???
  1237. local function exportKewData(kew, saveOnline)
  1238.     local buildzone = kew.buildzone
  1239.     local saved = {}
  1240.     saved.position =
  1241.         {
  1242.             x=buildzone.x,
  1243.             y=buildzone.y,
  1244.             z=buildzone.z
  1245.         }
  1246.    
  1247.     saved.players = {}
  1248.     for name, player in pairs(kew.playerlist) do
  1249.         table.insert(saved.players,player.name)
  1250.     end
  1251.     --saved.structures = buildzone.structures
  1252.     saved.buildings = buildzone.buildings
  1253.     --saved.totals = tracker.tallyTable(buildzone.buildings)
  1254.     --saved.highest = buildzone.highest
  1255.     saved.stats = {
  1256.         --cityVersion = registry.CITY_VERSION,
  1257.         --height = buildzone.highest,
  1258.         --densityIndex = math.floor(100*buildzone.filledSlots/49), -- the density index is made from built area (filledSlots) over the ground area (7x7 slots = 49)
  1259.         --greenIndex = math.floor(100*buildzone.greenSlots/49), --the green index is made from green area (greenSlots) over the ground area (7x7 slots = 49)
  1260.         --variety = tableLength(buildzone.variety),
  1261.         timeCompleted = math.floor(os.clock()),
  1262.         gameLength = registry.PHASE_LENGTH_GAME
  1263.     }
  1264.    
  1265.    
  1266.     fs.makeDir(registry.RECORDS_FOLDER)
  1267.    
  1268.     local file = fs.open(registry.RECORDS_FOLDER.."/"..kew.filename..".json","w")
  1269.     local filecontent = json.encodePretty(saved)
  1270.     debug.log("saving the game at buildzone "..kew.buildzone.locid.." to local file...")
  1271.     file.write(filecontent)
  1272.     file.close()
  1273.     debug.log("local file saved: "..registry.RECORDS_FOLDER.."/"..kew.filename..".json")
  1274.     if saveOnline then --for now this is disabled until we figure out how things look in the webGL viewer
  1275.        
  1276.         debug.log("saving the game at buildzone "..kew.buildzone.locid.." online...")
  1277.         --writeToDatabase(kew.filename,filecontent,saved.position.x,saved.position.z)
  1278.         --debug.log("saved")
  1279.     end
  1280. end
  1281.  
  1282. -- this function writes to the online database that we use to display models in the webGL viewer
  1283. local function writeToDatabase(name,data,x,z)
  1284.    
  1285.     -- the user agent needs to be renamed otherwise the dfeult one is Java and that is blocked by the .htaccess file on the website
  1286.     local headers = {
  1287.         [ "User-Agent" ] = "20.000 BLOCKS"
  1288.     }
  1289.    
  1290.     local link = http.post(
  1291.         "http://www.20000blocks.com/DatabaseAccess/UploadModel.php",
  1292.         "name="..texturlEncode(name).."&"..
  1293.         "content="..texturlEncode(data).."&"..
  1294.         "x="..x.."&"..
  1295.         "z="..z,
  1296.         headers
  1297.     )
  1298.     local linkURL = link.readAll()
  1299.     if linkURL then
  1300.         --message texts
  1301.         local msg_EN = 'The latest game result has been uploaded to the webviewer.\n'
  1302.         local msg_DE = 'Die neueste Runde wurde in den Webviewer geladen.\n'
  1303.         local linkText_EN = 'See it and share it!'
  1304.         local linkText_DE = 'Schau es und teile es!'
  1305.         local hoverText_EN = 'Click here to see and share the last game!'
  1306.         local hoverText_DE = 'Click here to see and share the last game!'
  1307.         --message text with the link
  1308.         local linkmsg_EN = '["",{"text":"'..msg_EN..'","color":"white","bold":false},{"text":"'..linkText_EN..'","color":"blue","underlined":true,"clickEvent":{"action":"open_url","value":"'..linkURL..'"},"hoverEvent":{"action":"show_text","value":{"text":"","extra":[{"text":"'..hoverText_EN..'","color":"gold"}]}},"bold":false}]'
  1309.        
  1310.         local linkmsg_DE = '["",{"text":"'..msg_DE..'","color":"gold","bold":false},{"text":"'..linkText_DE..'","color":"blue","underlined":true,"clickEvent":{"action":"open_url","value":"'..linkURL..'"},"hoverEvent":{"action":"show_text","value":{"text":"","extra":[{"text":"'..hoverText_DE..'","color":"gold"}]}},"bold":false}]'
  1311.         -- announce success in English
  1312.         commands.async.tellraw("@a",linkmsg_EN)
  1313.         if registry.ANNOUNCE_GERMAN then
  1314.             --announce success in German
  1315.             commands.async.tellraw("@a",linkmsg_DE)
  1316.         end
  1317.     end
  1318. end
  1319.  
  1320. function switchVersion(id, relative) --id is the new version, relative is whether it is incrmenting the last version (true means the number in id will be added to the current version, false is id is the new version number)
  1321.     http.post(
  1322.         "http://www.20000blocks.com/DatabaseAccess/SwitchVersion.php",
  1323.         "id="..texturlEncode(id).."&"..
  1324.         "relative="..texturlEncode(relative)
  1325.     )
  1326. end
  1327.  
  1328.  
  1329.  
  1330. --- Move from the Wait Phase to the Play Phase
  1331. -- Anything that needs to be done ONCE before the Play Phase starts
  1332. -- happens here.
  1333. -- The buildzone is moved to a clean area.
  1334. -- The players in the Kew are moved to the buildzone.
  1335. -- The buildzone is cleaned up (now that the chunks are loaded
  1336. -- The buildzone is prepares (detector blocks placed)
  1337. -- Players recieve starting items
  1338. -- A friendly message is shown for them to begin
  1339. -- Kew Phase and Timer are updated
  1340. -- @param kew The Kew to update
  1341. function endWaitPhase(kew)--, game)
  1342.     --moveBuildzone(kew.buildzone,game.builds)
  1343.     --teleportToZone(kew.buildzone,kew.playerlist,"Your game has started! BUILD A HOUSE!", "Das Spiel hat begonnen! BAUE EIN HAUS!")--teleport selected players
  1344.     debug.log("ending the wait phase for buildzone "..kew.buildzone.locid)
  1345.     cleanBuildzone(kew.buildzone)
  1346.     prepareBuildzone(kew.buildzone)--prepare build zone
  1347.     --giveItems(kew.playerlist,registry.STARTING_ITEMS) --Starting items are given as players join now, not all at once
  1348.     displayTitleToPlayers(kew.playerlist,"BUILD!","Place your resources and stand on a detector block.")
  1349.     kew.victory = false
  1350.     --displayTime(kew.buildzone.selector,0,0)
  1351.     kew.phase = registry.QUEUE_PHASE.PLAYING
  1352.     kew.timer = kew.phases[kew.phase].length
  1353.     portal.drawPortal(kew.portal,kew.phase)
  1354.     debug.log("wait phase for buildzone "..kew.buildzone.locid.. " - ended")
  1355. end
  1356.  
  1357. --- Move from the Play Phase to the End Phase
  1358. -- Anything that needs to be done ONCE before the End Phase starts
  1359. -- happens here.
  1360. -- Player highscores are updated
  1361. -- The Kew and Buildzone data is saved to file
  1362. -- The buildzone is given a clean and checked for value
  1363. -- Kew Phase and Timer are updated
  1364. -- @param kew The Kew to update
  1365. function endPlayPhase(kew)
  1366.     debug.log("ending the play phase for buildzone "..kew.buildzone.locid)
  1367.     processHighscores(kew)
  1368.     exportKewData(kew,true) -- saves the final state of the game and writes it to the online database
  1369.     cleanAfterGameOver(kew)
  1370.     kew.phase = registry.QUEUE_PHASE.GAMEOVER
  1371.     --displayTime(kew.buildzone.selector,0,0)
  1372.     kew.timer = kew.phases[kew.phase].length
  1373.     displayTitleToPlayers(kew.playerlist,"Times Up!","Use HOMECOMER to return to spawn")
  1374.     portal.drawPortal(kew.portal,kew.phase)
  1375.     debug.log("play phase for buildzone "..kew.buildzone.locid.. " - ended")
  1376. end
  1377.  
  1378. --- Move from the End Phase to the Wait Phase
  1379. -- Anything that needs to be done ONCE before the End Phase starts
  1380. -- happens here.
  1381. -- Players are removed from the Kews playerlist
  1382. -- Kew Phase and Timer are updated
  1383. -- @param kew The Kew to update
  1384. function endEndPhase(kew)
  1385.     debug.log("ending the final phase for buildzone "..kew.buildzone.locid)
  1386.     removePlayersFromKew(kew)
  1387.     kew.phase = registry.QUEUE_PHASE.DORMANT
  1388.     --displayTime(kew.buildzone.selector,0,0)
  1389.     kew.timer = kew.phases[kew.phase].length
  1390.     portal.drawPortal(kew.portal, kew.phase)
  1391.     debug.log("final phase for buildzone "..kew.buildzone.locid.. " - ended")
  1392. end
  1393.  
  1394. --- Calculate end of Phase and change to next Phase
  1395. -- This code runs ONCE at the end of each phase and what actually
  1396. -- happens is specific to which phase the given kew is in.
  1397. -- Kew timers are filled and Kew Phases are updated here only.
  1398. -- @param game The game object as created in setup()
  1399. -- TODO try using the state pattern (http://gameprogrammingpatterns.com/state.html) and assign the corresponding endPhase function to a pointer and run it from there
  1400. -- another example here http://lua-users.org/wiki/FiniteStateMachine
  1401. function doPhaseEnds(game)
  1402.     for i,kew in ipairs(game.queues) do
  1403.         if kew.timer <= 0 then
  1404.             if kew.phase == registry.QUEUE_PHASE.DORMANT then
  1405.                 --waiting phase ends goto play phase
  1406.                 endWaitPhase(kew) --, game)
  1407.             elseif kew.phase == registry.QUEUE_PHASE.PLAYING then
  1408.                 --playing phase ends goto end phase
  1409.                 endPlayPhase(kew)
  1410.             elseif kew.phase == registry.QUEUE_PHASE.GAMEOVER and countActivePlayers(kew) == 0 then
  1411.                 --end phase ends goto waiting phase
  1412.                 endEndPhase(kew)
  1413.             end
  1414.         end
  1415.     end
  1416. end
  1417.  
  1418.  
  1419. -- Replaces important blocks such as Detectors
  1420. -- Replaces everything that is needed to start the game. Does not
  1421. -- rebuild the floor, or clear anything away. based on the settings it
  1422. -- creates a full grid, or a partial grid, or no grid of Detectors
  1423. -- it also places the ring, although this is disabled for now
  1424. -- @param buildzone The buildzone to prepare
  1425. function prepareBuildzone(buildzone)
  1426.     debug.log("Preparing buildzone "..buildzone.locid.." ...")
  1427.     local bz = buildzone
  1428.     local x,y,z,w = bz.x,bz.y,bz.z,bz.w
  1429.     --debug.log("fillBox from inside prepareBuildzone")
  1430.     --place the white grid accross the full buildzone as a base
  1431.     --commands.async.fill(x,y,z,x+w,y,z+w,registry.BLOCKS.CAMP_FLOOR.block)
  1432.     mcset.fillBox(box(x,y,z,w,1,w),registry.BLOCKS.CAMP_FLOOR)
  1433.     --mcset.fillRing(buildzone.x,buildzone.y-1,buildzone.z,buildzone.w,registry.BLOCKS.CONSTRUCTION.block) --this draws the construction stripe around the buildzone
  1434.     --create the grid of detectors surrounded by plus plugs
  1435.     if registry.DO_GRID then
  1436.         --mcset.fillGrid()
  1437.         local halfCell = math.floor(registry.GRIDCELL_SIZE/2)
  1438.         for x=0,registry.GRIDCELL_COUNT-1 do
  1439.             for z=0,registry.GRIDCELL_COUNT-1 do
  1440.                 local rand = math.random()*100
  1441.                 if rand > registry.GRID_HOLE_CHANCE then --and result then
  1442.                     commands.async.setblock(bz.x+(x*registry.GRIDCELL_SIZE)+halfCell,bz.y+1,bz.z+(z*registry.GRIDCELL_SIZE)+halfCell,registry.BLOCKS.DETECT.block,registry.BLOCKS.DETECT.variant,"replace","minecraft:air")
  1443.                     commands.async.fill(bz.x+(x*registry.GRIDCELL_SIZE)+halfCell-registry.PLUG_LENGTH, bz.y,bz.z+(z*registry.GRIDCELL_SIZE)+halfCell,bz.x+(x*registry.GRIDCELL_SIZE)+halfCell+registry.PLUG_LENGTH,bz.y,bz.z+(z*registry.GRIDCELL_SIZE)+halfCell,registry.BLOCKS.PLUG.block)
  1444.                     commands.async.fill(bz.x+(x*registry.GRIDCELL_SIZE)+halfCell,bz.y,bz.z+(z*registry.GRIDCELL_SIZE)+halfCell-registry.PLUG_LENGTH,bz.x+(x*registry.GRIDCELL_SIZE)+halfCell,bz.y,bz.z+(z*registry.GRIDCELL_SIZE)+halfCell+registry.PLUG_LENGTH,registry.BLOCKS.PLUG.block)
  1445.                 end
  1446.             end
  1447.         end
  1448.     end
  1449.     --mark the game in the LOCS array as not available anymore, and save the updated game grid to the grid file
  1450.     --change the flag to played=true
  1451.     --print("prepareBuildzone", buildzone.locid)
  1452.     updateZonePlayedState(buildzone,true)
  1453.     debug.log("Buildzone "..buildzone.locid.." is prepared.")
  1454. end
  1455.  
  1456. -- Removes everything in a buildzone
  1457. -- Literally replaces every block inside the buildzone with air and
  1458. -- then removes all floating items.
  1459. -- @param buildzone A buildzone to clean
  1460. function cleanBuildzone(buildzone)
  1461.     debug.log("Cleaning buildzone "..buildzone.locid.." ...")
  1462.     --for each level, remove all blocks
  1463.     --debug.log("fillBox from inside cleanBuildzone")
  1464.     mcset.fillBox(box(buildzone.x-1, buildzone.y, buildzone.z-1, buildzone.w+2, 256-buildzone.y, buildzone.w+2), registry.BLOCKS.AIR)
  1465.     --for h=buildzone.y,255 do
  1466.     --  commands.async.fill(buildzone.x,h,buildzone.z,buildzone.x+buildzone.w,h,buildzone.z+buildzone.w,"minecraft:air")
  1467.     --end
  1468.     --remove all floating items in the loaded part of the world
  1469.     commands.async.kill("@e[type=item]")
  1470.     debug.log("Buildzone "..buildzone.locid.." is cleaned.")
  1471. end
  1472.  
  1473. --- Takes players out of a Kews playerlist
  1474. -- Gives all players a message telling them to return to spawn and
  1475. -- then removes them from the playerlist. This releases them from being
  1476. -- trapped in the buildzone boundary.
  1477. -- @param kew The Kew to clear players from
  1478. function removePlayersFromKew(kew)
  1479.     debug.log("removing all players from the list for buildzone "..kew.buildzone.locid.." ...")
  1480.     for name, player in pairs(kew.playerlist) do
  1481.         if registry.ANNOUNCE_ENGLISH then
  1482.             -- announce success in English
  1483.             commands.async.tellraw(player.name,'["",{"text":"TIME OUT! GAME COMPLETE! Use your HOMECOMER to return to spawn.","color":"white"}]')
  1484.         end
  1485.         if registry.ANNOUNCE_GERMAN then
  1486.             -- announce success in German
  1487.             commands.async.tellraw(player.name,'["",{"text":"ENDE! SPIEL ABGESCHLOSSEN! Nutze den HEIMKEHRER um zum Anfang zurück zu kehren.","color":"gold"}]')
  1488.         end
  1489.     end
  1490.     kew.playerlist = {}
  1491.     debug.log("Player list for buildzone "..kew.buildzone.locid.." is now empty")  
  1492. end
  1493.  
  1494. --- Resets a player to be harmless
  1495. -- Use this when you want to be sure a player cannot modify blocks
  1496. -- and that they have no wool to place. You can also send them a friendly
  1497. -- message about why you took their stuff!
  1498. -- @param playername String of the players name
  1499. -- @param message String A friendly message for the given player
  1500. function resetPlayer(playername,message)
  1501.     debug.log("resetPlayer: reseting player '"..playername.."' to adventure mdoe with no wool and pickaxe")
  1502.     commands.tell(playername,message)
  1503.     commands.async.gamemode(2,playername)
  1504.     commands.async.clear(playername,"minecraft:wool")
  1505.     commands.async.clear(playername,"minecraft:stone_pickaxe")
  1506. end
  1507.  
  1508. --- Checks if given player is in a buildzone
  1509. -- Uses a playerdata object and the buildzone selector
  1510. -- @param player A playerdata object from newPlayerData()
  1511. -- @param buildzone A buildzone object to get the selector from
  1512. -- @return result True if the player is in the buildzone
  1513. function checkForPlayerInBuildzone(player,buildzone)
  1514.     local result,message = commands.testfor('@a[name='..player.name..','..buildzone.selector..']')
  1515.     return result
  1516. end
  1517.  
  1518. --[[
  1519. --- Checks if a given player is in a given waiting area
  1520. -- Builds a selector for the portal from the given portal
  1521. -- @param player A playerdata object from newPlayerData()
  1522. -- @param portal A portal from a Kew object
  1523. -- @return result True if the player is in the wait area
  1524. function checkForPlayerInPortal(player,portal) -- TODO see if this function is needed, currently not used anywhere
  1525.     --local selector = "x="..portal.zone.corner_x..",y="..portal.zone.corner_y..",z="..portal.zone.corner_z..",dx="..portal.zone.corner_..",dy=1,dz="..registry.PORTAL.SIZE
  1526.     local selector = box_base(portal.zone).selector --gets a selector for only the bottom layer of blocks, that way when we fill that layer with blocks we 'close' the portal
  1527.     local result,message = commands.testfor('@a[name='..player.name..','..selector..']')
  1528.     return result
  1529. end
  1530.  
  1531. --- Checks if a given player is in a given area
  1532. -- Builds a selector for the area from the given area parameters
  1533. -- @param player A playerdata object from newPlayerData()
  1534. -- @param area Specified as a table with x,y,z,w,l,h fields
  1535. -- @return result True if the player is in the wait area
  1536. function checkForPlayerInArea(player,area) -- TODO see if this function is needed, currently not used anywhere
  1537.    
  1538.     local selector = "x="..area.x..",y="..area.y..",z="..area.z..",dx="..area.w..",dy="..area.h..",dz="..area.l
  1539.     --debug.log("checking for player '"..player.name.."' in area: "..selector)
  1540.     local result,message = commands.testfor('@a[name='..player.name..','..selector..']')
  1541.     return result
  1542. end
  1543. --]]
  1544.  
  1545. --- Gets a list of all players in a given Portal
  1546. -- Builds a selector for the portal from the given portal
  1547. -- @param portal A portal from a Kew object
  1548. -- @return result A list of playerdata objects from GetAllPos()
  1549. function checkPortal(portal)
  1550.     --local selector = "x="..portal.x..",y="..portal.y..",z="..portal.z..",dx="..registry.PORTAL.SIZE..",dy=1,dz="..registry.PORTAL.SIZE
  1551.     local selector = box_base(box_remove_base(portal.zone)).selector --gets a selector for only the second layer of blocks, that way when we fill that layer with blocks we 'close' the portal
  1552.     debug.log("checkPortal(): checking portal {"..selector.."}")
  1553.     local result = getAllPos('m=2,'..selector)
  1554.     debug.log("result is:"..json.encode(result))
  1555.     return result
  1556. end
  1557.  
  1558.  
  1559. --- Updates our information on all players we think are in the game
  1560. -- Checks the waitlist and all kews. Each player is checked if they are
  1561. -- still online and if they are playing in a buildzone.
  1562. -- @param game A game object as created by setup()
  1563. function checkPlayers(game)
  1564.     --local selector = "x="..registry.WAITZONE.x..",y="..(registry.WAITZONE.y-2)..",z="..registry.WAITZONE.z..",dx="..registry.WAITZONE.w..",dy=256,dz="..registry.WAITZONE.l
  1565.     --local loggedIn = getAllPos('m=2,'..selector)
  1566.     --get all players in adventure mode and sort them around
  1567.     local loggedIn = getAllPos('m=2')
  1568.     number_of_players_adventure = tableLength(loggedIn)
  1569.     --number_of_players_creative = tableLength(getAllPos('m=1')) --disabled for now until we figure how to deal with the glitches
  1570.     --refresh waitlist
  1571.     --game.waitlist = loggedIn --the .waitlist property is not used anymore
  1572.     --check currently playing players
  1573.     for l,kew in ipairs(game.queues) do
  1574.         for name,builder in pairs(kew.playerlist) do
  1575.             local isPlaying = checkForPlayerInBuildzone(builder,kew.buildzone)
  1576.             --remove players who are already in kews from the waitlist
  1577.             for j, player in ipairs(loggedIn) do
  1578.                 if player.name == builder.name then
  1579.                     --table.remove(loggedIn,j) --doing it this way created problems as the iterator skips an element after removal
  1580.                     loggedIn[j] = nil --doing it this way is safe
  1581.                 end
  1582.             end
  1583.             --if the game is in progress and the player is not found then remove them from the gamekew
  1584.             if not isPlaying and kew.phase == registry.QUEUE_PHASE.PLAYING then
  1585.                 --table.remove(kew.playerlist,i)
  1586.                 kew.playerlist[builder.name].status = registry.PLAYER_STATUS.LEFT_OTHERWISE
  1587.                 --print("Removed "..builder.name.." from game in progress")
  1588.             end
  1589.         end
  1590.     end
  1591.     --check if players are in the spawn area
  1592.     --debug.log("the loggedIn table is:"..json.encode(loggedIn))
  1593.     for j, player in ipairs(loggedIn) do
  1594.         local isInSpawnArea = checkForPlayerInArea(player, registry.MAIN_SPAWN_AREA)
  1595.         if isInSpawnArea then
  1596.             loggedIn[j] = nil
  1597.         end
  1598.     end
  1599.     --if there are still players in loggedIn then they must be force moved to spawn
  1600.     if tableLength(loggedIn) > 0 then
  1601.         debug.log("teleporting free roaming players back to spawn area: "..playerListToString(loggedIn))
  1602.         teleportToPoint(registry.SPAWN.x,registry.SPAWN.y,registry.SPAWN.z,loggedIn,true,"You have been teleported to the spawn area. Use the portals to join a game", "(Translate to DE)You have been teleported to the spawn area. Use the portals to join a game")
  1603.     end
  1604. end
  1605.  
  1606.  
  1607.  
  1608. function addToGame(kew,waiter)
  1609.     -- check if player already exists in playerlist
  1610.     -- teleport them
  1611.     -- add to playerlist if not
  1612.     -- add starting items if not
  1613.     -- update player status if they exist
  1614.     -- if playerlist is full, fill in the portal
  1615.     local buildzone = kew.buildzone
  1616.     local textEN = "You should never see this text"
  1617.     local textDE = "You should never see this text (in German)"
  1618.     if (#kew.playerlist > 0 and kew.playerlist[waiter.name]) then
  1619.         -- player has been here before so just change status to 0 and tp them
  1620.         --print("player was here before")
  1621.         kew.playerlist[waiter.name].status = registry.PLAYER_STATUS.IN_GAME
  1622.         textEN = "Welcome back to the buildzone."
  1623.         textDE = "Welcome back to the buildzone (TRANSLATE)"
  1624.         debug.log("Adding '"..waiter.name.."' as RETURNING player to buildzone #"..buildzone.locid)
  1625.     else
  1626.         --print("player was NOT here before")
  1627.         kew.playerlist[waiter.name] = waiter
  1628.         kew.playerlist[waiter.name].status = registry.PLAYER_STATUS.IN_GAME
  1629.         giveItems({waiter},registry.STARTING_ITEMS)
  1630.         textEN = "Welcome to the buildzone. Build your first structure."
  1631.         textDE = "Welcome to the buildzone. Build your first structure. (TRANSLATE)"
  1632.         debug.log("Adding '"..waiter.name.."' as NEW player to buildzone #"..buildzone.locid)
  1633.     end
  1634.     --print("buildzone", buildzone)
  1635.     --print("waiter", waiter)  
  1636.     --print("textEN", textEN)
  1637.     --print("textDE", textDE)
  1638.     scatterIntoZone(buildzone, {waiter}, textEN, textDE)
  1639.     --teleportToPoint(buildzone.x+2+(buildzone.w/2),buildzone.y+5,buildzone.z+2+(buildzone.w/2),{waiter},false,textEN, textDE)
  1640. end
  1641.  
  1642. function inZoneChangeMode(_box,mode)
  1643.     --debug.log("changing every in wait zone to adventure mode")
  1644.     local selector = _box.selector
  1645.     commands.async.gamemode(mode,"@a["..selector.."]")
  1646. end
  1647.  
  1648. -- Adds waiting players to Kew playerlists
  1649. -- checks which players are in which portals and adds them to games
  1650. -- based on that. If there are any creative mode players in the zone,
  1651. -- change them to adventure mode.
  1652. -- @param game the game object as created by setup()
  1653. function allocateWaiters(game)
  1654.     for i, kew in ipairs(game.queues) do
  1655.         --debug.log("allocateWaiters: the queues for loop start")
  1656.         if (kew.phase == registry.QUEUE_PHASE.DORMANT or kew.phase == registry.QUEUE_PHASE.PLAYING) and countActivePlayers(kew) < kew.maxplayers then
  1657.             --debug.log("allocateWaiters: the if statement start")
  1658.             inZoneChangeMode(kew.portal.zone,2)
  1659.             local waiters = checkPortal(kew.portal)
  1660.             if #waiters > 0 then debug.log("found player in portal") end
  1661.             for index,waiter in ipairs(waiters) do
  1662.                 --debug.log("allocateWaiters: the waiters for loop start")
  1663.                 addToGame(kew,waiter)
  1664.                 --debug.log("allocateWaiters: the waiters for loop end")
  1665.             end
  1666.             --debug.log("allocateWaiters: the if statement end")
  1667.         end
  1668.         --debug.log("allocateWaiters: the queues for loop end")
  1669.     end
  1670.     --debug.log("allocateWaiters: completed")
  1671. end
  1672.  
  1673. --- Adds a new request to check a Detector Block safely
  1674. -- Makes sure that incoming check requests dont exist already
  1675. -- Players can still have multiple requests, but no two requests from
  1676. -- the same tile will exist. Requests are processed at a rate of one per
  1677. -- game step, so its important that we dont have duplicates as it slows
  1678. -- the game loop down a lot.
  1679. -- @param player A playerdata Object as created by newPlayerData().
  1680. -- @param buildzone A buildzone to which the request should be added.
  1681. function addToChecklist(player,buildzone)
  1682.     for _, detector in ipairs(buildzone.waitingForCheck) do
  1683.         if detector.x == player.x and detector.y == player.y and detector.z == player.z then
  1684.             return false
  1685.         end
  1686.     end
  1687.     table.insert(buildzone.waitingForCheck,player)
  1688.     return true
  1689. end
  1690.  
  1691. --- Cleans barriers from a buildzone
  1692. -- removes all barrier blocks from a buildzone which are used to
  1693. -- carve space in Elements.
  1694. -- TODO This should be updated to only clean an area
  1695. -- the size of an element specified with x,y,z.
  1696. -- @param buildzone A buildzone that should be cleaned
  1697. function cleanBarriers(buildzone)
  1698.     --debug.log()
  1699.     for h=0,200 do
  1700.         commands.async.fill(buildzone.x,buildzone.y+h,buildzone.z,buildzone.x+buildzone.w,buildzone.y+h,buildzone.z+buildzone.w,"minecraft:air",0,"replace","minecraft:barrier")
  1701.     end
  1702. end
  1703.  
  1704. --- Update a buildzone that is in the Play Phase
  1705. -- Detection and Game Logic is mostly kept here. Meat and Potatoes time.
  1706. -- Wow, actually most of this is disabled for now.
  1707. -- If there are waiting requests for checking a detector block, do
  1708. -- the first one in the list.
  1709. -- Check it against the catalogue we have stored in the buildzone
  1710. -- (not the complete catalogue). If it matches then clone in the new
  1711. -- Building, give Rewards and clean Barriers.
  1712. -- Currently always returns False for victory.
  1713. -- @param kew a Kew object which contains a Buildzone
  1714. -- @return victory Boolean, Did this placement cause a victory?
  1715. function updatePlayedZone(kew)
  1716.     local buildzone = kew.buildzone
  1717.     local victory = false
  1718.     local buildzoneSelector = buildzone.selector
  1719.     --get all players on a detector block, add them to the list of things to check
  1720.     local detectLocations = getAllOnBlockType(registry.BLOCKS['DETECT'].block,buildzoneSelector)
  1721.     --print(#detectLocations.." Players standing on detectors")
  1722.     for _, player in ipairs(detectLocations) do
  1723.         addToChecklist(player,buildzone)
  1724.     end
  1725.  
  1726.     --DEAL WITH THE DETECTOR AT THE TOP OF THE LIST IF THERE IS ONE
  1727.     if #buildzone.waitingForCheck > 0 then
  1728.         --DO PARTICLE EFFECTS IF A DETECTING BLOCK THAT IS DETECTING
  1729.         for i,loc in ipairs(buildzone.waitingForCheck) do
  1730.             mcset.search(loc.x,loc.y+1,loc.z)
  1731.         end
  1732.         local totalResult = false
  1733.         local checked = table.remove(buildzone.waitingForCheck,1)
  1734.         local x,y,z,name = checked.x,checked.y,checked.z,checked.name
  1735.         for i,element in pairs(buildzone.elements) do
  1736.             local result,message = commands.testforblocks( element.keyX, element.keyY, element.keyZ, element.keyX+element.sizeX, element.keyY+registry.ELEMENT_HEIGHT, element.keyZ+element.sizeZ, x-math.floor(element.sizeX/2), y-1, z-math.floor(element.sizeZ/2),"masked")
  1737.             if result then
  1738.                 --clone in the correct vocab
  1739.                 local cloneres,clonemes = commands.clone( element.elementX, element.elementY, element.elementZ, element.elementX+element.sizeX, element.elementY+registry.ELEMENT_HEIGHT, element.elementZ+element.sizeZ, x-math.floor(element.sizeX/2), y-1, z-math.floor(element.sizeZ/2),"masked")
  1740.                 --debug.log(clonemes[1])
  1741.                 --commands.async.give(name,element.reward)
  1742.                 debug.log("successful detect of element #"..i.." in buildzone #"..buildzone.locid.." at:"..x..", "..y..", "..z)
  1743.                 -- announce  success in English
  1744.                 --[[
  1745.                 local rewardType = 'nature'
  1746.                 local rewardTypeDE = 'grüne'
  1747.                 if element.rewardUrban then
  1748.                     rewardType = 'urban'
  1749.                     rewardTypeDE = 'urbane'
  1750.                 end
  1751.                 --]]
  1752.                 if registry.ANNOUNCE_ENGLISH then
  1753.                     --announce success in English
  1754.                     --commands.async.tellraw(name,'["",{"text":"You built a '..element.nameEN.. ' ('..element.typeEN..'), which is '..element.height..'m tall and gives '..element.slots..' density pts, '..element.greenSlots..' green pts and '..element.rewardCount..'x '..rewardType..' resource!","color":"white"}]')
  1755.                     commands.async.tellraw(name,'["",{"text":"You built an element!","color":"white"}]')
  1756.                 end
  1757.                 if registry.ANNOUNCE_GERMAN then
  1758.                     -- announce  success in German
  1759.                     --commands.async.tellraw(name,'["",{"text":"Du hast ein '..element.typeDE..' | '..element.nameDE.. ' gebaut, das '..element.height..' Meter hoch ist und dir '..element.slots..' Punkte für die Dichte einbringt, jedoch '..element.greenSlots..' Punkte für Grünflächen und '..element.rewardCount..' '..rewardTypeDE..' Ressourcen!","color":"gold"}]')
  1760.                     commands.async.tellraw(name,'["",{"text":"Du hast ein element gebaut!","color":"gold"}]')
  1761.                 end
  1762.                
  1763.                 --clear out barrier blocks
  1764.                 cleanBarriers(buildzone)
  1765.                
  1766.                 --ADD THE NEW STRUCTURE TO THE RECORDS
  1767.                 --table.insert(buildzone.structures,element.nameEN)
  1768.                 --record the place of the element as x, y, z and element id
  1769.                 local building = {id=element.id,xpos=x,ypos=y-1,zpos=z,name="element_"..element.id,time=os.clock(),player=name}
  1770.                 table.insert(buildzone.buildings, building)
  1771.                
  1772.                 --save the game incrementally
  1773.                 exportKewData(kew,false) -- saves the new state of the game locally to a file, doesn't write to the database yet
  1774.                
  1775.                 --[[ DISABLED UNTIL THE NEW GOAL SYSTEM IS IMPLEMENTED
  1776.                
  1777.                 buildzone.greenSlots = buildzone.greenSlots + element.greenSlots
  1778.                
  1779.                 buildzone.filledSlots = buildzone.filledSlots + element.slots
  1780.                 local newHeight = y + element.height - buildzone.y-1 -- the Y coordinate of the highest block above the ground. Our world has its ground at 55 which is in buildzone.y, subtracting 1 to compensate for player height
  1781.                 if newHeight > buildzone.highest then
  1782.                     buildzone.highest = newHeight
  1783.                 end
  1784.                
  1785.                 -- count variety               
  1786.                 local varietyId = element.id -- we use a variety id instead of the element id because vocab id 1,2,3,4 for example are all houses so it is the same variety
  1787.                 --we add only one type of house, one type of green house, one type of garden and one type of extension and we skip the two risers
  1788.                 --]]
  1789.                 --[[
  1790.                 if vocab.id == 2 or vocab.id == 3 or vocab.id == 4 then varietyId = 1 end-- only one type for the 4 different orientations of the house
  1791.                 if vocab.id == 14 or vocab.id == 15 or vocab.id == 16 then varietyId = 13 end-- only one type for the 4 different orientations of the house extension
  1792.                 if vocab.id == 26 or vocab.id == 27 or vocab.id == 28 then varietyId = 25 end-- only one type for the 4 different orientations of the house garden extension
  1793.                 if vocab.id == 38 or vocab.id == 39 or vocab.id == 40 then varietyId = 37 end-- only one type for the 4 different orientations of the green roof house
  1794.                 if varietyId ~= 17 and varietyId ~= 18 then --skip the two riser as they are not buildings
  1795.                 --]]
  1796.                 --[[
  1797.                 if buildzone.variety[varietyId] then
  1798.                     --print("increasing existing item")
  1799.                     buildzone.variety[varietyId] = buildzone.variety[varietyId] + 1
  1800.                 else
  1801.                     --print("adding new item")
  1802.                     buildzone.variety[varietyId] = 1
  1803.                 end
  1804.                 --end
  1805.                
  1806.            
  1807.                
  1808.                
  1809.                 --- CHECK FOR PERSONAL RECORDS
  1810.                 --- check if the new structure is the highest
  1811.                 --- CHANGE here to live detect the contribution of the new structure to the 4 goals and update them
  1812.                
  1813.                 local personalbest = tracker.getScore(name,"highest")
  1814.                 if personalbest.count < newHeight then
  1815.                     --commands.async.tell(name,"You just topped your personal record for highest structure!")
  1816.                     commands.async.scoreboard("players","add",name,"highest",1)
  1817.                     if registry.ANNOUNCE_ENGLISH then
  1818.                         -- announce success in English
  1819.                         commands.async.tellraw(name,'["",{"text":"You just topped your personal record for highest neighbourhood!","color":"green"}]')
  1820.                     end
  1821.                     if registry.ANNOUNCE_GERMAN then
  1822.                         -- announce success in German
  1823.                         commands.async.tellraw(name,'["",{"text":"Du hast soeben deinen persönlichen Rekord für die höchste Nachbarschaft gebrochen!","color":"gold"}]')
  1824.                     end
  1825.                 end
  1826.            
  1827.                 ---
  1828.                 ---
  1829.                 -- CHECK if placing the current structure would result in beating a server-wide record on the 4 goals
  1830.                 --calculate total slots - FOR GOAL "DENSEST NEIGHBOURHOOD"
  1831.                 local most = tracker.getScore("Densest_[points]","highscores")
  1832.                 local Kint = math.floor(100 * buildzone.filledSlots / 49) -- Kint is the density index made from built area (filledSlots) over ground area (7x7 slots = 49)
  1833.                 if Kint > most.count then
  1834.                     commands.async.scoreboard("players","set","Densest_[points]","highscores",Kint)
  1835.                     if registry.ANNOUNCE_ENGLISH then
  1836.                         -- announce success in English
  1837.                         commands.async.tellraw("@a",'["",{"text":"Great! '..name.. ' just topped the record for the DENSEST NEIGHBOURHOOD!","color":"green"}]')
  1838.                     end
  1839.                     if registry.ANNOUNCE_GERMAN then
  1840.                         -- announce success in German
  1841.                         commands.async.tellraw("@a",'["",{"text":"Sehr gut! '..name.. ' hat einen neuen Rekord für die DICHTESTE NACHBARSCHAFT aufgestellt!","color":"gold"}]')
  1842.                     end                
  1843.                 end
  1844.                
  1845.                 -- FOR THE GOAL "MOST DIVERSE NEIGHBOURHOOD"
  1846.                 -- here we need to count how many varieties of buildings there are
  1847.                 --local structures = tracker.tallyTable(buildzone.structures) -- this counts the variety of buildings in a game
  1848.                 local mostDiverse = tracker.getScore("Most-Diverse_[out-of-26]","highscores")
  1849.                 local typeCount = tableLength(buildzone.variety)
  1850.                 --print("variety count is: "..typeCount)
  1851.                 if typeCount > mostDiverse.count then
  1852.                     commands.async.scoreboard("players","set","Most-Diverse_[out-of-26]","highscores", typeCount)
  1853.                     if registry.ANNOUNCE_ENGLISH then
  1854.                         -- announce success in English
  1855.                         commands.async.tellraw("@a",'["",{"text":"Wow! '..name.. ' just topped the record for the MOST DIVERSE NEIGHBOURHOOD!","color":"green"}]')
  1856.                     end
  1857.                     if registry.ANNOUNCE_GERMAN then
  1858.                         -- announce success in German
  1859.                         commands.async.tellraw("@a",'["",{"text":"Wow! '..name.. ' hat soeben einen neuen Rekord für die VIELSEITIGSTE NACHBARSCHAFT aufgestellt!","color":"gold"}]')
  1860.                     end
  1861.                 end
  1862.    
  1863.                 -- FOR THE GOAL "GREENEST NEIGHBOURHOOD"
  1864.                 -- here we need to count the number of green vocabs
  1865.                 local greenest = tracker.getScore("Greenest_[points]","highscores")
  1866.                 local Gint = math.floor(100*buildzone.greenSlots/49) --Gint is the green index, made from green area (greenSlots) over ground area (7x7 slots = 49)
  1867.                 if Gint > greenest.count then
  1868.                     commands.async.scoreboard("players","set","Greenest_[points]","highscores",Gint)
  1869.                     if registry.ANNOUNCE_ENGLISH then
  1870.                         -- announce success in English
  1871.                         commands.async.tellraw("@a",'["",{"text":"Awesome! '..name.. ' just topped the record for the GREENEST NEIGHBOURHOOD!","color":"green"}]')
  1872.                     end
  1873.                     if registry.ANNOUNCE_GERMAN then
  1874.                         -- announce success in German
  1875.                         commands.async.tellraw("@a",'["",{"text":"Klasse! '..name.. ' hat einen neuen Rekord für die GRÜNSTE NACHBARSCHAFT aufgestellt!","color":"gold"}]')
  1876.                     end
  1877.                 end
  1878.    
  1879.                 --calculate highest placement -- FOR THE GOAL "TALLEST NEIGHBOURHOOD"
  1880.                 local highest = tracker.getScore("Tallest_[meters]","highscores")
  1881.                 if buildzone.highest > highest.count then
  1882.                     commands.async.scoreboard("players","set","Tallest_[meters]","highscores",buildzone.highest)
  1883.                     if registry.ANNOUNCE_ENGLISH then
  1884.                         -- announce success in English
  1885.                         commands.async.tellraw("@a",'["",{"text":"Incredible! '..name..' just topped the record for TALLEST NEIGHBOURHOOD!","color":"green"}]')
  1886.                     end
  1887.                     if registry.ANNOUNCE_GERMAN then
  1888.                         -- announce success in German
  1889.                         commands.async.tellraw("@a",'["",{"text":"Unglaublich! '..name..' hat einen neuen Rekord für die HÖCHSTE NACHBARSCHAFT aufgestellt!","color":"gold"}]')
  1890.                     end
  1891.                 end
  1892.                
  1893.                
  1894.                 --increase the "how many structures did i build" score for the building player
  1895.                 commands.async.scoreboard("players","add",name,"built",1)
  1896.                 commands.async.scoreboard("players","add",name,"building_"..element.id,1)
  1897.                 --]]
  1898.                 totalResult = true
  1899.                 break
  1900.                
  1901.             end
  1902.         end
  1903.         if totalResult then
  1904.             --yey win, do a happy time
  1905.             mcset.successParticle(x,y,z)
  1906.            
  1907.         else
  1908.             --no vocab found so do a fail particle
  1909.             --announce in English
  1910.             commands.async.tellraw(name,'["",{"text":"The shape you have built does not match any shape in the catalogue, try a different one.","color":"red"}]')
  1911.             if registry.ANNOUNCE_GERMAN then
  1912.                 -- announce in German
  1913.                 commands.async.tellraw(name,'["",{"text":"Die Kombination, die du gebaut hast passt leider zu keinem Gebäude, versuche es mit einer anderen Form.","color":"gold"}]')
  1914.             end
  1915.             mcset.failParticle(x,y-1,z)
  1916.             debug.log("The key that player activated is not in the catalogue.")
  1917.         end
  1918.     end
  1919.     return victory
  1920. end
  1921.  
  1922.  
  1923.  
  1924. -- the main function
  1925. function MAIN()
  1926.    
  1927.     debug.log("Starting 20.000 BLOCKS")
  1928.     debug.log("Version: "..VERSION_NUMBER.." - "..VERSION_NAME)
  1929.     debug.log(_G._VERSION) -- log the Lua version number
  1930.    
  1931.     --LOCS = buildGrid(registry.GAME_FIELD.countX, registry.GAME_FIELD.countZ)
  1932.    
  1933.     local game = setup()
  1934.    
  1935.     if not game then
  1936.         return
  1937.     end
  1938.    
  1939.     --print("20.000 BLOCKS is running...")
  1940.     --local skip = false
  1941.     --if not skip then
  1942.     debug.log("20.000 BLOCKS is running...")
  1943.     while true do
  1944.         --debug.log("beginning of main game loop")
  1945.         debug.printState(game, registry.DEBUG_MODE)
  1946.         --debug.log("begin update(game)")
  1947.         --debug.log(to_string(game))
  1948.         update(game)
  1949.         --debug.log("end update(game)")    
  1950.         --debug.log(to_string(game))       
  1951.         --make always nice weather
  1952.         --commands.async.weather("clear",10000)
  1953.        
  1954.         --if Q is held then exit the game loop
  1955.         local event, key, isHeld = os.pullEvent("key")
  1956.         if key == keys.q and isHeld then break end
  1957.        
  1958.         -- this needs to be quite high to avoid players experiencing glitches
  1959.         -- where they are returned back to their last location from few seconds ago
  1960.         -- if they move fast. this happens becuase we use the teleport command
  1961.         -- to get player locations by teleporting them to their current location
  1962.         os.sleep(2)
  1963.         --
  1964.         --debug.log("end of main game loop")
  1965.     end
  1966.     --end
  1967.     -- do the things we want to do on exit
  1968.     debug.log("Quiting 20.000 BLOCKS...")
  1969.     loader.unloadPackages()
  1970. end
  1971.  
  1972. -- process command line parameters
  1973. local tArgs = { ... } -- get the command line arguments
  1974. if #tArgs == 1 then
  1975.     cleanbuildzone = tArgs[1]
  1976. elseif #tArgs > 1 then
  1977.     print("Usage: play <true(cleans buildzone)/false(default, doesnt clean)>")
  1978.     return
  1979. end
  1980. -- run the game
  1981. MAIN()
Add Comment
Please, Sign In to add comment