· 8 years ago · Nov 29, 2017, 10:00 AM
1local VERSION = '2.5 - horizontal catalogue'
2os.loadAPI('registry')
3os.loadAPI('tracker')
4os.loadAPI('json')
5
6local DEBUG_MODE = registry.DEBUG_MODE or true
7--local monitor = peripheral.wrap("right")
8
9--contains a list of all posible Buildzones (built a bit later)
10local LOCS = {}
11local cleanbuildzone = false
12
13
14-- DONE remove the computer dependency on the monitor
15-- place chunkloaders in a grid that would cover the play area and the catalogue area
16-- game area depends on:
17-- make a platfrom under the computer with the right dimensions - size of play area, size of catalogue, space between them from snow
18-- draw the borders of grid slots
19-- place the grid points along the area in sandstone
20-- mark the play area in sandstone
21-- mark the catalogue area for keys in sandstone
22-- place color points for goal/rewards specification
23--draw the grid points within the catalogue slots
24-- draw the grid points and place the activator stones in the play area
25-- move the code from 555-575 that verifies the voceb slots to a better suited location
26-- save a local file after successful cretion to mark the used variables
27-- if upon start the settigns differ from the ones in the local file then delete and remodel from scratch, dont remove catalogue or play creations by players
28
29-- need a script to clean up the air above the game area
30--we then need a new script to save old catalogues to external files
31-- and another script to restore old catalgoues to new catalogue slots from external file
32
33----UTILITY FUNCTIONS
34-- check if the file exists
35function file_exists(file)
36 local f = fs.open(file, "rb")
37 if f then f:close() end
38 return f ~= nil
39end
40
41--sort table by random
42local function shuffleTable( t )
43 local rand = math.random
44 assert( t, "shuffleTable() expected a table, got nil" )
45 local iterations = #t
46 local j
47
48 for i = iterations, 2, -1 do
49 j = rand(i)
50 t[i], t[j] = t[j], t[i]
51 end
52end
53
54-- returns how many items are in a table/list
55function tablelength(T)
56 local count = 0
57 for _ in pairs(T) do count = count + 1 end
58 return count
59end
60
61--creates a ring of blocks using coordinates
62function fillRing(x,y,z,w,block)
63 commands.fill(x,y,z,x+w,y,z,block)
64 commands.fill(x+w,y,z,x+w,y,z+w,block)
65 commands.fill(x,y,z+w,x+w,y,z+w,block)
66 commands.fill(x,y,z+w,x,y,z,block)
67end
68
69--PARTICLE FUNCTIONS
70--particles shown to player while their shape is being checked for match
71function searchParticle(x,y,z)
72 commands.async.particle("fireworksSpark",x,y,z,0.01,3,0.01,0.01,100)
73end
74-- particles shown to player on successful vocab match
75function successParticle(x,y,z)
76 commands.async.particle("happyVillager",x,y,z,2,2,2,1,1000)
77 commands.async.playsound("random.levelup","@a",x,y,z,1,1.2)
78end
79--- particles shown to player on failed vocab match
80function failParticle(x,y,z)
81 commands.async.particle("reddust",x,y,z,0.1,0.1,1.5,1,200)
82 commands.async.particle("reddust",x,y,z,1.5,0.1,0.1,1,200)
83 commands.async.playsound("random.bowhit","@a",x,y,z,1,0.8)
84end
85
86----END OF UTILITY FUNCTIONS
87
88--creates a grid of absolute references (0,0) (1,0)
89function buildGrid(w,h)
90 local grid = readGridFromFile()
91 if grid then
92 -- grid was read from the file successfully
93 else
94 --generate file from scratch
95 grid = {}
96 for z=0,h-1 do
97 for x=0,w-1 do
98 table.insert(grid,{x=x,z=z,played=false})
99 end
100 end
101 end
102 return grid
103end
104
105function readGridFromFile()
106 local result = nil
107 fs.makeDir("/records")
108 local filename = "/records/_buildzones-list.json"
109
110 if file_exists(filename) then
111 ---read from file
112 result = json.decodeFromFile(filename)
113 end
114 return result
115end
116
117function writeGridToFile()
118 fs.makeDir("/records")
119 local filename = "/records/_buildzones-list.json"
120
121 local file = fs.open(filename,"w")
122 file.write(json.encodePretty(LOCS))
123 file.close()
124end
125
126--constructor for player object so that data structure is consistant
127function newPlayerData(name,x,y,z)
128 local p = {
129 name = name,
130 x=x,
131 y=y,
132 z=z
133 }
134 return p
135end
136
137--return a list of all players in the game world as player objects
138local function getAllPos(selector)
139 local result, message = commands.tp("@a["..selector.."]","~ ~ ~")
140 local names = {}
141 if result == true then
142 for i,result in ipairs(message) do
143 local wordpattern = "[^, ]+"
144 local numberpattern = "[%-% ]%d+[%.]%d+"
145 local words,numbers = {},{}
146
147 for word in string.gmatch(result, wordpattern) do
148 table.insert(words,word)
149 end
150
151 for number in string.gmatch(result, numberpattern) do
152 table.insert(numbers,number)
153 end
154
155 local coords = {
156 x = math.floor(numbers[1]),
157 y = math.floor(numbers[2]),
158 z = math.floor(numbers[3])
159 }
160 local name = words[2]
161 table.insert(names,newPlayerData(name,coords.x,coords.y,coords.z))
162 end
163 end
164 return names
165end
166
167
168--returns a list of player objects containing all players who are standing on the given block, and who also are in the given selection
169local function getAllOnBlockType(block,selector)
170 local result, message = commands.exec("execute @a["..selector.."] ~ ~ ~ detect ~ ~-1 ~ "..block.." -1 tp @p[r=1] ~ ~ ~")
171 local names = {}
172 if result == true then
173 for i,result in ipairs(message) do
174 local wordpattern = "[^, ]+"
175 local numberpattern = "[%-% ]%d+[%.]%d+"
176 local words,numbers = {},{}
177
178 for word in string.gmatch(result, wordpattern) do
179 table.insert(words,word)
180 end
181 for number in string.gmatch(result, numberpattern) do
182 table.insert(numbers,number)
183 end
184
185 if numbers[1] and numbers[2] and numbers[3] then
186 local coords = {
187 x = math.floor(numbers[1]),
188 y = math.floor(numbers[2]),
189 z = math.floor(numbers[3])
190 }
191 local name = words[2]
192 table.insert(names,newPlayerData(name,coords.x,coords.y,coords.z))
193 --print("Found a player - getOnBlock")
194 else
195 --print("Error: Coordinate Numbers were missing")
196 end
197 end
198 end
199 return names
200end
201
202---BEGIN HOMECOMER RELATED FUNCTIONS
203
204--gives a player a HOMECOMER egg with their name on it. Removes all spawn eggs first
205local function giveHomecomer(name)
206 commands.clear(name,'spawn_egg')
207
208 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]}}')
209end
210
211--returns a list of HOMECOMER entities and their names
212local function getHomecomers()
213 local result, message = commands.exec("execute @e[type="..registry.HOMECOMER_TYPE.."] ~ ~ ~ tp @e[r=1] ~ ~ ~")
214 local names = {}
215 if result == true then
216 for i,result in ipairs(message) do
217 local wordpattern = "[^, ]+"
218 local numberpattern = "[%-% ]%d+[%.]%d+"
219 local words,numbers = {},{}
220
221 for word in string.gmatch(result, wordpattern) do
222 table.insert(words,word) print(word)
223 end
224 for number in string.gmatch(result, numberpattern) do
225 table.insert(numbers,number)
226 end
227
228 if numbers[1] and numbers[2] and numbers[3] then
229 local coords = {
230 x = math.floor(numbers[1]),
231 y = math.floor(numbers[2]),
232 z = math.floor(numbers[3])
233 }
234 local name = words[4]
235 table.insert(names,newPlayerData(name,coords.x,coords.y,coords.z))
236 --print("Found a player - getOnBlock "..name)
237 else
238 --print("Error: Coordinate Numbers were missing")
239 end
240 end
241 end
242 return names
243end
244
245--takes a list of homecomers and deals with all those players, moving them back to spawn and removing them from game
246--also gives them a new homecomer
247local function dealWithHomecomers(game, homecomers)
248 for _,homecomer in pairs(homecomers) do
249 --remove player from current game if in game
250 removePlayerFromKew(game,homecomer.name)
251 --teleport them back to spawn
252 movePlayerToSpawn(homecomer.name)
253 --give a new homecomer
254 giveHomecomer(homecomer.name)
255 --particle effects
256 --teleport message
257 end
258 --kill all homecomers
259 if #homecomers>0 then
260 commands.tp("@e[type="..registry.HOMECOMER_TYPE.."]",100000,200,100000)
261 commands.kill("@e[type="..registry.HOMECOMER_TYPE.."]")
262 os.sleep(#homecomers*0.2)
263 end
264end
265
266---
267function checkForHomecomers(game)
268 --execute on all homecomers
269 local homecomers = getHomecomers()
270 dealWithHomecomers(game,homecomers)
271end
272
273--- END OF HOMECOMER FUNCTIONS
274
275-- removes a player from a game queue
276local function removePlayerFromKew(game,playername)
277 for _,kew in pairs(game.queues) do
278 for index, player in ipairs(kew.playerlist) do
279 if player.name == playername then
280 table.remove(kew.playerlist,index)
281 end
282 end
283 if #kew.playerlist == 0 and kew.phase == 2 then
284 --game can be ended as no players are left
285 kew.timer = 0
286 end
287 end
288end
289
290--teleports a player ot the spawn area
291local function movePlayerToSpawn(playername)
292 respawnPlayer(playername)
293 commands.async.tp(playername,registry.SPAWN.x,registry.SPAWN.y,registry.SPAWN.z,registry.SPAWN.a1,registry.SPAWN.a2)
294 if registry.ANNOUNCE_ENGLISH then
295 commands.async.tellraw(playername,'["",{"text":"You used your HOMECOMER and TELEPORTED BACK TO SPAWN","color":"white"}]')
296 end
297 if registry.ANNOUNCE_GERMAN then
298 commands.async.tellraw(playername,'["",{"text":"Du hast den HEIMKEHRER benutzt um dich zurück zum Anfang zu teleportieren.","color":"gold"}]')
299 end
300end
301
302--creates a villager with special items
303local function spawnVillager(x,y,z)
304 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"]}}} ]}}')
305end
306
307
308--displays a time as experience points to a selection of players
309local function displayTime(selector,minutes,seconds)
310 --commands.title("@a["..selector.."]","subtitle",'{text:"Time left: '..minutes..":"..seconds..'",color:red,bold:false,underlined:false,italic:false,strikethrough:false,obfuscated:false}')
311 commands.async.xp("-1000000L","@a["..selector.."]")
312 local secondstot = (minutes * 60) + seconds
313 commands.async.xp(tostring(secondstot).."L","@a["..selector.."]")
314end
315
316--simply runs displayTime on a list of players
317local function displayTimeToGroup(playerlist,minutes,seconds)
318 for i,player in ipairs(playerlist) do
319 displayTime("name="..player.name,minutes,seconds)
320 end
321end
322
323--displays a title to a selection of players
324local function displayTitle(selector,text)
325 commands.async.title("@a["..selector.."]","title",'{text:"'..text..'"}')
326end
327
328--simply runs displayTitle on a list of players
329local function displayTitleToGroup(playerlist,text)
330 for i,player in ipairs(playerlist) do
331 displayTitle("name="..player.name,text)
332 end
333end
334
335--teleports a list of players to an exact place and sends them a message about it
336local function teleportToPoint(x,y,z,playerlist,clear,textEN, textDE)
337 for i,player in ipairs(playerlist) do
338 player.x = x
339 player.y = y
340 player.z = z
341 commands.async.gamemode(2,player.name)
342 if clear then
343 commands.async.clear(player.name,"wool")
344 commands.async.clear(player.name,"stone_pickaxe")
345 end
346 commands.tp("@a[name="..player.name.."]",x,y,z)
347 if registry.ANNOUNCE_ENGLISH then
348 commands.async.tellraw(player.name,'["",{"text":"'..textEN..'","color":"white"}]')
349 end
350 if registry.ANNOUNCE_GERMAN then
351 commands.async.tellraw(player.name,'["",{"text":"'..textDE..'","color":"gold"}]')
352 end
353 end
354end
355
356--teleports a list of players to a given buildzone
357local function teleportToZone(buildzone,playerlist,textEN, textDE)
358 teleportToPoint(buildzone.x+2+(buildzone.w/2),buildzone.y+5,buildzone.z+2+(buildzone.w/2),playerlist,true,textEN, textDE)
359
360end
361
362--gives the same list of items to a list of players
363local function giveItems(playerlist,itemlist)
364 print("Giving initial inventory items to players")
365 local given = 0
366 for i,player in ipairs(playerlist) do
367 --commands.async.clear(player.name)
368 for j,item in ipairs(itemlist) do
369 commands.async.give("@a[name="..player.name.."]",item)
370 given = given +1
371 end
372 giveHomecomer(player.name)
373 end
374 print("Giving initial inventory items to players...DONE!")
375 return given
376end
377
378--a multi builder which uses the vocab constructor to create sets of vocab
379local function makeVocabZones(quant,w)
380 local x,y,z = registry.FIRSTVOCAB.x, registry.FIRSTVOCAB.y, registry.FIRSTVOCAB.z
381 local result = {}
382 local id = 1
383 for i=0,quant-1 do
384 for k=0,3 do
385 local zpos = i-4
386 local ypos = k
387 --print("vocab at X")
388 --print(x-(2*w)-6)
389 --print("and Z")
390 --print(z+((w+1)*zpos))
391 local nextVocab = newVocabZone(
392 x-(2*w)-6,y+(ypos*(registry.VOCAB_HEIGHT+3)),
393 z+((w+1)*zpos),
394 w,
395 id,
396 registry.REWARDS[id] or registry.DEFAULT_REWARD,
397 registry.VOCABS_DATA[id].nameEN or registry.DEFAULT_NAME,
398 registry.VOCABS_DATA[id].nameDE or registry.DEFAULT_NAME,
399 registry.VOCABS_DATA[id].typeEN,
400 registry.VOCABS_DATA[id].typeDE,
401 registry.VOCABS_DATA[id].height,
402 registry.VOCABS_DATA[id].slots,
403 registry.VOCABS_DATA[id].green,
404 registry.VOCABS_DATA[id].greenSlots,
405 registry.VOCABS_DATA[id].rewardUrban,
406 registry.VOCABS_DATA[id].rewardCount
407 )
408 table.insert(result,nextVocab)
409 id = id +1
410 end
411 end
412 return result
413end
414
415--finds the next free location(or buildzone) for a new game in a given area.
416--giving force as True will mean it overrides the first location no matter what
417local function findNextLoc(width,zones,force)
418 local x,y,z,locid = 0,0,0,1
419 for i,loc in ipairs(LOCS) do
420 locid = i
421 x,y,z = registry.FIRSTZONE.x+(loc.x*(width+1)),registry.FIRSTZONE.y,registry.FIRSTZONE.z+(-loc.z*(width+1)) -- these are the coordinates of this LOC in the minecraft world
422 --print("testing for available zone at: "..x..", "..y..", "..z)
423 --print("which is at grid cell at: "..loc.x..", "..loc.z)
424 --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
425 local result = true
426 if loc.played then
427 result = false
428 --print("zone has been played")
429 end
430 --print("testing done")
431 --force the first zone to be selected unless it is taken in the "zones" parameter
432 if force then result = true end
433 --checks if the zone is already in the list of unavailable zones passed as parameter
434 local zonefree = true
435 for i,zone in ipairs(zones) do
436 if zone.x == x and zone.z == z then
437 zonefree = false
438 end
439 end
440 --print("next position free is ",loc.x*width,oy,loc.z*width)
441 --if result then print("true") else print("false") end
442 if result and zonefree then
443 --print("using loc: ",loc.x,loc.z)
444 return x,y,z,locid --returns the coordinates of the new zone, plus its id in the LOCS table
445 end
446 end
447 return nil,nil,nil, nil --returns empty if no zone is available
448
449end
450
451--relocates a buildzone to a new coordinate safely
452--avoids overlapping with other buildzones
453function moveBuildzone(buildzone,zones)
454 local x,y,z,locid = findNextLoc(buildzone.w,zones)
455 if x and y and z and locid then
456 --print("moved buildzone from "..buildzone.x..","..buildzone.z.." to "..x..","..y)
457 local w = buildzone.w
458 buildzone.x,buildzone.y,buildzone.z = x,y+registry.BUILDZONE_FLOOR_HEIGHT,z
459 buildzone.locid = locid --reassign the location id corresponding to the LOCS item for the grid cell of the moved zone
460 buildzone.selector = "x="..x..",y="..tostring(y-1)..",z="..z..",dx="..w..",dy=256,dz="..w
461 buildzone.structures = {} --a list of all vocabularies which have been contructed
462 else
463 --print("buildzone at "..buildzone.x..","..buildzone.z.." stayed where it is")
464 end
465end
466
467--multi builder to create sets of buildzones using the buildzone constructor
468local function makeBuildzones(quant,vocab,width,floorHeight)
469 local result = {}
470 for i=1,quant do
471 --print("locating available slot")
472 local x,y,z,locid = findNextLoc(width,result)
473 if x and y and z and locid then
474 print("Created a new Buildzone at",x,y,z)
475 table.insert(result,newBuildZone(x,y+floorHeight,z,width,vocab,locid))
476 else
477 --print("failed to make new buildzone")
478 end
479 end
480
481 local remaining = registry.NUMBER_OF_BUILDZONES - #result
482 --print("doing this remaining thing")
483 for i=1, remaining do
484 local x,y,z,locid = findNextLoc(width,result,true)
485 if x and y and z and locid then
486 --print("forced new buildzone at",x,y,z)
487 table.insert(result,newBuildZone(x,y+floorHeight,z,width,vocab, locid))
488 else
489 --print("failed to force new buildzone")
490 end
491 end
492
493
494 return result
495end
496
497--vocab constructor. Enforces some data structure
498function newVocabZone(x,y,z,w,id, reward,nameEN, nameDE, typeEN, typeDE, height, slots,green,greenSlots, rewardUrban, rewardCount)
499 local nvz = {}
500 nvz.x ,nvz.y ,nvz.z ,nvz.w = x,y,z,w
501
502 nvz.cx = nvz.x - nvz.w - 2
503 nvz.cy = nvz.y
504 nvz.cz = nvz.z
505 nvz.nameEN = nameEN
506 nvz.reward = reward
507 --- new stuff
508 nvz.id = id
509 nvz.nameDE = nameDE
510 nvz.typeEN = typeEN
511 nvz.typeDE = typeDE
512 nvz.height = height
513 nvz.slots = slots
514 nvz.green = green
515 nvz.greenSlots = greenSlots
516 nvz.rewardUrban = rewardUrban
517 nvz.rewardCount = rewardCount
518
519 return nvz
520
521end
522
523--buildzone constructor. Enforces some data structure
524function newBuildZone(x,y,z,w,vocabZones,locid)
525 local nbz = {}
526 nbz.x ,nbz.y ,nbz.z ,nbz.w = x,y,z,w
527 nbz.selector = "x="..x..",y="..(y-5)..",z="..z..",dx="..w..",dy=256,dz="..w
528 nbz.structures = {} --a list of all vocabularies names which have been contructed
529 nbz.buildings = {} --a list of all vocabularies with full data (x,y,z,id,name) which have been contructed
530 nbz.filledSlots = 0 --to count how many slots have been filled with buildings. the matrix is 7x7x20 slots. one slot is 9x9x9 blocks big
531 nbz.greenSlots = 0 --to count how many of the slots are green. the matrix is 7x7x20 slots. one slot is 9x9x9 blocks big
532 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
533 nbz.waitingForCheck = {}
534 nbz.highest = 0
535 nbz.vocab = vocabZones
536 nbz.locid = locid
537 return nbz
538end
539
540--kew constructor. Enforces some data structure
541function newQueue(buildzone,maxplayers)
542 local q = {}
543 q.timer = 1
544 q.phase = 1
545
546 q.victory = false
547 q.phases = {
548 {
549 name = "Selecting Players",
550 length = registry.PHASE_LENGTH_WAIT,
551 displaylength = 15 --this field is not used
552 },
553 {
554 name = "Game In Progress",
555 length = registry.PHASE_LENGTH_GAME,
556 displaylength = 70 --this field is not used
557 },
558 {
559 name = "Round Complete",
560 length = registry.PHASE_LENGTH_OVER,
561 displaylength = 5 --this field is not used
562 }
563 }
564 q.playerlist = {}
565 q.maxplayers = maxplayers
566 q.buildzone = buildzone
567 local timestamp = math.floor(os.clock())
568 q.filename = timestamp.."at"..q.buildzone.x.."_"..q.buildzone.z
569 return q
570end
571
572-- sets up the game field and the game manager objects
573function setup()
574 print("Starting Setup function.")
575 local game = {}
576 game.vocab = {}
577 game.builds = {}
578 game.queues = {}
579 game.waitlist = {}
580 game.spawn = registry.SPAWN
581 game.lastClock = os.clock()
582 game.nowTime = os.clock()
583
584 print("Game object created")
585
586 --vocabzone creation
587 print("Making vocabulary objects.")
588 game.vocab = makeVocabZones(registry.NUMBER_OF_VOCAB,registry.VOCAB_WIDTH)
589
590 print("Testing for properly setup vocabs.")
591 for i,vz in ipairs(game.vocab) do
592 local x,y,z,w = vz.x,vz.y,vz.z,vz.w
593 local cx,cy,cz = vz.cx,vz.cy,vz.cz
594
595 local detector, message1 = commands.testforblock(x+(math.floor(w/2)),y,z+(math.floor(w/2)),registry.BLOCKS.DETECT.block)
596 local blocker, message2 = commands.testforblock(x+(math.floor(w/2)),y,z+(math.floor(w/2)),registry.BLOCKS.DETECT_DEAD.block)
597 if not (detector or blocker) then
598 for nx=0,2 do
599 for nz=0,2 do --- BELOW THERE IS HARDCODED STUFF, MAKE DEPENDENT ON VARIABLES
600 commands.setblock(x+(nx*9)+4,y-1,z+(nz*9)+4,registry.BLOCKS.VOCAB_REPLACE.block)
601 commands.setblock(cx+(nx*9)+4,cy-1,cz+(nz*9)+4,registry.BLOCKS.VOCAB_DETECT.block)
602 end
603 end
604 commands.setblock(x+(math.floor(w/2)),y,z+(math.floor(w/2)),registry.BLOCKS.DETECT_DEAD.block)
605
606 end
607
608 end
609
610 --generate catalogue rewards
611 for i=1,#registry.VOCABS_DATA do
612 if registry.VOCABS_DATA[i].rewardUrban then
613 registry.REWARDS[i] = registry.houseBlock(registry.VOCABS_DATA[i].rewardCount)
614 else
615 registry.REWARDS[i] = registry.gardenBlock(registry.VOCABS_DATA[i].rewardCount)
616 end
617 end
618 print("Catalogue definitions have been imported")
619
620 --kill all villagers
621 commands.exec("kill @e[type=Villager]")
622 print("Villagers destroyed")
623
624 --buildzone creation
625 print("Making building zone objects.")
626 --game.builds appears to store the games currently in progress
627 game.builds = makeBuildzones(registry.NUMBER_OF_BUILDZONES,game.vocab,registry.BUILDZONE_WIDTH,registry.BUILDZONE_FLOOR_HEIGHT)
628
629 for i,build in ipairs(game.builds) do
630 table.insert(game.queues,newQueue(build, registry.MAX_PLAYERS_PER_GAME))
631 end
632
633 ----
634 print("Adding scoreboards.")
635 --print(#registry.VOCABS_DATA)
636 for i=1,#registry.VOCABS_DATA do
637 commands.scoreboard("objectives","add","building_"..i,"dummy")
638 end
639 commands.scoreboard("objectives","add","highscores","dummy","Best Neighbourhoods")
640 if registry.SHOW_HIGHSCORES then
641 commands.scoreboard("objectives","setdisplay","sidebar","highscores")
642 end
643 commands.scoreboard("objectives","add","VillagerLife","dummy")
644 commands.scoreboard("objectives","add","built","dummy", "Structures Built")
645 commands.scoreboard("objectives","add","highest","dummy", "Personal Highest")
646 commands.scoreboard("objectives","add","played","dummy","Games Played")
647 commands.scoreboard("objectives","setdisplay","list","played")
648
649 commands.title("@a","times",0,30,30) -- what does this do?
650
651 ----
652 print("setting the needed preferences of the minecraft world.")
653 commands.gamerule("doDaylightCycle",false) -- this is included in the spawn controller script
654 commands.gamerule("keepInventory",true)
655 --commands.gamerule("doTileDrops",false) -- this is included in the spawn controller script
656 commands.gamerule("sendCommandFeedback", true)
657 commands.gamerule("logAdminCommands",false) -- this is included in the spawn controller script
658 commands.gamerule("commandBlockOutput",false) -- this is included in the spawn controller script
659 commands.time("set",6000) -- this is included in the spawn controller script
660
661 ---
662 math.randomseed( os.time() )
663 print("Computer clock is: "..os.clock())
664
665 if DEBUG_MODE then
666 for i,build in ipairs(game.builds) do
667 build.phase = 2
668 build.timer = 500
669 end
670 end
671
672 local ox,oy,oz = commands.getBlockPosition()
673 print("Computer Co-ordinates ",ox,oy,oz)
674 print("20.000 BLOCKS is ready to run!")
675
676 return game
677end
678
679--main game loop
680--runs the game object through each of these update steps in order
681function update(game)
682 local elapsed = updateClock(game)
683 --update players
684 checkPlayers(game)
685 doTimerUpdates(game,elapsed)
686 doPhaseUpdates(game)
687 doPhaseEnds(game)
688 checkBoundaries(game)
689 checkForHomecomers(game)
690 if #game.waitlist > 0 then allocateWaiters(game) end
691end
692
693--calculates elapsed time during a game tick
694function updateClock(game)
695 game.nowTime = os.clock()
696 local elapsed = game.nowTime - game.lastClock
697 game.lastClock = game.nowTime
698 return elapsed
699end
700
701--updates all kews in the game object based on elapsed time
702function doTimerUpdates(game,elapsed)
703 for i,kew in ipairs(game.queues) do
704 kew.timer = kew.timer - elapsed
705 end
706end
707
708--check players are inside their buildzone and move them back if not
709function checkBoundaries(game)
710 for i,kew in ipairs(game.queues) do
711 if kew.phase ==2 then
712 --boundaries
713 local x_min = kew.buildzone.x
714 local x_max = kew.buildzone.x+kew.buildzone.w
715 local z_min = kew.buildzone.z
716 local z_max = kew.buildzone.z+kew.buildzone.w
717
718 --local toBeCorrected = {}
719
720 for j,player in ipairs(kew.playerlist) do
721 local listOfOne = getAllPos('m=2,name='..player.name)
722 if listOfOne and listOfOne[1] then
723 player.x = listOfOne[1].x
724 player.y = listOfOne[1].y
725 player.z = listOfOne[1].z
726 local changed = false
727 if player.x > x_max then
728 changed = true
729 player.x = x_max-2
730 end
731 if player.x < x_min then
732 changed = true
733 player.x = x_min+2
734 end
735 if player.z > z_max then
736 changed = true
737 player.z = z_max-2
738 end
739 if player.z < z_min then
740 changed = true
741 player.z = z_min+2
742 end
743 if changed then
744 teleportToPoint(player.x,kew.buildzone.y,player.z,{player},false,
745 "TELEPORTED BACK TO GAME: Please stay inside the building zone or use HOMECOMER to leave the game!",
746 "Zurück ins Spiel teleportiert: Bitte bleib innerhalb des Baufeldes oder nutze den HEIMKEHRER um das Spiel zu verlassen!")
747 end
748 end
749 end
750 end
751 end
752end
753
754
755--here you can set the logic which determines if a buildzone was valuable.
756--Return true if it is crap and should be replaced
757function checkIfBuildzoneIsCrap(buildzone)
758 if #buildzone.structures < registry.MIN_BUILDINGS then
759 print("Buildzone was crap")
760 return true
761 end
762 print("Buildzone was ok")
763 return false
764end
765
766
767--Everything that happens after a buildzone was completed
768--due to time limit.
769function cleanAfterGameOver(kew)
770 local buildzone = kew.buildzone
771 commands.async.setblock(buildzone.x,buildzone.y,buildzone.z,registry.BLOCKS.VICTORY_MARKER.block)
772 fillRing(buildzone.x,buildzone.y,buildzone.z,buildzone.w,"minecraft:air",0,"replace",registry.BLOCKS.CONSTRUCTION.block,registry.BLOCKS.CONSTRUCTION.data)
773 --for each level remove playing blocks like detectors
774 for h=0,256-buildzone.y do
775 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.data)
776 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.data)
777 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.data)
778 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.data)
779 end
780 --replaces air on the bottom level with flooring to show the area is completed
781 commands.async.fill(buildzone.x,buildzone.y-1,buildzone.z,buildzone.x+buildzone.w,buildzone.y-1,buildzone.z+buildzone.w,registry.BLOCKS.CAMP_FLOOR.block,registry.BLOCKS.CAMP_FLOOR.data,"replace",registry.BLOCKS.PLUG.block,registry.BLOCKS.PLUG.data)
782 commands.async.fill(buildzone.x,buildzone.y,buildzone.z,buildzone.x+buildzone.w,buildzone.y,buildzone.z+buildzone.w,registry.BLOCKS.PHVFLOOR.block,registry.BLOCKS.PHVFLOOR.data,"replace","minecraft:air","0")
783
784 ---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
785
786 local wasCrap = checkIfBuildzoneIsCrap(buildzone)
787 local gameovermessageEN
788 local gameovermessageDE
789 if wasCrap then
790 --mark this buildzone for replacement
791 --change the flag to played=false
792 updateZonePlayedState(buildzone,false)
793 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"
794 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"
795
796 else
797 --change the flag to played=true
798 updateZonePlayedState(buildzone,true)
799
800 gameovermessageEN = "Thank you for playing IBA_GAME! Play another game or look for your game result at: www.20000blocks.com"
801 gameovermessageDE = "Vielen Dank, dass du IBA_GAME gespielt hast! Starte eine neue Runde oder schau dir deine Spielergebnisse an unter: www.20000blocks.com"
802 end
803
804 for _, player in ipairs(kew.playerlist) do
805 if registry.ANNOUNCE_ENGLISH then
806 -- announce success in English
807 commands.async.tellraw(player.name,'["",{"text":"'..gameovermessageEN..'","color":"white"}]')
808 end
809 if registry.ANNOUNCE_GERMAN then
810 -- announce success in German
811 commands.async.tellraw(player.name,'["",{"text":"'..gameovermessageDE..'","color":"gold"}]')
812 end
813 end
814
815end
816
817function updateZonePlayedState(buildzone, newstate)
818 --change the flag to played=newstate
819 LOCS[buildzone.locid].played = newstate
820 --and write the LOCS object to the json file
821 writeGridToFile()
822end
823
824--these happen every tick and require the game object
825--updates are performed on each Queue (kew) and within each
826--of those on each buildzone.
827function doPhaseUpdates(game)
828 for i,kew in ipairs(game.queues) do
829
830 local minutes = string.format("%02d",math.floor(kew.timer/60))
831 local seconds = string.format("%02d",math.floor(kew.timer - (minutes*60)))
832 if kew.timer <= 0 then
833 minutes = "00"
834 seconds = "00"
835 end
836
837 if kew.phase == 1 then
838 --waiting phase
839 if #kew.playerlist == kew.maxplayers and kew.timer > 5 then kew.timer = 5 end
840 if not DEBUG_MODE and #kew.playerlist == 0 then kew.timer = kew.phases[1].length end
841
842 displayTitleToGroup(kew.playerlist,"Game starting!")
843 displayTimeToGroup(kew.playerlist,minutes,seconds)
844 --show countdown
845 elseif kew.phase == 2 then
846 --playing phase
847 if #kew.playerlist == 0 then timer = 0 end -- finish if all players quit
848 -- do vocab logic
849 local victory = updatePlayedZone(kew) --currently victory updatePlayedZone returns always false
850 --
851 displayTimeToGroup(kew.playerlist,minutes,seconds)
852
853 elseif kew.phase == 3 then
854 --end phase
855 displayTitleToGroup(kew.playerlist,"Use HOMECOMER to return")
856 --displayTimeToGroup(kew.playerlist,minutes,seconds)
857
858
859 end
860 end
861end
862
863--this runs after a buildzone is completed
864--it should tally structure types and set scoreboard highscores
865--it also rewards all participants with more played score
866function processHighscores(kew)
867 local buildzone = kew.buildzone
868
869
870
871 --add score to players who finished this game
872 for _,player in ipairs(kew.playerlist) do
873 commands.async.scoreboard("players","add",player.name,"played",1)
874 end
875end
876
877--function to export a buildzone detail once it is complete
878--requires a kew so it can access the playerlist
879--the saveOnline is a bool flag true: save the game also online to the database; false: save only in a file locally
880local function exportKewData(kew, saveOnline)
881 local buildzone = kew.buildzone
882 local saved = {}
883 saved.position =
884 {
885 x=buildzone.x,
886 y=buildzone.y,
887 z=buildzone.z
888 }
889
890 saved.players = {}
891 for _, player in ipairs(kew.playerlist) do
892 table.insert(saved.players,player.name)
893 end
894 --saved.structures = buildzone.structures
895 saved.buildings = buildzone.buildings
896 saved.totals = tracker.tallyTable(buildzone.structures)
897 --saved.highest = buildzone.highest
898 saved.stats = {
899 cityVersion = registry.CITY_VERSION,
900 height = buildzone.highest,
901 densityIndex = math.floor(100*buildzone.filledSlots/49), -- the density index is made from built area (filledSlots) over the ground area (7x7 slots = 49)
902 greenIndex = math.floor(100*buildzone.greenSlots/49), --the green index is made from green area (greenSlots) over the ground area (7x7 slots = 49)
903 variety = tablelength(buildzone.variety),
904 timeCompleted = timestamp,
905 gameLength = registry.PHASE_LENGTH_GAME
906 }
907
908 fs.makeDir("/records")
909 local file = fs.open("/records/"..kew.filename..".json","w")
910 filecontent = json.encodePretty(saved)
911 file.write(filecontent)
912 file.close()
913
914 if saveOnline then --for now this is disabled until we figure out how things look in the webGL viewer
915 --writeToDatabase(kew.filename,filecontent,saved.position.x,saved.position.z)
916 end
917end
918
919-- this function writes to the online database that we use to display models in the webGL viewer
920local function writeToDatabase(name,data,x,z)
921
922 -- the user agent needs to be renamed otherwise the dfeult one is Java and that is blocked by the .htaccess file on the website
923 local headers = {
924 [ "User-Agent" ] = "20.000 BLOCKS"
925 }
926
927 local link = http.post(
928 "http://www.20000blocks.com/DatabaseAccess/UploadModel.php",
929 "name="..textutils.urlEncode(name).."&"..
930 "content="..textutils.urlEncode(data).."&"..
931 "x="..x.."&"..
932 "z="..z,
933 headers
934 )
935 local linkURL = link.readAll()
936 if linkURL then
937 --message texts
938 local msg_EN = 'The latest game result has been uploaded to the webviewer.\n'
939 local msg_DE = 'Die neueste Runde wurde in den Webviewer geladen.\n'
940 local linkText_EN = 'See it and share it!'
941 local linkText_DE = 'Schau es und teile es!'
942 local hoverText_EN = 'Click here to see and share the last game!'
943 local hoverText_DE = 'Click here to see and share the last game!'
944 --message text with the link
945 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}]'
946
947 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}]'
948 -- announce success in English
949 commands.async.tellraw("@a",linkmsg_EN)
950 if registry.ANNOUNCE_GERMAN then
951 --announce success in German
952 commands.async.tellraw("@a",linkmsg_DE)
953 end
954 end
955end
956
957function 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)
958 http.post(
959 "http://www.20000blocks.com/DatabaseAccess/SwitchVersion.php",
960 "id="..textutils.urlEncode(id).."&"..
961 "relative="..textutils.urlEncode(relative)
962 )
963end
964
965--this code runs ONCE at the end of each phase
966--what actually happens is specific to which phase the
967--particular kew is in.
968function doPhaseEnds(game)
969 for i,kew in ipairs(game.queues) do
970 if kew.timer <= 0 then
971 if kew.phase == 1 then
972 --waiting phase ends goto play phase
973
974 moveBuildzone(kew.buildzone,game.builds) --what does this do?
975 teleportToZone(kew.buildzone,kew.playerlist,"Your game has started! BUILD A HOUSE!", "Das Spiel hat begonnen! BAUE EIN HAUS!")--teleport selected players
976 if cleanbuildzone then
977 cleanBuildzone(kew.buildzone)
978 prepareBuildzone(kew.buildzone)--prepare build zone
979 else
980 print("buildzone is left as it is. to clean it use: <play.lua true>")
981 end
982 giveItems(kew.playerlist,registry.STARTING_ITEMS) --give starting items
983 displayTitle(kew.buildzone.selector,"BUILD!")
984 kew.victory = false
985 --displayTime(kew.buildzone.selector,0,0)
986 kew.phase = 2
987 kew.timer = kew.phases[kew.phase].length
988 elseif kew.phase == 2 then
989 --playing phase ends goto end phase
990 processHighscores(kew)
991 exportKewData(kew,true) -- saves the final state of the game and writes it to the online database
992 cleanAfterGameOver(kew)
993 kew.phase = 3
994 --displayTime(kew.buildzone.selector,0,0)
995 kew.timer = kew.phases[kew.phase].length
996 elseif kew.phase == 3 then
997 --end phase ends goto waiting phase
998 removePlayersFromKew(game,kew)
999 kew.phase = 1
1000 --displayTime(kew.buildzone.selector,0,0)
1001 kew.timer = kew.phases[kew.phase].length
1002 end
1003 end
1004 end
1005end
1006
1007
1008--Replaces everything that is needed to start the game. Does not rebuild the floor, or clear anything away.
1009--based on the settings it creates a full grid, or a partial grid, or no grid
1010--it also places the ring, although this is disabled for now
1011function prepareBuildzone(buildzone)
1012 print("Preparing buildzone")
1013 local bz = buildzone
1014 local x,y,z,w = bz.x,bz.y,bz.z,bz.w
1015
1016 commands.async.fill(x,y-1,z,x+w,y-1,z+w,registry.BLOCKS.CAMP_FLOOR.block) --place the white grid accross the full buildzone as a base
1017
1018 fillRing(buildzone.x,buildzone.y-1,buildzone.z,buildzone.w,registry.BLOCKS.CONSTRUCTION.block) --this draws the construction stripe around the buildzone
1019 --create the grid of detectors surrounded by plus plugs
1020 if registry.DO_GRID then
1021 for x=0,registry.GRIDCELL_COUNT-1 do
1022 for z=0,registry.GRIDCELL_COUNT-1 do
1023 local rand = math.random()*100
1024 if rand > registry.GRID_HOLE_CHANCE then --and result then
1025 local halfCell = math.floor(registry.GRIDCELL_SIZE/2)
1026 commands.async.setblock(bz.x+(x*registry.GRIDCELL_SIZE)+halfCell,bz.y,bz.z+(z*registry.GRIDCELL_SIZE)+halfCell,registry.BLOCKS.DETECT.block,registry.BLOCKS.DETECT.data,"replace","minecraft:air")
1027 commands.async.fill(bz.x+(x*registry.GRIDCELL_SIZE)+halfCell-registry.PLUG_LENGTH, bz.y-1,bz.z+(z*registry.GRIDCELL_SIZE)+halfCell,bz.x+(x*registry.GRIDCELL_SIZE)+halfCell+registry.PLUG_LENGTH,bz.y-1,bz.z+(z*registry.GRIDCELL_SIZE)+halfCell,registry.BLOCKS.PLUG.block)
1028 commands.async.fill(bz.x+(x*registry.GRIDCELL_SIZE)+halfCell,bz.y-1,bz.z+(z*registry.GRIDCELL_SIZE)+halfCell-registry.PLUG_LENGTH,bz.x+(x*registry.GRIDCELL_SIZE)+halfCell,bz.y-1,bz.z+(z*registry.GRIDCELL_SIZE)+halfCell+registry.PLUG_LENGTH,registry.BLOCKS.PLUG.block)
1029 end
1030 end
1031 end
1032 end
1033 --mark the game in the LOCS array as not available anymore, and save the updated game grid to the grid file
1034 --change the flag to played=true
1035 updateZonePlayedState(buildzone,true)
1036 print("Preparing buildzone...DONE!")
1037end
1038
1039--deletes everything inside the buildzone
1040function cleanBuildzone(buildzone)
1041 print("Cleaning buildzone")
1042 --for each level, remove all blocks
1043 for h=buildzone.y,255 do
1044 commands.async.fill(buildzone.x,h,buildzone.z,buildzone.x+buildzone.w,h,buildzone.z+buildzone.w,"minecraft:air")
1045 end
1046 --remove all floating items in the buildzone
1047 commands.async.kill("@e[type=item]")
1048 print("Cleaning buildzone...DONE!")
1049end
1050
1051function removePlayersFromKew(game,kew)
1052 for _, player in ipairs(kew.playerlist) do
1053 if registry.ANNOUNCE_ENGLISH then
1054 -- announce success in English
1055 commands.async.tellraw(player.name,'["",{"text":"TIME OUT! GAME COMPLETE! Use your HOMECOMER to return to spawn.","color":"white"}]')
1056 end
1057 if registry.ANNOUNCE_GERMAN then
1058 -- announce success in German
1059 commands.async.tellraw(player.name,'["",{"text":"ENDE! SPIEL ABGESCHLOSSEN! Nutze den HEIMKEHRER um zum Anfang zurück zu kehren.","color":"gold"}]')
1060 end
1061 end
1062 kew.playerlist = {}
1063end
1064
1065function respawnPlayer(playername,message)
1066 commands.tell(playername,message)
1067 commands.async.gamemode(2,playername)
1068 commands.async.clear(playername,"minecraft:wool")
1069 commands.async.clear(playername,"minecraft:stone_pickaxe")
1070end
1071
1072function checkForPlayerInBuildzone(player,buildzone)
1073 local result,message = commands.testfor('@a[name='..player.name..','..buildzone.selector..']')
1074 return result
1075end
1076
1077function checkForPlayerInWaitzone(player)
1078 local selector = "x="..registry.WAITZONE.x..",y="..registry.WAITZONE.y..",z="..registry.WAITZONE.z..",dx="..registry.WAITZONE.w..",dy=256,dz="..registry.WAITZONE.l
1079 local result,message = commands.testfor('@a[name='..player.name..','..selector..']')
1080 return result
1081end
1082
1083function checkPlayers(game)
1084 local selector = "x="..registry.WAITZONE.x..",y="..registry.WAITZONE.y..",z="..registry.WAITZONE.z..",dx="..registry.WAITZONE.w..",dy=256,dz="..registry.WAITZONE.l
1085 local loggedIn = getAllPos('m=2,'..selector)
1086 --refresh waitlist
1087 game.waitlist = loggedIn
1088 --check currently playing players
1089 for l,kew in ipairs(game.queues) do
1090 for i,builder in ipairs(kew.playerlist) do
1091 local isPlaying = checkForPlayerInBuildzone(builder,kew.buildzone)
1092 --remove players who are already in kews from the waitlist
1093 for j, player in ipairs(loggedIn) do
1094 if player.name == builder.name then
1095 table.remove(loggedIn,j)
1096 end
1097 end
1098 --if the game is in progress and the player is not found then remove them from the gamekew
1099 if not isPlaying and kew.phase == 2 then
1100 --table.remove(kew.playerlist,i)
1101 --print("Removed "..builder.name.." from game in progress")
1102 end
1103 end
1104 end
1105end
1106
1107--adds players who wait in the orange room to slots in waiting buildzones
1108function allocateWaiters(game)
1109 --find free slots
1110 local freeslots = {}
1111 for i, kew in ipairs(game.queues) do
1112 if kew.phase == 1 and #kew.playerlist < kew.maxplayers then
1113 local slots = kew.maxplayers - #kew.playerlist
1114 for j=1,slots do
1115 table.insert(freeslots,kew)
1116 end
1117 end
1118 end
1119
1120 --RE-ENABLE SECOND SHUFFLETABLE IF YOU WANT RANDOM PLAYER MATCHUPS
1121 shuffleTable(game.waitlist)
1122 --shuffleTable(freeslots)
1123
1124 while #freeslots > 0 and #game.waitlist > 0 do
1125 local player = table.remove(game.waitlist,1)
1126 local freeslot = table.remove(freeslots,1).playerlist
1127 table.insert(freeslot,player)
1128 end
1129end
1130
1131
1132
1133--makes sure that incoming check requests dont exist already
1134function addToChecklist(player,buildzone)
1135 for _, detector in ipairs(buildzone.waitingForCheck) do
1136 if detector.x == player.x and detector.y == player.y and detector.z == player.z then
1137 return false
1138 end
1139 end
1140 table.insert(buildzone.waitingForCheck,player)
1141 return true
1142end
1143
1144--removes all barrier blocks from a buildzone which are used to carve space in vocabs
1145function cleanBarriers(buildzone)
1146 for h=0,200 do
1147 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")
1148 end
1149end
1150
1151--The main chunk of code which deals with stepping on detector blocks and reading the vocab
1152function updatePlayedZone(kew)
1153 local buildzone = kew.buildzone
1154 local victory = false
1155 local buildzoneSelector = buildzone.selector
1156 --get all players on a detector block, add them to the list of things to check
1157 local detectLocations = getAllOnBlockType(registry.BLOCKS['DETECT'].block,buildzoneSelector)
1158 --print(#detectLocations.." Players standing on detectors")
1159 for _, player in ipairs(detectLocations) do
1160 addToChecklist(player,buildzone)
1161 end
1162
1163 --DEAL WITH THE DETECTOR AT THE TOP OF THE LIST IF THERE IS ONE
1164 if #buildzone.waitingForCheck > 0 then
1165 --DO PARTICLE EFFECTS IF A DETECTING BLOCK THAT IS DETECTING
1166 for i,loc in ipairs(buildzone.waitingForCheck) do
1167 searchParticle(loc.x,loc.y+1,loc.z)
1168 end
1169 local totalResult = false
1170 local checked = table.remove(buildzone.waitingForCheck,1)
1171 local x,y,z,name = checked.x,checked.y,checked.z,checked.name
1172 for i,vocab in pairs(buildzone.vocab) do
1173 local result,message = commands.testforblocks( vocab.x, vocab.y, vocab.z, vocab.x+vocab.w, vocab.y+registry.VOCAB_HEIGHT, vocab.z+vocab.w, x-math.floor(vocab.w/2), y-1, z-math.floor(vocab.w/2),"masked")
1174 if result then
1175 --clone in the correct vocab
1176 local cloneres,clonemes = commands.clone( vocab.cx, vocab.cy, vocab.cz, vocab.cx+vocab.w, vocab.cy+registry.VOCAB_HEIGHT, vocab.cz+vocab.w, x-math.floor(vocab.w/2), y-1, z-math.floor(vocab.w/2),"masked")
1177 if DEBUG_MODE then
1178 print(clonemes[1])
1179 end
1180 commands.async.give(name,vocab.reward)
1181 -- announce vocab success in English
1182 local rewardType = 'nature'
1183 local rewardTypeDE = 'grüne'
1184 if vocab.rewardUrban then
1185 rewardType = 'urban'
1186 rewardTypeDE = 'urbane'
1187 end
1188
1189 if registry.ANNOUNCE_ENGLISH then
1190 --announce success in English
1191 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"}]')
1192 end
1193 if registry.ANNOUNCE_GERMAN then
1194 -- announce vocab success in German
1195 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"}]')
1196 end
1197
1198 --clear out barrier blocks
1199 cleanBarriers(buildzone)
1200
1201 --ADD THE NEW STRUCTURE TO THE RECORDS
1202 table.insert(buildzone.structures,vocab.nameEN)
1203 --record the place of the vocab as x, y, z and vocab id
1204 local building = {id=vocab.id,xpos=x,ypos=y,zpos=z,name=vocab.nameEN,time=os.clock(),player=name}
1205 table.insert(buildzone.buildings, building)
1206 --if vocab.green then
1207 buildzone.greenSlots = buildzone.greenSlots + vocab.greenSlots
1208 --end
1209 buildzone.filledSlots = buildzone.filledSlots + vocab.slots
1210 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
1211 if newHeight > buildzone.highest then
1212 buildzone.highest = newHeight
1213 end
1214 -- count variety
1215 --print("adding variety: "..vocab.id)
1216 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
1217 --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
1218 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
1219 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
1220 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
1221 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
1222 if varietyId ~= 17 and varietyId ~= 18 then --skip the two riser as they ar enot buildings
1223 if buildzone.variety[varietyId] then
1224 --print("increasing existing item")
1225 buildzone.variety[varietyId] = buildzone.variety[varietyId] + 1
1226 else
1227 --print("adding new item")
1228 buildzone.variety[varietyId] = 1
1229 end
1230 end
1231
1232 --save the game incrementally
1233 exportKewData(kew,false) -- saves the new state of the game locally to a file, doenst write to the database yet
1234
1235 --- CHECK FOR PERSONAL RECORDS
1236 --- check if the new structure is the highest
1237 --- CHANGE here to live detect the contribution of the new structure to the 4 goals and update them
1238
1239 local personalbest = tracker.getScore(name,"highest")
1240 if personalbest.count < newHeight then
1241 --commands.async.tell(name,"You just topped your personal record for highest structure!")
1242 commands.async.scoreboard("players","add",name,"highest",1)
1243 if registry.ANNOUNCE_ENGLISH then
1244 -- announce success in English
1245 commands.async.tellraw(name,'["",{"text":"You just topped your personal record for highest neighbourhood!","color":"green"}]')
1246 end
1247 if registry.ANNOUNCE_GERMAN then
1248 -- announce success in German
1249 commands.async.tellraw(name,'["",{"text":"Du hast soeben deinen persönlichen Rekord für die höchste Nachbarschaft gebrochen!","color":"gold"}]')
1250 end
1251 end
1252
1253 ---
1254 ---
1255 -- CHECK if placing the current structure would result in beating a server-wide record on the 4 goals
1256 --calculate total slots - FOR GOAL "DENSEST NEIGHBOURHOOD"
1257 local most = tracker.getScore("Densest_[points]","highscores")
1258 local Kint = math.floor(100 * buildzone.filledSlots / 49) -- Kint is the density index made from built area (filledSlots) over ground area (7x7 slots = 49)
1259 if Kint > most.count then
1260 commands.async.scoreboard("players","set","Densest_[points]","highscores",Kint)
1261 if registry.ANNOUNCE_ENGLISH then
1262 -- announce success in English
1263 commands.async.tellraw("@a",'["",{"text":"Great! '..name.. ' just topped the record for the DENSEST NEIGHBOURHOOD!","color":"green"}]')
1264 end
1265 if registry.ANNOUNCE_GERMAN then
1266 -- announce success in German
1267 commands.async.tellraw("@a",'["",{"text":"Sehr gut! '..name.. ' hat einen neuen Rekord für die DICHTESTE NACHBARSCHAFT aufgestellt!","color":"gold"}]')
1268 end
1269 end
1270
1271 -- FOR THE GOAL "MOST DIVERSE NEIGHBOURHOOD"
1272 -- here we need to count how many varieties of buildings there are
1273 --local structures = tracker.tallyTable(buildzone.structures) -- this counts the variety of buildings in a game
1274 local mostDiverse = tracker.getScore("Most-Diverse_[out-of-26]","highscores")
1275 local typeCount = tablelength(buildzone.variety)
1276 --print("variety count is: "..typeCount)
1277 if typeCount > mostDiverse.count then
1278 commands.async.scoreboard("players","set","Most-Diverse_[out-of-26]","highscores", typeCount)
1279 if registry.ANNOUNCE_ENGLISH then
1280 -- announce success in English
1281 commands.async.tellraw("@a",'["",{"text":"Wow! '..name.. ' just topped the record for the MOST DIVERSE NEIGHBOURHOOD!","color":"green"}]')
1282 end
1283 if registry.ANNOUNCE_GERMAN then
1284 -- announce success in German
1285 commands.async.tellraw("@a",'["",{"text":"Wow! '..name.. ' hat soeben einen neuen Rekord für die VIELSEITIGSTE NACHBARSCHAFT aufgestellt!","color":"gold"}]')
1286 end
1287 end
1288
1289 -- FOR THE GOAL "GREENEST NEIGHBOURHOOD"
1290 -- here we need to count the number of green vocabs
1291 local greenest = tracker.getScore("Greenest_[points]","highscores")
1292 local Gint = math.floor(100*buildzone.greenSlots/49) --Gint is the green index, made from green area (greenSlots) over ground area (7x7 slots = 49)
1293 if Gint > greenest.count then
1294 commands.async.scoreboard("players","set","Greenest_[points]","highscores",Gint)
1295 if registry.ANNOUNCE_ENGLISH then
1296 -- announce success in English
1297 commands.async.tellraw("@a",'["",{"text":"Awesome! '..name.. ' just topped the record for the GREENEST NEIGHBOURHOOD!","color":"green"}]')
1298 end
1299 if registry.ANNOUNCE_GERMAN then
1300 -- announce success in German
1301 commands.async.tellraw("@a",'["",{"text":"Klasse! '..name.. ' hat einen neuen Rekord für die GRÜNSTE NACHBARSCHAFT aufgestellt!","color":"gold"}]')
1302 end
1303 end
1304
1305 --calculate highest placement -- FOR THE GOAL "TALLEST NEIGHBOURHOOD"
1306 local highest = tracker.getScore("Tallest_[meters]","highscores")
1307 if buildzone.highest > highest.count then
1308 commands.async.scoreboard("players","set","Tallest_[meters]","highscores",buildzone.highest)
1309 if registry.ANNOUNCE_ENGLISH then
1310 -- announce success in English
1311 commands.async.tellraw("@a",'["",{"text":"Incredible! '..name..' just topped the record for TALLEST NEIGHBOURHOOD!","color":"green"}]')
1312 end
1313 if registry.ANNOUNCE_GERMAN then
1314 -- announce success in German
1315 commands.async.tellraw("@a",'["",{"text":"Unglaublich! '..name..' hat einen neuen Rekord für die HÖCHSTE NACHBARSCHAFT aufgestellt!","color":"gold"}]')
1316 end
1317 end
1318
1319
1320 --increase the "how many structures did i build" score for the building player
1321 commands.async.scoreboard("players","add",name,"built",1)
1322 commands.async.scoreboard("players","add",name,"building_"..vocab.id,1)
1323
1324 totalResult = true
1325 break
1326
1327 end
1328 end
1329 if totalResult then
1330 --yey win, do a happy time
1331 successParticle(x,y,z)
1332 else
1333 --no vocab found so do a fail particle
1334 --announce in English
1335 commands.async.tellraw(name,'["",{"text":"The shape you have built does not match any shape in the catalogue, try a different one.","color":"red"}]')
1336 if registry.ANNOUNCE_GERMAN then
1337 -- announce in German
1338 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"}]')
1339 end
1340 failParticle(x,y-1,z)
1341 end
1342 end
1343 return victory
1344end
1345
1346--Display game information on the monitor
1347function debugDisplay(game)
1348 monitor.clear()
1349 if blink then
1350 monitor.setCursorPos(1,1)
1351 monitor.setTextColor(colors.red)
1352 monitor.write("Running")
1353 monitor.setTextColor(colors.white)
1354 redstone.setOutput("top",true)
1355 blink = false
1356 else
1357 redstone.setOutput("top",false)
1358 blink = true
1359 end
1360 local line = 2
1361
1362 for i,kew in ipairs(game.queues) do
1363 monitor.setCursorPos(1,line)
1364 local minutes = string.format("%02d",math.floor(kew.timer/60))
1365 local seconds = string.format("%02d",math.floor(kew.timer - (minutes*60)))
1366 monitor.write("Buildzone "..i.." | Phase: "..kew.phase.." | Time: "..minutes..":"..seconds)
1367 monitor.setCursorPos(1,line+1)
1368 for i,player in ipairs(kew.playerlist) do
1369 monitor.write(player.name.." ")
1370 end
1371 line = line +2
1372 end
1373
1374 monitor.setCursorPos(1,10)
1375 for i,player in ipairs(game.waitlist) do
1376 monitor.write(player.name.." ")
1377 end
1378end
1379
1380
1381-- the main function
1382function MAIN()
1383 local symbols = {
1384 "(X) ",
1385 "(-) "
1386 }
1387 local spin = 1
1388
1389
1390 term.clear()
1391 term.setTextColor(colors.blue)
1392 print("Starting 20.000 Blocks")
1393 print("Version: "..VERSION)
1394 term.setTextColor(colors.white)
1395
1396
1397 local tArgs = { ... } -- get the command line arguments
1398
1399 if #tArgs == 1 then
1400 cleanbuildzone = tArgs[1]
1401 elseif #tArgs > 1 then
1402 print("Usage: play <true(cleans buildzone)/false(default, doesnt clean)>")
1403 return
1404 end
1405
1406 --sets where and how big the PHV area is
1407 --second number is along the long edge of PHV starting at the spawn side
1408 --first number is along the short edge of PHV starting from the park side
1409 if DEBUG_MODE then
1410
1411 registry.FIRSTZONE = registry.DEBUG_ZONE
1412
1413 LOCS = buildGrid(1,1)
1414 else
1415 LOCS = buildGrid(11,27)
1416 end
1417
1418 local blink = true
1419 local game = setup()
1420 while true do
1421 term.clear()
1422 term.setTextColor(colors.green)
1423 print("20,000 Blocks is active "..symbols[spin])
1424 term.setTextColor(colors.white)
1425 spin = spin +1
1426 if spin > #symbols then spin = 1 end
1427
1428
1429 update(game)
1430
1431 commands.async.weather("clear",10000)
1432
1433 --[[
1434 if monitor then
1435 debugDisplay(game)
1436 end
1437 local resetbutton = redstone.getInput("left")
1438 if resetbutton then
1439 print("reset!")
1440 for i,kew in ipairs(game.queues) do
1441 if kew.phase == 2 then
1442 kew.timer = 5
1443 end
1444 end
1445 end
1446 ]]--
1447 sleep(0.1)
1448 end
1449end
1450
1451-- run the game
1452MAIN()