local VERSION = '1.0.1 - record breaking message are now green' os.loadAPI('tracker') os.loadAPI('json') print("Starting 20.000 Blocks") DEBUG_MODE = false local monitor = peripheral.wrap("right") local ox,oy,oz = commands.getBlockPosition() CITY_VERSION = 1 -- this denotes which iteration of the map is current. When the map is full, i.e. all buildzones have been played we replace it with an empty one and increase the city vesrion with 1 SPAWN_VILLAGER = false DO_GRID = true GRID_HOLE_CHANCE = 0 NUMBER_OF_BUILDZONES = 6 NUMBER_OF_VOCAB = 10 GAME_LENGTH = 600 VOCAB_WIDTH = 27 VOCAB_HEIGHT = 19 BUILDZONE_WIDTH = 62 -- the actual zone width is 63 blocks, but for some reason due to how minecraft handles relative coordinates this has to be 62 BUILDZONE_FLOOR_HEIGHT = -1 HOMECOMER_TYPE = "Pig" HOMECOMER_VALUE = 90 --90 is for pig SPAWN = { x=12110, y=57, z=-1777, a1=90, a2=0 } FIRSTZONE= { x=11685, y=57, z=-1869 } WAITZONE = { x=12093, y=56, z=-1779, w=5, l=5 } --this is the block at ~-4 ~ ~-4 from the corner of the shape zone --i.e., (the one with the grammar shape in it) of the vocab that is closest to zero FIRSTVOCAB= { x=0, y=57, z=5 } VICTORY_HEIGHT = 30 VICTORY_TOTAL = 50 VICTORY_SPECIFIC = {name='garden',count=101} WOOL_PER_EMERALD = 64 BLOCKS = { CAMP_FLOOR = {block='minecraft:sandstone',data=0}, CONSTRUCTION = {block='minecraft:diamond_ore',data=0}, RING = {block='minecraft:diamond_ore',data=0}, DETECT = {block='minecraft:red_sandstone',data=0}, DETECT_DEAD = {block='minecraft:obsidian',data=0}, VOCAB_DETECT = {block="minecraft:quartz_block",data=0}, VOCAB_REPLACE = {block="minecraft:coal_block",data=0}, VICTORY_MARKER = {block="minecraft:emerald_block",data=0}, PLUG = {block="minecraft:lapis_ore",data=0}, PHVFLOOR = {block="minecraft:stone_slab",data=3}, BUILDING_HOUSE = {block="minecraft:wool",data=0}, BUILDING_GARDEN = {block="minecraft:wool",data=13}, BUILDING_WATER = {block="minecraft:wool",data=1}, _matWhiteSmooth ={block="mincreaft:stained_hardened_clay",data=0}, _matWhiteSmooth2 = {block="minecraft:brick_block",data=0}, _matYellowSmooth = {block="minecraft:stained_hardened_clay",data=4}, _matPinkSmooth = {block="minecraft:stained_hardened_clay",data=6}, _matBlueSmooth = {block="minecraft:stained_hardened_clay",data=9}, _matPurpleSmooth = {block="minecraft:stained_hardened_clay",data=10}, _matWhiteTextured = {block="minecraft:planks",data=4}, _matYellowTextured = {block="minecraft:planks",data=1}, _matPinkTextured = {block="minecraft:planks",data=0}, _matBlueTextured = {block="minecraft:planks",data=2}, _matPurpleTextured = {block="minecraft:planks",data=5}, } --contains a list of all posible Buildzones (build a bit later) LOCS = {} if DEBUG_MODE then GAME_LENGTH = 12000 NUMBER_OF_BUILDZONES = 1 end -- see if the file exists function file_exists(file) local f = fs.open(file, "rb") if f then f:close() end return f ~= nil end --creates a grid of absolute references (0,0) (1,0) function buildGrid(w,h) local grid = readGridFromFile() if grid then -- grid was read from teh file successfully else --generate file from scratch grid = {} for z=0,h-1 do for x=0,w-1 do table.insert(grid,{x=x,z=z,played=false}) end end end return grid end function readGridFromFile() local result = nil fs.makeDir("/records") local filename = "/records/_buildzones-list.yaml" if file_exists(filename) then ---read from file result = json.decodeFromFile(filename) end return result end function writeGridToFile() fs.makeDir("/records") local filename = "/records/_buildzones-list.yaml" local file = fs.open(filename,"w") file.write(json.encodePretty(LOCS)) file.close() end --sets where and how big the PHV area is --second number is along the long edge of PHV starting at the spawn side --first number is along the short edge of PHV starting from the park side if DEBUG_MODE then FIRSTZONE= { x=5, y=57, z=-7 } LOCS = buildGrid(1,3) else LOCS = buildGrid(11,27) end --call these to return a proper minecraft item string including adventure mode properties function houseBlock(quant) text = BLOCKS.BUILDING_HOUSE.block..' '..quant..' '..BLOCKS.BUILDING_HOUSE.data..' {display:{Name:"Urban Resource",Lore:[Place this against a Detector to build denser, more urban buildings]},CanPlaceOn:["'..BLOCKS.BUILDING_HOUSE.block..'","'..BLOCKS.PLUG.block..'","'..BLOCKS.DETECT.block..'"]}' return text end function gardenBlock(quant) text = BLOCKS.BUILDING_GARDEN.block..' '..quant..' '..BLOCKS.BUILDING_GARDEN.data..' {display:{Name:"Nature Resource",Lore:[Place this against a Detector to build greener, more open buildings]},CanPlaceOn:["'..BLOCKS.BUILDING_HOUSE.block..'","'..BLOCKS.PLUG.block..'","'..BLOCKS.DETECT.block..'"]}' return text end PICKAXE_USES = 40 pickaxe = 'stone_pickaxe 1 '..131-PICKAXE_USES..' {CanDestroy:["'..BLOCKS.BUILDING_HOUSE.block..'"],display:{Name:"Resource Remover",Lore:[Use the pickaxe to remove up to '..PICKAXE_USES..' resource blocks]}}' STARTING_ITEMS = { houseBlock(30), gardenBlock(30),pickaxe } DEFAULT_NAME = 'BUILDING' --[[ VOCAB_NAMES = { 'freestand-house', 'freestand-house', 'freestand-house', 'freestand-house', 'dbl-ext-house', 'dbl-ext-house', 'dbl-ext-house', 'dbl-ext-house', 'bridge-house', 'bridge-house', 'bridge-garden', 'bridge-garden', 'ext-house', 'ext-house', 'ext-house', 'ext-house', 'riser-1', 'riser-2', 'city-garden', 'city-plaza', 'dbl-ext-garden', 'dbl-ext-garden', 'dbl-ext-garden', 'dbl-ext-garden', 'ext-garden', 'ext-garden', 'ext-garden', 'ext-garden', 'corner-garden', 'corner-garden', 'corner-garden', 'corner-garden', 'corner-house', 'corner-house', 'corner-house', 'corner-house', 'freestand-garden', 'freestand-garden', 'freestand-garden', 'freestand-garden' } --]] ---------------------------------------------------------------------------------------------------------------- -- the data structure below represents the catalogue of vocabularies as built in minecraft -- id - is unique for each vocab, starting from 1 going to 40 currently -- column and row - represent the "physical" location of the vocab model in the catalogue in the minecraft world -- nameEN and name DE - are names for english and german. for now english names must not have spaces, use dashes (-) instead -- typeEN and typeDE - are the type names, also in english and german such as tech&science spaces -- height - is how many blocks is this structure tall -- slots - is how many potential detector stones is this structure occupying as a building, ex. 2 for houses, 3 for corner extenstions, 0 for plazas -- green - is true or false and is used for easier calculation of the GREENEST GOAL - we dont use this field now and can be removed -- greenSlots - is how many slots are green and is used for easier calculation of the GREENEST GOAL VOCABS_DATA = { -- urban houses {id=1, column=1, row=1,nameEN="SINGLE-FAMILY-HOUSE",nameDE="EINFAMILIENHAUS", typeEN="HOUSES",typeDE="HÄUSER",height=10, slots=2, green=false, greenSlots=0, rewardUrban=false, rewardCount=4}, {id=2, column=1, row=2,nameEN="SINGLE-FAMILY-HOUSE",nameDE="EINFAMILIENHAUS", typeEN="HOUSES",typeDE="HÄUSER",height=10, slots=2, green=false, greenSlots=0, rewardUrban=false, rewardCount=4}, {id=3, column=1, row=3,nameEN="SINGLE-FAMILY-HOUSE",nameDE="EINFAMILIENHAUS", typeEN="HOUSES",typeDE="HÄUSER",height=10, slots=2, green=false, greenSlots=0, rewardUrban=false, rewardCount=4}, {id=4, column=1, row=4,nameEN="SINGLE-FAMILY-HOUSE",nameDE="EINFAMILIENHAUS", typeEN="HOUSES",typeDE="HÄUSER",height=10, slots=2, green=false, greenSlots=0, rewardUrban=false, rewardCount=4}, -- urban businesses {id=5, column=2, row=1,nameEN="GYM",nameDE="FITNESSSTUDIO", typeEN="BUSINESSES",typeDE="GESCHÄFTE",height=20, slots=5, green=false, greenSlots=0, rewardUrban=false, rewardCount=8}, {id=6, column=2, row=2,nameEN="OFFICE-BUILDING",nameDE="BÜROGEBÄUDE", typeEN="BUSINESSES",typeDE="GESCHÄFTE",height=20, slots=5, green=false, greenSlots=0, rewardUrban=false, rewardCount=8}, {id=7, column=2, row=3,nameEN="HOTEL",nameDE="HOTEL", typeEN="BUSINESSES",typeDE="GESCHÄFTE",height=20, slots=5, green=false, greenSlots=0, rewardUrban=false, rewardCount=8}, {id=8, column=2, row=4 ,nameEN="BANK",nameDE="BANK", typeEN="BUSINESSES",typeDE="GESCHÄFTE",height=20, slots=5, green=false, greenSlots=0, rewardUrban=false, rewardCount=8}, -- urban tech&science spaces {id=9, column=3, row=1,nameEN="LIBRARY",nameDE="BIBLIOTHEK", typeEN="TECH & SCIENCE SPACES",typeDE="WISSENSCHAFTSGEBÄUDE",height=16, slots=1, green=false, greenSlots=0, rewardUrban=false, rewardCount=10}, {id=10, column=3, row=2,nameEN="FABLAB",nameDE="INNOVATIONSZENTRUM", typeEN="TECH & SCIENCE SPACES",typeDE="WISSENSCHAFTSGEBÄUDE",height=16, slots=1, green=false, greenSlots=0, rewardUrban=false, rewardCount=10}, -- green tech&science spaces {id=11, column=3, row=3,nameEN="AQUAPONIC-GREENHOUSE",nameDE="AQUAPONIK-GEWÄCHSHAUS", typeEN="TECH & SCIENCE SPACES",typeDE="WISSENSCHAFTSGEBÄUDE",height=16, slots=1, green=true, greenSlots=1, rewardUrban=true, rewardCount=10}, {id=12, column=3, row=4,nameEN="SCIENCE-BRIDGE",nameDE="WISSENSCHAFTSBRÜCKE", typeEN="TECH & SCIENCE SPACES",typeDE="WISSENSCHAFTSGEBÄUDE",height=16, slots=1, green=true, greenSlots=1, rewardUrban=true, rewardCount=10}, -- urban house extensions {id=13, column=4, row=1,nameEN="HOUSE-EXTENSION",nameDE="HAUSERWEITERUNG", typeEN="EXTENSIONS",typeDE="ERWEITERUNGEN",height=10, slots=1, green=false, greenSlots=0, rewardUrban=false, rewardCount=6}, {id=14, column=4, row=2,nameEN="HOUSE-EXTENSION",nameDE="HAUSERWEITERUNG", typeEN="EXTENSIONS",typeDE="ERWEITERUNGEN",height=10, slots=1, green=false, greenSlots=0, rewardUrban=false, rewardCount=6}, {id=15, column=4, row=3,nameEN="HOUSE-EXTENSION",nameDE="HAUSERWEITERUNG", typeEN="EXTENSIONS",typeDE="ERWEITERUNGEN",height=10, slots=1, green=false, greenSlots=0, rewardUrban=false, rewardCount=6}, {id=16, column=4, row=4,nameEN="HOUSE-EXTENSION",nameDE="HAUSERWEITERUNG", typeEN="EXTENSIONS",typeDE="ERWEITERUNGEN",height=10, slots=1, green=false, greenSlots=0, rewardUrban=false, rewardCount=6}, --risers {id=17, column=5, row=1,nameEN="DOUBLE-FLOOR-RISER",nameDE="VERSCHIEBUNG-ZWEI-HOCH", typeEN="RISERS",typeDE="VERTIKALE VERSCHIEBUNGEN",height=0, slots=0, green=false, greenSlots=0, rewardUrban=true, rewardCount=8}, {id=18, column=5, row=2,nameEN="SINGLE-FLOOR-RISER",nameDE="VERSCHIEBUNG-EINEN-HOCH", typeEN="RISERS",typeDE="VERTIKALE VERSCHIEBUNGEN",height=0, slots=0, green=false, greenSlots=0, rewardUrban=true, rewardCount=6}, --plazas {id=19, column=5, row=3,nameEN="CITY-PLAZA",nameDE="STÄDTISCHER PLATZ", typeEN="PLAZAS",typeDE="PLÄTZE",height=0, slots=0, green=false, greenSlots=0, rewardUrban=false, rewardCount=2}, {id=20, column=5, row=4,nameEN="PARK-PLAZA",nameDE="GRÜNER PLATZ", typeEN="PLAZAS",typeDE="PLÄTZE",height=0, slots=0, green=true, greenSlots=1, rewardUrban=true, rewardCount=2}, --green businesses {id=21, column=6, row=1,nameEN="GREEN-GYM",nameDE="GRÜNES FITNESSSTUDIO", typeEN="BUSINESSES",typeDE="GESCHÄFTE",height=20, slots=5, green=true, greenSlots=5, rewardUrban=true, rewardCount=8}, {id=22, column=6, row=2,nameEN="GREEN-OFFICE-BUILDING",nameDE="GRÜNES BÜROGEBÄUDE", typeEN="BUSINESSES",typeDE="GESCHÄFTE",height=20, slots=5, green=true, greenSlots=5, rewardUrban=true, rewardCount=8}, {id=23, column=6, row=3,nameEN="GREEN-HOTEL",nameDE="GRÜNES HOTEL", typeEN="BUSINESSES",typeDE="GESCHÄFTE",height=20, slots=5, green=true, greenSlots=5, rewardUrban=true, rewardCount=8}, {id=24, column=6, row=4,nameEN="GREEN-BANK",nameDE="GRÜNES BANK", typeEN="BUSINESSES",typeDE="GESCHÄFTE",height=20, slots=5, green=true, greenSlots=5, rewardUrban=true, rewardCount=8}, ---green private garden extensions {id=25, column=7, row=1,nameEN="HOUSE-GARDEN",nameDE="PRIVATGARTEN", typeEN="EXTENSIONS",typeDE="ERWEITERUNGEN",height=0, slots=0, green=true, greenSlots=1, rewardUrban=true, rewardCount=6}, {id=26, column=7, row=2,nameEN="HOUSE-GARDEN",nameDE="PRIVATGARTEN", typeEN="EXTENSIONS",typeDE="ERWEITERUNGEN",height=0, slots=0, green=true, greenSlots=1, rewardUrban=true, rewardCount=6}, {id=27, column=7, row=3,nameEN="HOUSE-GARDEN",nameDE="PRIVATGARTEN", typeEN="EXTENSIONS",typeDE="ERWEITERUNGEN",height=0, slots=0, green=true, greenSlots=1, rewardUrban=true, rewardCount=6}, {id=28, column=7, row=4,nameEN="HOUSE-GARDEN",nameDE="PRIVATGARTEN", typeEN="EXTENSIONS",typeDE="ERWEITERUNGEN",height=0, slots=0, green=true, greenSlots=1, rewardUrban=true, rewardCount=6}, ---green gathering spaces {id=29, column=8, row=1,nameEN="OPEN-АIR-CINEMA",nameDE="FREILUFTKINO", typeEN="GATHERING SPACES",typeDE="TREFFPUNKTE",height=8, slots=3, green=true, greenSlots=3, rewardUrban=true, rewardCount=9}, {id=30, column=8, row=2,nameEN="OPEN-AIR-SWIMMING-POOL",nameDE="FREIBAD", typeEN="GATHERING SPACES",typeDE="TREFFPUNKTE",height=8, slots=3, green=true, greenSlots=3, rewardUrban=true, rewardCount=9}, {id=31, column=8, row=3,nameEN="GREEN-CAFE",nameDE="GRÜNES CAFÉ", typeEN="GATHERING SPACES",typeDE="TREFFPUNKTE",height=8, slots=3, green=true, greenSlots=3, rewardUrban=true, rewardCount=9}, {id=32, column=8, row=4,nameEN="PALM-TREE-HOUSE",nameDE="PALMENGARTEN", typeEN="GATHERING SPACES",typeDE="TREFFPUNKTE",height=8, slots=3, green=true, greenSlots=3, rewardUrban=true, rewardCount=9}, ----urban gathering spaces {id=33, column=9, row=1,nameEN="CINEMA",nameDE="KINO", typeEN="GATHERING SPACES",typeDE="TREFFPUNKTE",height=8, slots=3, green=false, greenSlots=0, rewardUrban=false, rewardCount=9}, {id=34, column=9, row=2,nameEN="CONFERENCE-HALL",nameDE="KONFERENZHALLE", typeEN="GATHERING SPACES",typeDE="TREFFPUNKTE",height=8, slots=3, green=false, greenSlots=0, rewardUrban=false, rewardCount=9}, {id=35, column=9, row=3,nameEN="CAFÉ",nameDE="CAFÉ", typeEN="GATHERING SPACES",typeDE="TREFFPUNKTE",height=8, slots=3, green=false, greenSlots=0, rewardUrban=false, rewardCount=9}, {id=36, column=9, row=4,nameEN="ART-GALLERY",nameDE="KUNSTGALERIE", typeEN="GATHERING SPACES",typeDE="TREFFPUNKTE",height=8, slots=3, green=false, greenSlots=0, rewardUrban=false, rewardCount=9}, ---green-roof houses {id=37, column=10, row=1,nameEN="GREEN-ROOF-HOUSE",nameDE="GRÜNES EINFAMILIENHAUS", typeEN="HOUSES",typeDE="HÄUSER",height=10, slots=2, green=true, greenSlots=2, rewardUrban=true, rewardCount=4}, {id=38, column=10, row=2,nameEN="GREEN-ROOF-HOUSE",nameDE="GRÜNES EINFAMILIENHAUS", typeEN="HOUSES",typeDE="HÄUSER",height=10, slots=2, green=true, greenSlots=2, rewardUrban=true, rewardCount=4}, {id=39, column=10, row=3,nameEN="GREEN-ROOF-HOUSE",nameDE="GRÜNES EINFAMILIENHAUS", typeEN="HOUSES",typeDE="HÄUSER",height=10, slots=2, green=true, greenSlots=2, rewardUrban=true, rewardCount=4}, {id=40, column=10, row=4,nameEN="GREEN-ROOF-HOUSE",nameDE="GRÜNES EINFAMILIENHAUS", typeEN="HOUSES",typeDE="HÄUSER",height=10, slots=2, green=true, greenSlots=2, rewardUrban=true, rewardCount=4}, } DEFAULT_REWARD = houseBlock(4) REWARDS = {} --constructor for player object so that data structure is consistant function newPlayerData(name,x,y,z) local p = { name = name, x=x, y=y, z=z } return p end --return a list of all players in the game world as player objects local function getAllPos(selector) local result, message = commands.tp("@a["..selector.."]","~ ~ ~") local names = {} if result == true then for i,result in ipairs(message) do local wordpattern = "[^, ]+" local numberpattern = "[%-% ]%d+[%.]%d+" local words,numbers = {},{} for word in string.gmatch(result, wordpattern) do table.insert(words,word) end for number in string.gmatch(result, numberpattern) do table.insert(numbers,number) end local coords = { x = math.floor(numbers[1]), y = math.floor(numbers[2]), z = math.floor(numbers[3]) } local name = words[2] table.insert(names,newPlayerData(name,coords.x,coords.y,coords.z)) --print("Player Found - getAllPos") end end return names end --sort table by random local function shuffleTable( t ) local rand = math.random assert( t, "shuffleTable() expected a table, got nil" ) local iterations = #t local j for i = iterations, 2, -1 do j = rand(i) t[i], t[j] = t[j], t[i] end end --returns a list of player objects containing all players who are standing on the given block, and who also are in the given selection local function getAllOnBlockType(block,selector) local result, message = commands.exec("execute @a["..selector.."] ~ ~ ~ detect ~ ~-1 ~ "..block.." -1 tp @p[r=1] ~ ~ ~") local names = {} if result == true then for i,result in ipairs(message) do local wordpattern = "[^, ]+" local numberpattern = "[%-% ]%d+[%.]%d+" local words,numbers = {},{} for word in string.gmatch(result, wordpattern) do table.insert(words,word) end for number in string.gmatch(result, numberpattern) do table.insert(numbers,number) end if numbers[1] and numbers[2] and numbers[3] then local coords = { x = math.floor(numbers[1]), y = math.floor(numbers[2]), z = math.floor(numbers[3]) } local name = words[2] table.insert(names,newPlayerData(name,coords.x,coords.y,coords.z)) print("Found a player - getOnBlock") else print("Error: Coordinate Numbers were missing") end end end return names end --gives a player a HOMECOMER egg with their name on it. Removes all spawn eggs first local function giveHomecomer(name) commands.clear(name,'spawn_egg') commands.give(name,'spawn_egg 1 '..HOMECOMER_VALUE..' {CanPlaceOn:["'..BLOCKS.PHVFLOOR.block..'","'..BLOCKS.CAMP_FLOOR.block..'","'..BLOCKS._matWhiteSmooth.block..'","'..BLOCKS._matWhiteSmooth2.block..'","'..BLOCKS._matYellowSmooth.block..'","'..BLOCKS._matPinkSmooth.block..'","'..BLOCKS._matBlueSmooth.block..'","'..BLOCKS._matPurpleSmooth.block..'","'..BLOCKS._matWhiteTextured.block..'","'..BLOCKS._matYellowTextured.block..'","'..BLOCKS._matPinkTextured.block..'","'..BLOCKS._matBlueTextured.block..'","'..BLOCKS._matPurpleTextured.block..'"],display:{Name:"HOMECOMER - '..name..'",Lore:[Use this on the floor to return to spawn]}}') end --returns a list of HOMECOMER entities and their names local function getHomecomers() local result, message = commands.exec("execute @e[type="..HOMECOMER_TYPE.."] ~ ~ ~ tp @e[r=1] ~ ~ ~") local names = {} if result == true then for i,result in ipairs(message) do local wordpattern = "[^, ]+" local numberpattern = "[%-% ]%d+[%.]%d+" local words,numbers = {},{} for word in string.gmatch(result, wordpattern) do table.insert(words,word) print(word) end for number in string.gmatch(result, numberpattern) do table.insert(numbers,number) end if numbers[1] and numbers[2] and numbers[3] then local coords = { x = math.floor(numbers[1]), y = math.floor(numbers[2]), z = math.floor(numbers[3]) } local name = words[4] table.insert(names,newPlayerData(name,coords.x,coords.y,coords.z)) print("Found a player - getOnBlock "..name) else print("Error: Coordinate Numbers were missing") end end end return names end local function removePlayerFromKew(game,playername) for _,kew in pairs(game.queues) do for index, player in ipairs(kew.playerlist) do if player.name == playername then table.remove(kew.playerlist,index) end end if #kew.playerlist == 0 and kew.phase == 2 then --game can be ended as no players are left kew.timer = 0 end end end local function movePlayerToSpawn(playername) respawnPlayer(playername) commands.async.tp(playername,SPAWN.x,SPAWN.y,SPAWN.z,SPAWN.a1,SPAWN.a2) commands.async.tellraw(playername,'["",{"text":"You used your HOMECOMER and TELEPORTED BACK TO SPAWN","color":"white"}]') commands.async.tellraw(playername,'["",{"text":"Du hast den HEIMKEHRER benutzt um dich zurück zum Anfang zu teleportieren.","color":"gold"}]') end --takes a list of homecomers and deals with all those players, moving them back to spawn and removing them from game --also gives them a new homecomer local function dealWithHomecomers(game, homecomers) for _,homecomer in pairs(homecomers) do --remove player from current game if in game removePlayerFromKew(game,homecomer.name) --teleport them back to spawn movePlayerToSpawn(homecomer.name) --give a new homecomer giveHomecomer(homecomer.name) --particle effects --teleport message end --kill all homecomers if #homecomers>0 then commands.tp("@e[type="..HOMECOMER_TYPE.."]",100000,200,100000) commands.kill("@e[type="..HOMECOMER_TYPE.."]") os.sleep(#homecomers*0.2) end end --creates a villager with special items local function spawnVillager(x,y,z) 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:'..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-PICKAXE_USES..',tag:{CanDestroy:["minecraft:wool"]}}} ]}}') end --displays a time as experience points to a selection of players local function displayTime(selector,minutes,seconds) --commands.title("@a["..selector.."]","subtitle",'{text:"Time left: '..minutes..":"..seconds..'",color:red,bold:false,underlined:false,italic:false,strikethrough:false,obfuscated:false}') commands.async.xp("-1000000L","@a["..selector.."]") local secondstot = (minutes * 60) + seconds commands.async.xp(tostring(secondstot).."L","@a["..selector.."]") end --displays a title to a selection of players local function displayTitle(selector,text) commands.async.title("@a["..selector.."]","title",'{text:"'..text..'"}') end --simply runs displayTitle on a list of players local function displayTitleToGroup(playerlist,text) for i,player in ipairs(playerlist) do displayTitle("name="..player.name,text) end end --simply runs displayTime on a list of players local function displayTimeToGroup(playerlist,minutes,seconds) for i,player in ipairs(playerlist) do displayTime("name="..player.name,minutes,seconds) end end --teleports a list of players to an exact place and sends them a message about it local function teleportToPoint(x,y,z,playerlist,clear,textEN, textDE) for i,player in ipairs(playerlist) do player.x = x player.y = y player.z = z commands.async.gamemode(2,player.name) if clear then commands.async.clear(player.name,"wool") commands.async.clear(player.name,"stone_pickaxe") end commands.tp("@a[name="..player.name.."]",x,y,z) commands.async.tellraw(player.name,'["",{"text":"'..textEN..'","color":"white"}]') commands.async.tellraw(player.name,'["",{"text":"'..textDE..'","color":"gold"}]') end end --teleports a list of players to a given buildzone local function teleportToZone(buildzone,playerlist,textEN, textDE) teleportToPoint(buildzone.x+2+(buildzone.w/2),buildzone.y+5,buildzone.z+2+(buildzone.w/2),playerlist,true,textEN, textDE) end --gives the same list of items to a list of players local function giveItems(playerlist,itemlist) local given = 0 for i,player in ipairs(playerlist) do --commands.async.clear(player.name) for j,item in ipairs(itemlist) do commands.async.give("@a[name="..player.name.."]",item) given = given +1 end giveHomecomer(player.name) end return given end --a multi builder which uses the vocab constructor to create sets of vocab local function makeVocabZones(quant,w) local x,y,z = FIRSTVOCAB.x, FIRSTVOCAB.y, FIRSTVOCAB.z local result = {} local id = 1 for i=0,quant-1 do for k=0,3 do local zpos = i-4 local ypos = k --print("vocab at X") --print(x-(2*w)-6) --print("and Z") --print(z+((w+1)*zpos)) local nextVocab = newVocabZone( x-(2*w)-6,y+(ypos*(VOCAB_HEIGHT+3)), z+((w+1)*zpos), w, id, REWARDS[id] or DEFAULT_REWARD, VOCABS_DATA[id].nameEN or DEFAULT_NAME, VOCABS_DATA[id].nameDE or DEFAULT_NAME, VOCABS_DATA[id].typeEN, VOCABS_DATA[id].typeDE, VOCABS_DATA[id].height, VOCABS_DATA[id].slots, VOCABS_DATA[id].green, VOCABS_DATA[id].greenSlots, VOCABS_DATA[id].rewardUrban, VOCABS_DATA[id].rewardCount ) table.insert(result,nextVocab) id = id +1 end end return result end --finds the next free location(or buildzone) for a new game in a given area. --giving force as True will mean it overrides the first location no matter what local function findNextLoc(width,zones,force) local x,y,z,locid = 0,0,0,1 for i,loc in ipairs(LOCS) do locid = i x,y,z = FIRSTZONE.x+(loc.x*(width+1)),FIRSTZONE.y,FIRSTZONE.z+(-loc.z*(width+1)) -- these are the coordinates of this LOC in the minecraft world --print("testing for available zone at: "..x..", "..y..", "..z) --print("which is at grid cell at: "..loc.x..", "..loc.z) --local result,message = commands.testforblock(x,y+BUILDZONE_FLOOR_HEIGHT,z,"minecraft:air") -- this was used for the testing based on the minecraft model local result = true if loc.played then result = false print("zone has been played") end --print("testing done") --force the first zone to be selected unless it is taken in the "zones" parameter if force then result = true end --checks if the zone is already in the list of unavailable zones passed as parameter local zonefree = true for i,zone in ipairs(zones) do if zone.x == x and zone.z == z then zonefree = false end end --print("next position free is ",loc.x*width,oy,loc.z*width) --if result then print("true") else print("false") end if result and zonefree then --print("using loc: ",loc.x,loc.z) return x,y,z,locid --returns the coordinates of the new zone, plus its id in the LOCS table end end return nil,nil,nil, nil --returns empty if no zone is available end --relocates a buildzone to a new coordinate safely --avoids overlapping with other buildzones function moveBuildzone(buildzone,zones) local x,y,z,locid = findNextLoc(buildzone.w,zones) if x and y and z and locid then print("moved buildzone from "..buildzone.x..","..buildzone.z.." to "..x..","..y) local w = buildzone.w buildzone.x,buildzone.y,buildzone.z = x,y+BUILDZONE_FLOOR_HEIGHT,z buildzone.locid = locid --reassign the location id corresponding to the LOCS item for the grid cell of the moved zone buildzone.selector = "x="..x..",y="..tostring(y-1)..",z="..z..",dx="..w..",dy=256,dz="..w buildzone.structures = {} --a list of all vocabularies which have been contructed else print("buildzone at "..buildzone.x..","..buildzone.z.." stayed where it is") end end --multi builder to create sets of buildzones using the buildzone constructor local function makeBuildzones(quant,vocab,width,height) local result = {} for i=1,quant do --print("locating available slot") local x,y,z,locid = findNextLoc(width,result) if x and y and z and locid then --print("made new buildzone at",x,y,z) table.insert(result,newBuildZone(x,y+height,z,width,vocab,locid)) else --print("failed to make new buildzone") end end local remaining = NUMBER_OF_BUILDZONES - #result --print("doing this remaining thing") for i=1, remaining do local x,y,z,locid = findNextLoc(width,result,true) if x and y and z and locid then --print("forced new buildzone at",x,y,z) table.insert(result,newBuildZone(x,y+height,z,width,vocab, locid)) else print("failed to force new buildzone") end end return result end --vocab constructor. Enforces some data structure function newVocabZone(x,y,z,w,id, reward,nameEN, nameDE, typeEN, typeDE, height, slots,green,greenSlots, rewardUrban, rewardCount) local nvz = {} nvz.x ,nvz.y ,nvz.z ,nvz.w = x,y,z,w nvz.cx = nvz.x - nvz.w - 2 nvz.cy = nvz.y nvz.cz = nvz.z nvz.nameEN = nameEN nvz.reward = reward --- new stuff nvz.id = id nvz.nameDE = nameDE nvz.typeEN = typeEN nvz.typeDE = typeDE nvz.height = height nvz.slots = slots nvz.green = green nvz.greenSlots = greenSlots nvz.rewardUrban = rewardUrban nvz.rewardCount = rewardCount return nvz end --buildzone constructor. Enforces some data structure function newBuildZone(x,y,z,w,vocabZones,locid) local nbz = {} nbz.x ,nbz.y ,nbz.z ,nbz.w = x,y,z,w nbz.selector = "x="..x..",y="..(y-5)..",z="..z..",dx="..w..",dy=256,dz="..w nbz.structures = {} --a list of all vocabularies names which have been contructed nbz.buildings = {} --a list of all vocabularies with full data (x,y,z,id,name) which have been contructed nbz.filledSlots = 0 --to count how many slots have been filled with buildings. the matrix is 7x7x20 slots. one slot is 9x9x9 blocks big nbz.greenSlots = 0 --to count how many of the slots are green. the matrix is 7x7x20 slots. one slot is 9x9x9 blocks big 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 nbz.waitingForCheck = {} nbz.highest = 0 nbz.vocab = vocabZones nbz.locid = locid return nbz end --kew constructor. Enforces some data structure function newQueue(buildzone,maxplayers) local q = {} q.timer = 1 q.phase = 1 q.victory = false q.phases = { { name = "Selecting Players", length = 25, displaylength = 15 }, { name = "Game In Progress", length = GAME_LENGTH, displaylength = 70 }, { name = "Round Complete", length = 35, displaylength = 5 } } q.playerlist = {} q.maxplayers = maxplayers q.buildzone = buildzone return q end --creates a ring of blocks using coordinates function fillRing(x,y,z,w,block) commands.fill(x,y,z,x+w,y,z,block) commands.fill(x+w,y,z,x+w,y,z+w,block) commands.fill(x,y,z+w,x+w,y,z+w,block) commands.fill(x,y,z+w,x,y,z,block) end function setup() print("debug: starting setup function.") local game = {} game.vocab = {} game.builds = {} game.queues = {} game.waitlist = {} game.spawn = SPAWN game.lastClock = os.clock() game.nowTime = os.clock() --generate vocab rewards for i=1,#VOCABS_DATA do if VOCABS_DATA[i].rewardUrban then REWARDS[i] = houseBlock(VOCABS_DATA[i].rewardCount) else REWARDS[i] = gardenBlock(VOCABS_DATA[i].rewardCount) end end --kill all villagers commands.exec("kill @e[type=Villager]") --buildzone and vocabzone creation print("debug-setup: making vocab zones.") game.vocab = makeVocabZones(NUMBER_OF_VOCAB,VOCAB_WIDTH) print("debug-setup: making building zones.") game.builds = makeBuildzones(NUMBER_OF_BUILDZONES,game.vocab,BUILDZONE_WIDTH,BUILDZONE_FLOOR_HEIGHT) for i,build in ipairs(game.builds) do table.insert(game.queues,newQueue(build,4)) end print("debug-setup: testing for properly setup vocabs.") for i,vz in ipairs(game.vocab) do local x,y,z,w = vz.x,vz.y,vz.z,vz.w local cx,cy,cz = vz.cx,vz.cy,vz.cz local detector, message1 = commands.testforblock(x+(math.floor(w/2)),y,z+(math.floor(w/2)),BLOCKS.DETECT.block) local blocker, message2 = commands.testforblock(x+(math.floor(w/2)),y,z+(math.floor(w/2)),BLOCKS.DETECT_DEAD.block) if not (detector or blocker) then for nx=0,2 do for nz=0,2 do commands.setblock(x+(nx*9)+4,y-1,z+(nz*9)+4,BLOCKS.VOCAB_REPLACE.block) commands.setblock(cx+(nx*9)+4,cy-1,cz+(nz*9)+4,BLOCKS.VOCAB_DETECT.block) end end commands.setblock(x+(math.floor(w/2)),y,z+(math.floor(w/2)),BLOCKS.DETECT_DEAD.block) end end print("debug-setup: adding scoreboards.") --[[ for i, name in ipairs(VOCAB_NAMES) do commands.scoreboard("objectives","add",name,"dummy") end --]] --print(#VOCABS_DATA) for i=1,#VOCABS_DATA do commands.scoreboard("objectives","add","building_"..i,"dummy") end --commands.gamerule("doDaylightCycle",false) -- this is included in the spawn controller script commands.gamerule("keepInventory",true) --commands.gamerule("doTileDrops",false) -- this is included in the spawn controller script --commands.gamerule("logAdminCommands",false) -- this is included in the spawn controller script --commands.gamerule("commandBlockOutput",false) -- this is included in the spawn controller script --commands.time("set",6000) -- this is included in the spawn controller script commands.scoreboard("objectives","add","highscores","dummy","Best Neighbourhoods") commands.scoreboard("objectives","add","VillagerLife","dummy") commands.scoreboard("objectives","add","built","dummy", "Structures Built") commands.scoreboard("objectives","add","highest","dummy", "Personal Highest") commands.scoreboard("objectives","add","played","dummy","Games Played") commands.title("@a","times",0,30,30) -- what does this do? math.randomseed( os.time() ) commands.scoreboard("objectives","setdisplay","sidebar","highscores") commands.scoreboard("objectives","setdisplay","list","played") --print("time is: "..os.time()) --print( "time is: "..textutils.formatTime( os.time(), false ) ) --print("day is: "..os.day()) print("computer clock is: "..os.clock()) if DEBUG_MODE then for i,build in ipairs(game.builds) do build.phase = 2 build.timer = 500 end end print("Computer Co-ordinates ",ox,oy,oz) print("20.000 Blocks Active!") return game end function checkForHomecomers(game) --execute on all homecomers local homecomers = getHomecomers() dealWithHomecomers(game,homecomers) end --main game loop --runs the game object through each of these update steps in order function update(game) local elapsed = updateClock(game) --update players checkPlayers(game) doTimerUpdates(game,elapsed) doPhaseUpdates(game) doPhaseEnds(game) checkBoundaries(game) checkForHomecomers(game) if #game.waitlist > 0 then allocateWaiters(game) end end --calculates elapsed time during a game tick function updateClock(game) game.nowTime = os.clock() local elapsed = game.nowTime - game.lastClock game.lastClock = game.nowTime return elapsed end --updates all kews in the game object based on elapsed time function doTimerUpdates(game,elapsed) for i,kew in ipairs(game.queues) do kew.timer = kew.timer - elapsed end end --check players are inside their buildzone and move them back if not function checkBoundaries(game) for i,kew in ipairs(game.queues) do if kew.phase ==2 then --boundaries local x_min = kew.buildzone.x local x_max = kew.buildzone.x+kew.buildzone.w local z_min = kew.buildzone.z local z_max = kew.buildzone.z+kew.buildzone.w local toBeCorrected = {} for j,player in ipairs(kew.playerlist) do local listOfOne = getAllPos('m=2,name='..player.name) if listOfOne and listOfOne[1] then player.x = listOfOne[1].x player.y = listOfOne[1].y player.z = listOfOne[1].z local changed = false if player.x > x_max then changed = true player.x = x_max-2 end if player.x < x_min then changed = true player.x = x_min+2 end if player.z > z_max then changed = true player.z = z_max-2 end if player.z < z_min then changed = true player.z = z_min+2 end if changed then teleportToPoint(player.x,kew.buildzone.y,player.z,{player},false,"TELEPORTED BACK TO GAME: Please stay inside the building zone or use HOMECOMER to leave the game!", "Zurück ins Spiel teleportiert: Bitte bleib innerhalb des Baufeldes oder nutze den HEIMKEHRER um das Spiel zu verlassen!") end end end end end end --here you can set the logic which determines if a buildzone was valuable. --Return true if it is crap and should be replaced function checkIfBuildzoneIsCrap(buildzone) if #buildzone.structures < 5 then print("Buildzone was crap") return true end print("Buildzone was ok") return false end --Everything that happens after a buildzone was completed --due to time limit. function cleanAfterVictory(buildzone) commands.async.setblock(buildzone.x,buildzone.y,buildzone.z,BLOCKS.VICTORY_MARKER.block) fillRing(buildzone.x,buildzone.y,buildzone.z,buildzone.w,"minecraft:air",0,"replace",BLOCKS.CONSTRUCTION.block,BLOCKS.CONSTRUCTION.data) for h=0,256-buildzone.y do 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",BLOCKS.DETECT.block,BLOCKS.DETECT.data) 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",BLOCKS.PLUG.block,BLOCKS.PLUG.data) 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",BLOCKS.BUILDING_GARDEN.block,BLOCKS.BUILDING_GARDEN.data) 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",BLOCKS.BUILDING_HOUSE.block,BLOCKS.BUILDING_HOUSE.data) end commands.async.fill(buildzone.x,buildzone.y-1,buildzone.z,buildzone.x+buildzone.w,buildzone.y-1,buildzone.z+buildzone.w,BLOCKS.CAMP_FLOOR.block,BLOCKS.CAMP_FLOOR.data,"replace",BLOCKS.PLUG.block,BLOCKS.PLUG.data) commands.async.fill(buildzone.x,buildzone.y,buildzone.z,buildzone.x+buildzone.w,buildzone.y,buildzone.z+buildzone.w,BLOCKS.PHVFLOOR.block,BLOCKS.PHVFLOOR.data,"replace","minecraft:air","0") local wasCrap = checkIfBuildzoneIsCrap(buildzone) if wasCrap then --mark this buildzone for replacement commands.async.setblock(buildzone.x,buildzone.y,buildzone.z,"minecraft:air") else --need to find the LOCS item corresponding to this buildzone --change the flag to played=true LOCS[buildzone.locid].played = true --and write the LOCS object to the json file writeGridToFile() end end --these happen every tick and require the game object --updates are performed on each Queue (kew) and within each --of those on each buildzone. function doPhaseUpdates(game) for i,kew in ipairs(game.queues) do local minutes = string.format("%02d",math.floor(kew.timer/60)) local seconds = string.format("%02d",math.floor(kew.timer - (minutes*60))) if kew.timer <= 0 then minutes = "00" seconds = "00" end if kew.phase == 1 then --waiting phase if #kew.playerlist == kew.maxplayers and kew.timer > 5 then kew.timer = 5 end if not DEBUG_MODE and #kew.playerlist == 0 then kew.timer = kew.phases[1].length end displayTitleToGroup(kew.playerlist,"Game starting!") displayTimeToGroup(kew.playerlist,minutes,seconds) --show countdown elseif kew.phase == 2 then --playing phase if #kew.playerlist == 0 then timer = 0 end -- finish if all players quit -- do vocab logic local victory = updatePlayedZone(kew.buildzone) --currently victory updatePlayedZone returns always false -- displayTimeToGroup(kew.playerlist,minutes,seconds) elseif kew.phase == 3 then --end phase displayTitleToGroup(kew.playerlist,"Use HOMECOMER to return") --displayTimeToGroup(kew.playerlist,minutes,seconds) end end end --this runs after a buildzone is completed --it should tally structure types and set scoreboard highscores --it also rewards all participants with more played score function processHighscores(kew) local buildzone = kew.buildzone --add score to players who finished this game for _,player in ipairs(kew.playerlist) do commands.async.scoreboard("players","add",player.name,"played",1) end end --function to export a buildzone detail once it is complete --requires a kew so it can access the playerlist local function exportKewData(kew) local buildzone = kew.buildzone local saved = {} saved.position = { x=buildzone.x, y=buildzone.y, z=buildzone.z } local timestamp = math.floor(os.clock()) --saved.timeCompleted = timestamp saved.players = {} for _, player in ipairs(kew.playerlist) do table.insert(saved.players,player.name) end --saved.structures = buildzone.structures saved.buildings = buildzone.buildings saved.totals = tracker.tallyTable(buildzone.structures) --saved.highest = buildzone.highest saved.stats = { cityVersion = CITY_VERSION, height = buildzone.highest, densityIndex = math.floor(100*buildzone.filledSlots/49), -- the density index is made from built area (filledSlots) over the ground area (7x7 slots = 49) greenIndex = math.floor(100*buildzone.greenSlots/49), --the green index is made from green area (greenSlots) over the ground area (7x7 slots = 49) variety = tablelength(buildzone.variety), timeCompleted = timestamp, gameLength = GAME_LENGTH } fs.makeDir("/records") local file = fs.open("/records/"..timestamp.."at"..buildzone.x.."_"..buildzone.z..".yaml","w") file.write(json.encodePretty(saved)) file.close() end --this code runs ONCE at the end of each phase --what actually happens is specific to which phase the --particular kew is in. function doPhaseEnds(game) for i,kew in ipairs(game.queues) do if kew.timer <= 0 then if kew.phase == 1 then --waiting phase ends goto play phase moveBuildzone(kew.buildzone,game.builds) teleportToZone(kew.buildzone,kew.playerlist,"Your game has started! BUILD A HOUSE!", "Das Spiel hat begonnen! BAUE EIN HAUS!")--teleport selected players cleanBuildzone(kew.buildzone) prepareBuildzone(kew.buildzone)--prepare build zone giveItems(kew.playerlist,STARTING_ITEMS) --give starting items displayTitle(kew.buildzone.selector,"BUILD!") kew.victory = false --displayTime(kew.buildzone.selector,0,0) kew.phase = 2 kew.timer = kew.phases[2].length elseif kew.phase == 2 then --playing phase ends goto end phase processHighscores(kew) exportKewData(kew) cleanAfterVictory(kew.buildzone) kew.phase = 3 --displayTime(kew.buildzone.selector,0,0) kew.timer = kew.phases[3].length elseif kew.phase == 3 then --end phase ends goto waiting phase removePlayersFromKew(game,kew) kew.phase = 1 --displayTime(kew.buildzone.selector,0,0) kew.timer = kew.phases[1].length end end end end --Replaces everything that is needed to start the game. Does not rebuild the floor, or clear anything away. --based on the settings it creates a full grid, or a partial grid, or no grid --it also places the ring, although this is disabled for now function prepareBuildzone(buildzone) local bz = buildzone local x,y,z,w = bz.x,bz.y,bz.z,bz.w commands.fill(x,y-1,z,x+w,y-1,z+w,BLOCKS.CAMP_FLOOR) --place the white grid accross the full buildzone as a base fillRing(buildzone.x,buildzone.y,buildzone.z,buildzone.w,BLOCKS.CONSTRUCTION.block) --this draws the construction stripe around the buildzone --create the grid of detectors surrounded by plus plugs if DO_GRID then for x=0,6 do for z=0,6 do local rand = math.random()*100 --local result, text = commands.testforblock(bz.x+(x*9)+4,bz.y-1,bz.z+(z*9)+4,BLOCKS.CAMP_FLOOR.block) if rand > GRID_HOLE_CHANCE then --and result then commands.async.setblock(bz.x+(x*9)+4,bz.y,bz.z+(z*9)+4,BLOCKS.DETECT.block,BLOCKS.DETECT.data,"replace","minecraft:air") commands.async.fill(bz.x+(x*9)+1,bz.y-1,bz.z+(z*9)+4,bz.x+(x*9)+7,bz.y-1,bz.z+(z*9)+4,BLOCKS.PLUG.block) commands.async.fill(bz.x+(x*9)+4,bz.y-1,bz.z+(z*9)+1,bz.x+(x*9)+4,bz.y-1,bz.z+(z*9)+7,BLOCKS.PLUG.block) --commands.async.setblock(bz.x+(x*9)+4,bz.y-1,bz.z+(z*9)+4,BLOCKS.CAMP_FLOOR.block) end end end end commands.async.setblock(buildzone.x,buildzone.y,buildzone.z,BLOCKS.CONSTRUCTION.block) end --deletes everything inside the buildzone function cleanBuildzone(buildzone) print("Cleaning buildzone") for h=0,VICTORY_HEIGHT+20 do commands.async.fill(buildzone.x,buildzone.y+h,buildzone.z,buildzone.x+buildzone.w,buildzone.y+h,buildzone.z+buildzone.w,"minecraft:air") end end --[[ --moves all players assigned to the queue into the waiting area for the provided game. --It also changes them to Adventure mode and clears their inventory function respawnPlayers(game,kew) teleportToPoint(game.spawn.x,game.spawn.y,game.spawn.z,kew.playerlist,true,"TELEPORTED: Your game ended so you have been returned to the Spawn Point", "DE: TELEPORTED: Your game ended so you have been returned to the Spawn Point") --put players back in spawn area for i,player in ipairs(kew.playerlist) do --table.insert(game.waitlist,player) respawnPlayer(player,"") end kew.playerlist = {} end --]] function removePlayersFromKew(game,kew) for _, player in ipairs(kew.playerlist) do --commands.tell(player.name,"Your game is over. Use your Heimkehrer to return to spawn") -- announce success in English commands.async.tellraw(player.name,'["",{"text":"TIME OUT! GAME COMPLETE! Use your HOMECOMER to return to spawn.","color":"white"}]') -- announce success in German commands.async.tellraw(player.name,'["",{"text":"ENDE! SPIEL ABGESCHLOSSEN! Nutze den HEIMKEHRER um zum Anfang zurück zu kehren.","color":"gold"}]') end kew.playerlist = {} end function respawnPlayer(playername,message) commands.tell(playername,message) commands.async.gamemode(2,playername) commands.async.clear(playername,"minecraft:wool") commands.async.clear(playername,"minecraft:stone_pickaxe") end function checkForPlayerInBuildzone(player,buildzone) local result,message = commands.testfor('@a[name='..player.name..','..buildzone.selector..']') return result end function checkForPlayerInWaitzone(player) local selector = "x="..WAITZONE.x..",y="..WAITZONE.y..",z="..WAITZONE.z..",dx="..WAITZONE.w..",dy=256,dz="..WAITZONE.l local result,message = commands.testfor('@a[name='..player.name..','..selector..']') return result end function checkPlayers(game) local selector = "x="..WAITZONE.x..",y="..WAITZONE.y..",z="..WAITZONE.z..",dx="..WAITZONE.w..",dy=256,dz="..WAITZONE.l local loggedIn = getAllPos('m=2,'..selector) --refresh waitlist game.waitlist = loggedIn --check currently playing players for l,kew in ipairs(game.queues) do for i,builder in ipairs(kew.playerlist) do local isPlaying = checkForPlayerInBuildzone(builder,kew.buildzone) --remove players who are already in kews from the waitlist for j, player in ipairs(loggedIn) do if player.name == builder.name then table.remove(loggedIn,j) end end --if the game is in progress and the player is not found then remove them from the gamekew if not isPlaying and kew.phase == 2 then --table.remove(kew.playerlist,i) --print("Removed "..builder.name.." from game in progress") end end end end --adds players who wait in the orange room to slots in waiting buildzones function allocateWaiters(game) --find free slots local freeslots = {} for i, kew in ipairs(game.queues) do if kew.phase == 1 and #kew.playerlist < kew.maxplayers then local slots = kew.maxplayers - #kew.playerlist for j=1,slots do table.insert(freeslots,kew) end end end --RE-ENABLE SECOND SHUFFLETABLE IF YOU WANT RANDOM PLAYER MATCHUPS shuffleTable(game.waitlist) --shuffleTable(freeslots) while #freeslots > 0 and #game.waitlist > 0 do local player = table.remove(game.waitlist,1) local freeslot = table.remove(freeslots,1).playerlist table.insert(freeslot,player) end end --PARTICLE FUNCTIONS --particles shown to player while their shape is being checked for match function searchParticle(x,y,z) commands.async.particle("fireworksSpark",x,y,z,0.01,3,0.01,0.01,100) end -- particles shown to player on successful vocab match function successParticle(x,y,z) commands.async.particle("happyVillager",x,y,z,2,2,2,1,1000) commands.async.playsound("random.levelup","@a",x,y,z,1,1.2) end --- particles shown to player on failed vocab match function failParticle(x,y,z) commands.async.particle("reddust",x,y,z,0.1,0.1,1.5,1,200) commands.async.particle("reddust",x,y,z,1.5,0.1,0.1,1,200) commands.async.playsound("random.bowhit","@a",x,y,z,1,0.8) end --makes sure that incoming check requests dont exist already function addToChecklist(player,buildzone) for _, detector in ipairs(buildzone.waitingForCheck) do if detector.x == player.x and detector.y == player.y and detector.z == player.z then return false end end table.insert(buildzone.waitingForCheck,player) return true end --removes all barrier blocks from a buildzone which are used to carve space in vocabs function cleanBarriers(buildzone) for h=0,200 do 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") end end --The main chunk of code which deals with stepping on detector blocks and reading the vocab function updatePlayedZone(buildzone) local victory = false local buildzoneSelector = buildzone.selector --get all players on diamond, add them to the list of things to check local detectLocations = getAllOnBlockType(BLOCKS.DETECT.block,buildzoneSelector) for _, player in ipairs(detectLocations) do addToChecklist(player,buildzone) end --DEAL WITH THE DETECTOR AT THE TOP OF THE LIST IF THERE IS ONE if #buildzone.waitingForCheck > 0 then --DO PARTICLE EFFECTS IF A DETECTING BLOCK THAT IS DETECTING for i,loc in ipairs(buildzone.waitingForCheck) do searchParticle(loc.x,loc.y+1,loc.z) end local totalResult = false local checked = table.remove(buildzone.waitingForCheck,1) local x,y,z,name = checked.x,checked.y,checked.z,checked.name for i,vocab in pairs(buildzone.vocab) do local result,message = commands.testforblocks( vocab.x, vocab.y, vocab.z, vocab.x+vocab.w, vocab.y+VOCAB_HEIGHT, vocab.z+vocab.w, x-math.floor(vocab.w/2), y-1, z-math.floor(vocab.w/2),"masked") if result then --clone in the correct vocab local cloneres,clonemes = commands.clone( vocab.cx, vocab.cy, vocab.cz, vocab.cx+vocab.w, vocab.cy+VOCAB_HEIGHT, vocab.cz+vocab.w, x-math.floor(vocab.w/2), y-1, z-math.floor(vocab.w/2),"masked") if DEBUG_MODE then print(clonemes[1]) end commands.async.give(name,vocab.reward) -- announce vocab success in English local rewardType = 'nature' local rewardTypeDE = 'grüne' if vocab.rewardUrban then rewardType = 'urban' rewardTypeDE = 'urbane' end --print(vocab.typeEN) --print(vocab.nameEN) --print(vocab.height) --print(vocab.slots) --print(vocab.greenSlots) --print(vocab.rewardCount) --print(rewardType) commands.async.tellraw(name,'["",{"text":"You built a '..vocab.nameEN.. ' ('..vocab.typeEN..'), which is '..vocab.height..'m tall and gives '..vocab.slots..' density pts, '..vocab.greenSlots..' green pts and '..vocab.rewardCount..'x '..rewardType..' resource!","color":"white"}]') -- announce vocab success in German commands.async.tellraw(name,'["",{"text":"Du hast ein '..vocab.typeDE..' | '..vocab.nameDE.. ' gebaut, das '..vocab.height..' Meter hoch ist und dir '..vocab.slots..' Punkte für die Dichte einbringt, jedoch '..vocab.greenSlots..' Punkte für Grünflächen und '..vocab.rewardCount..' '..rewardTypeDE..' Ressourcen!","color":"gold"}]') --clear out barrier blocks cleanBarriers(buildzone) --ADD THE NEW STRUCTURE TO THE RECORDS table.insert(buildzone.structures,vocab.nameEN) ---CHANGE HERE to make it record the place of the vocab too x, y, z and vocab id local building = {id=vocab.id,xpos=x,ypos=y,zpos=z,name=vocab.nameEN,time=os.clock(),player=name} table.insert(buildzone.buildings, building) --if vocab.green then buildzone.greenSlots = buildzone.greenSlots + vocab.greenSlots --end buildzone.filledSlots = buildzone.filledSlots + vocab.slots local newHeight = y + vocab.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 if newHeight > buildzone.highest then buildzone.highest = newHeight end -- count variety --print("adding variety: "..vocab.id) local varietyId = vocab.id -- we use a variety id instead of the vocab id because vocab id 1,2,3,4 for example are all houses so it is the same variety --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 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 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 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 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 if varietyId ~= 17 and varietyId ~= 18 then --skip the two riser as they ar enot buildings if buildzone.variety[varietyId] then --print("increasing existing item") buildzone.variety[varietyId] = buildzone.variety[varietyId] + 1 else --print("adding new item") buildzone.variety[varietyId] = 1 end end --- CHECK FOR PERSONAL RECORDS --- check if the new structure is the highest --- CHANGE here to live detect the contribution of the new structure to the 4 goals and update them local personalbest = tracker.getScore(name,"highest") if personalbest.count < newHeight then --commands.async.tell(name,"You just topped your personal record for highest structure!") commands.async.scoreboard("players","add",name,"highest",1) -- announce success in English commands.async.tellraw(name,'["",{"text":"You just topped your personal record for highest neighbourhood!","color":"green"}]') -- announce success in German commands.async.tellraw(name,'["",{"text":"Du hast soeben deinen persönlichen Rekord für die höchste Nachbarschaft gebrochen!","color":"gold"}]') end --- --- -- CHECK if placing the current structure would result in beating a server-wide record on the 4 goals --calculate total slots - FOR GOAL "DENSEST NEIGHBOURHOOD" local most = tracker.getScore("Densest_[points]","highscores") local Kint = math.floor(100 * buildzone.filledSlots / 49) -- Kint is the density index made from built area (filledSlots) over ground area (7x7 slots = 49) if Kint > most.count then commands.async.scoreboard("players","set","Densest_[points]","highscores",Kint) -- announce success in English commands.async.tellraw("@a",'["",{"text":"Great! '..name.. ' just topped the record for the DENSEST NEIGHBOURHOOD!","color":"green"}]') -- announce success in German commands.async.tellraw("@a",'["",{"text":"Sehr gut! '..name.. ' hat einen neuen Rekord für die DICHTESTE NACHBARSCHAFT aufgestellt!","color":"gold"}]') --commands.async.say("@a","The record for the Densest Neighbourhood has been topped!") end -- FOR THE GOAL "MOST DIVERSE NEIGHBOURHOOD" -- here we need to count how many varieties of buildings there are --local structures = tracker.tallyTable(buildzone.structures) -- this counts the variety of buildings in a game local mostDiverse = tracker.getScore("Most-Diverse_[out-of-26]","highscores") local typeCount = tablelength(buildzone.variety) --print("variety count is: "..typeCount) if typeCount > mostDiverse.count then commands.async.scoreboard("players","set","Most-Diverse_[out-of-26]","highscores", typeCount) -- announce success in English commands.async.tellraw("@a",'["",{"text":"Wow! '..name.. ' just topped the record for the MOST DIVERSE NEIGHBOURHOOD!","color":"green"}]') -- announce success in German commands.async.tellraw("@a",'["",{"text":"Wow! '..name.. ' hat soeben einen neuen Rekord für die VIELSEITIGSTE NACHBARSCHAFT aufgestellt!","color":"gold"}]') --commands.async.say("@a","The record for the Most Diverse Neighbourhood has been topped!") end -- FOR THE GOAL "GREENEST NEIGHBOURHOOD" -- here we need to count the number of green vocabs local greenest = tracker.getScore("Greenest_[points]","highscores") local Gint = math.floor(100*buildzone.greenSlots/49) --Gint is the green index, made from green area (greenSlots) over ground area (7x7 slots = 49) if Gint > greenest.count then commands.async.scoreboard("players","set","Greenest_[points]","highscores",Gint) -- announce success in English commands.async.tellraw("@a",'["",{"text":"Awesome! '..name.. ' just topped the record for the GREENEST NEIGHBOURHOOD!","color":"green"}]') -- announce success in German commands.async.tellraw("@a",'["",{"text":"Klasse! '..name.. ' hat einen neuen Rekord für die GRÜNSTE NACHBARSCHAFT aufgestellt!","color":"gold"}]') ---commands.async.say("@a","The record for the Densest Neighbourhood has been topped!") end --calculate highest placement -- FOR THE GOAL "TALLEST NEIGHBOURHOOD" local highest = tracker.getScore("Tallest_[meters]","highscores") if buildzone.highest > highest.count then commands.async.scoreboard("players","set","Tallest_[meters]","highscores",buildzone.highest) -- announce success in English commands.async.tellraw("@a",'["",{"text":"Incredible! '..name..' just topped the record for TALLEST NEIGHBOURHOOD!","color":"green"}]') -- announce success in German commands.async.tellraw("@a",'["",{"text":"Unglaublich! '..name..' hat einen neuen Rekord für die HÖCHSTE NACHBARSCHAFT aufgestellt!","color":"gold"}]') --commands.async.say("@a","The record for the Tallest Negihbourhood has been topped!") end --increase the "how many structures did i build" score for the building player commands.async.scoreboard("players","add",name,"built",1) commands.async.scoreboard("players","add",name,"building_"..vocab.id,1) totalResult = true break end end if totalResult then --yey win, do a happy time successParticle(x,y,z) else --no vocab found so do a fail particle --announce in English commands.async.tellraw(name,'["",{"text":"The shape you have built does not match any shape in the catalogue, try a different one.","color":"red"}]') -- announce in German 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"}]') failParticle(x,y-1,z) end end return victory end -- returns how many items are in a table/list function tablelength(T) local count = 0 for _ in pairs(T) do count = count + 1 end return count end --Display game information on the monitor function debugDisplay(game) monitor.clear() if blink then monitor.setCursorPos(1,1) monitor.setTextColor(colors.red) monitor.write("Running") monitor.setTextColor(colors.white) redstone.setOutput("top",true) blink = false else redstone.setOutput("top",false) blink = true end local line = 2 for i,kew in ipairs(game.queues) do monitor.setCursorPos(1,line) local minutes = string.format("%02d",math.floor(kew.timer/60)) local seconds = string.format("%02d",math.floor(kew.timer - (minutes*60))) monitor.write("Buildzone "..i.." | Phase: "..kew.phase.." | Time: "..minutes..":"..seconds) monitor.setCursorPos(1,line+1) for i,player in ipairs(kew.playerlist) do monitor.write(player.name.." ") end line = line +2 end monitor.setCursorPos(1,10) for i,player in ipairs(game.waitlist) do monitor.write(player.name.." ") end end --BEGIN RUNTIME CODE local blink = true local game = setup() while true do update(game) if monitor then debugDisplay(game) end commands.async.weather("clear",10000) local resetbutton = redstone.getInput("left") if resetbutton then print("reset!") for i,kew in ipairs(game.queues) do if kew.phase == 2 then kew.timer = 5 end end end sleep(2) end