· 8 years ago · Dec 20, 2017, 10:54 AM
1VERSION_NUMBER = '2.6.0'
2VERSION_NAME = 'Multi-Waitus no-wait Single-Waitus'
3os.loadAPI('utils')
4os.loadAPI('registry')
5os.loadAPI('tracker')
6os.loadAPI('json')
7
8--make game to autostart
9--fix portal shapes
10--players must get correct starting materials and be able to place them
11--set spawn point ?
12--clear experience points upon using the homecomer or reseting
13--restore creative to those who had it
14--if game is over, a new one cannot be started until all players use the homecomer
15
16
17--contains a list of all posible Buildzones (built a bit later)
18local LOCS = {}
19local cleanbuildzone = false
20local debug_log = {} --keep all messages of debug log here
21local catalogue_elements = {} --keep the initialized catalogue elements here instead of in the game object to avoid recursive containment in the table "game"
22local buildzones = {} --keep the initialized buildzones here instead of in the game object to avoid recursive containment in the table "game"
23
24local number_of_players_adventure --will keep the number of players in adventure mode on the server
25local number_of_players_creative --will keep the number of players in creative mode on the server
26
27----UTILITY FUNCTIONS
28
29local function debugT(message) --, print)
30 --local fullmsg = json.encode(debug.traceback())..": "..message
31 ---print(debug.getinfo(1, "n").name)
32 local fullmsg = message
33 --print("logging...:"..fullmsg)
34 if registry.DEBUG_MODE then
35 term.setTextColor(colors.red)
36 --print("DEBUG: "..fullmsg)
37 term.setTextColor(colors.white)
38 end
39 local timestamp = os.clock()
40 table.insert(debug_log, {time=timestamp,message=fullmsg})
41
42 local logfilename = "log.txt"
43
44 --save message to logfile
45 if #debug_log == 1 then
46 --if it is the first message then reset the log file
47 local file = fs.open(logfilename, "w")
48 file.write(timestamp..": "..fullmsg.."\n")
49 file.close()
50 else
51 --append it to the log file
52 local file = fs.open(logfilename, "a")
53 file.write(timestamp..": "..fullmsg.."\n")
54 file.close()
55 end
56 --]]
57 --print("logged:"..fullmsg)
58end
59
60local function BitAND(a,b)--Bitwise and
61 local p,c=1,0
62 while a>0 and b>0 do
63 local ra,rb=a%2,b%2
64 if ra+rb>1 then c=c+p end
65 a,b,p=(a-ra)/2,(b-rb)/2,p*2
66 end
67 return c
68end
69
70function lshift(x, by)
71 return x * 2 ^ by
72end
73
74function rshift(x, by)
75 return math.floor(x / 2 ^ by)
76end
77
78--sort table by random
79local function shuffleTable( t )
80 local rand = math.random
81 assert( t, "shuffleTable() expected a table, got nil" )
82 local iterations = #t
83 local j
84
85 for i = iterations, 2, -1 do
86 j = rand(i)
87 t[i], t[j] = t[j], t[i]
88 end
89end
90
91-- returns how many items are in a table/list
92function tablelength(T)
93 local count = 0
94 for _ in pairs(T) do count = count + 1 end
95 return count
96end
97
98--- Fills a region recursively even if it is bigger by the allowed number of blocks.
99-- @param x
100-- @param y
101-- @param z
102-- @param sizeX
103-- @param sizeY
104-- @param sizeZ
105-- @param fill_material A table with field .block and .data
106-- @param replace Optional, Boolean
107-- @param replace_material Optional, A table with field .block and .data
108
109function fillSmart(x,y,z,sizeX,sizeY,sizeZ, fill_material,replace,replace_material)
110 local check = {x,y,z,sizeX,sizeY,sizeZ, fill_material,replace,replace_material}
111 for i,item in ipairs(check) do
112 debugT("type: "..type(item))
113 end
114 --check if volume is bigger than maximum blocks 32768
115 local limit = 32768
116 local w,h,l = sizeX, sizeY, sizeZ
117 --print("which is nil?", w,h,l)
118 if math.abs(w*l*h) <= limit then
119 --if not then do simple fill
120 --print("filling in one go:",x,y,z,x+w-(w/math.abs(w)),y+h-(h/math.abs(h)),z+l-(l/math.abs(l)),material)
121 if replace and replace_material.block then
122 commands.async.fill(x,y,z,x+w-(w/math.abs(w)),y+h-(h/math.abs(h)),z+l-(l/math.abs(l)),fill_material.block, fill_material.data, "replace", replace_material.block, replace_material.data)
123 else
124 commands.async.fill(x,y,z,x+w-(w/math.abs(w)),y+h-(h/math.abs(h)),z+l-(l/math.abs(l)),fill_material.block, fill_material.data )
125 end
126 return true
127 else
128 --divide in 8 sub-boxes and run again
129 local halfW = math.floor(w/2)
130 local restW = w-halfW
131 local halfH = math.floor(h/2)
132 local restH = h-halfH
133 local halfL = math.floor(l/2)
134 local restL = l-halfL
135 --check if one of them is zero
136 fillSmart(x+halfW, y+halfH, z+halfL, restW, restH, restL, fill_material,replace,replace_material) --always run
137 --
138 if math.abs(halfW) > 0 then
139 fillSmart(x, y+halfH, z+halfL, halfW, restH, restL, fill_material,replace,replace_material) --run if halfW is not zero
140 end
141 if math.abs(halfH) > 0 then
142 fillSmart(x+halfW, y, z+halfL, restW, halfH, restL, fill_material,replace,replace_material) --run if halfH is not zero
143 end
144 if math.abs(halfL) > 0 then
145 fillSmart( x+halfW, y+halfH, z, restW, restH, halfL, fill_material,replace,replace_material) --run if halfL is not zero
146 end
147 ---
148 if math.abs(halfW) > 0 and math.abs(halfH) > 0 then
149 fillSmart( x, y, z+halfL, halfW, halfH, restL, fill_material,replace,replace_material) --run if halfs W and H are not zero
150 end
151 if math.abs(halfL) > 0 and math.abs(halfH) > 0 then
152 fillSmart( x+halfW, y, z, restW, halfH, halfL, fill_material,replace,replace_material)--run if halfH and halfL are not zero
153 end
154 if math.abs(halfW) > 0 and math.abs(halfL) > 0 then
155 fillSmart( x, y+halfH, z, halfW, restH, halfL, fill_material,replace,replace_material) -- run if half W and L are not zero
156 end
157 ---
158 if math.abs(halfW) > 0 and math.abs(halfH) > 0 and math.abs(halfL) > 0 then
159 fillSmart( x, y, z, halfW, halfH, halfL, fill_material,replace,replace_material) --run if neither half is zero
160 end
161 return true
162 end
163 return false
164end
165
166--creates a ring of blocks using coordinates
167function fillRing(x,y,z,sizeX,sizeZ,height, fill_material,replace,replace_material)
168 local h = 1
169 if height then
170 h = height
171 end
172 --debugT("fillSmart from inside fillRing")
173 fillSmart(x,y,z,sizeX,h,1, fill_material,replace,replace_material)
174 fillSmart(x,y,z,1,h,sizeZ, fill_material,replace,replace_material)
175 fillSmart(x+sizeX-1,y,z,1,h,sizeZ, fill_material,replace,replace_material)
176 fillSmart(x,y,z+sizeZ-1,sizeX,h,1, fill_material,replace,replace_material)
177end
178
179--fills an offset region of the specified one
180--direction: true - outside; false - inside
181function fillSmartOffset(x,y,z,sizeX,sizeY,sizeZ,offset, direction, fill_material,replace,replace_material)
182 --debugT("fillSmart from inside fillSmartOffset")
183 if offset == 0 then
184 fillSmart(x,y,z,sizeX,sizeY,sizeZ, fill_material,replace,replace_material)
185 return
186 end
187 local uX = sizeX/math.abs(sizeX)
188 local uY = sizeY/math.abs(sizeY)
189 local uZ = sizeZ/math.abs(sizeZ)
190 if direction then
191 --outside offset
192 fillSmart(x-uX*offset,y-uY*offset,z-uZ*offset,sizeX+2*uX*offset,sizeY+2*uY*offset,sizeZ+2*uZ*offset, fill_material,replace,replace_material)
193 else
194 --inside offset
195 fillSmart(x+uX*offset,y+uY*offset,z+uZ*offset,sizeX-2*uX*offset,sizeY-2*uY*offset,sizeZ-2*uZ*offset, fill_material,replace,replace_material)
196 end
197end
198
199--fills an offset region of the specified one
200-- offsets only along X and Z, doesnt offset vertically
201--direction: true - outside; false - inside
202function fillSmartOffsetXZ(x,y,z,sizeX,sizeY,sizeZ,offset, direction, fill_material,replace,replace_material)
203 --debugT("fillSmart from inside fillSmartOffset")
204 if offset == 0 then
205 fillSmart(x,y,z,sizeX,sizeY,sizeZ, fill_material,replace,replace_material)
206 return
207 end
208 local uX = sizeX/math.abs(sizeX)
209 --local uY = sizeY/math.abs(sizeY)
210 local uZ = sizeZ/math.abs(sizeZ)
211 if direction then
212 --outside offset
213 fillSmart(x-uX*offset,y,z-uZ*offset,sizeX+2*uX*offset,sizeY,sizeZ+2*uZ*offset, fill_material,replace,replace_material)
214 else
215 --inside offset
216 fillSmart(x+uX*offset,y,z+uZ*offset,sizeX-2*uX*offset,sizeY,sizeZ-2*uZ*offset, fill_material,replace,replace_material)
217 end
218end
219
220-- fills a horizontal region with grid markers
221-- takes the grid settings from the registry
222-- grid origin is the main computer
223function fillGrid(x,y,z,sizeX,sizeZ,fill_material)
224 local uX = sizeX/math.abs(sizeX)
225 local uZ = sizeZ/math.abs(sizeZ)
226 for ix = x, x+sizeX-uX, uX do
227 for iz = z, z+sizeZ - uZ, uZ do
228 --check if the coordinate is a grid point
229 if (ix-registry.computer.x) % registry.GRIDCELL_SIZE == 0 and (iz-registry.computer.z) % registry.GRIDCELL_SIZE == 0 then
230 commands.async.setblock(ix,y,iz,fill_material.block, fill_material.data)
231 end
232 end
233 end
234end
235
236
237
238---clones an area even if it is biger than the minecraft limit
239function cloneSmart()
240 --commands.clone( element.elementX, element.elementY, element.elementZ, element.elementX+element.sizeX, element.elementY+registry.VOCAB_HEIGHT, element.elementZ+element.sizeZ, x-math.floor(element.sizeX/2), y-1, z-math.floor(element.sizeZ/2),"masked")
241end
242
243--PARTICLE FUNCTIONS
244--particles shown to player while their shape is being checked for match
245function searchParticle(x,y,z)
246 commands.async.particle("fireworksSpark",x,y,z,0.01,3,0.01,0.01,100)
247end
248-- particles shown to player on successful vocab match
249function successParticle(x,y,z)
250 commands.async.particle("happyVillager",x,y,z,2,2,2,1,1000)
251 commands.async.playsound("random.levelup","@a",x,y,z,1,1.2)
252end
253--- particles shown to player on failed vocab match
254function failParticle(x,y,z)
255 commands.async.particle("reddust",x,y,z,0.1,0.1,1.5,1,200)
256 commands.async.particle("reddust",x,y,z,1.5,0.1,0.1,1,200)
257 commands.async.playsound("random.bowhit","@a",x,y,z,1,0.8)
258end
259
260----END OF UTILITY FUNCTIONS
261
262--creates a grid of absolute references (0,0) (1,0)
263--rename to buildGameField
264function buildGrid(w,h)
265 debugT("creating the LOCS table with all buildzones in this game...")
266 local grid = readGridFromFile()
267 if grid then
268 -- grid was read from the file successfully
269 debugT("LOCS table read from the local file")
270 else
271 --generate file from scratch
272
273 grid = {}
274 for z=0,h-1 do
275 for x=0,w-1 do
276 table.insert(grid,{x=x,z=z,played=false})
277 end
278 end
279 debugT("LOCS table created from scratch")
280 end
281
282 return grid
283end
284
285function readGridFromFile()
286 local result = nil
287 fs.makeDir("/records")
288 local filename = "/records/_buildzones-list.json"
289
290 if utils.file_exists(filename) then
291 ---read from file
292 result = json.decodeFromFile(filename)
293 end
294 return result
295end
296
297function writeGridToFile()
298 fs.makeDir("/records")
299 local filename = "/records/_buildzones-list.json"
300
301 local file = fs.open(filename,"w")
302 file.write(json.encodePretty(LOCS))
303 file.close()
304end
305
306--- Constructor for playerdata object
307-- @param name the player minecraft name as a string
308-- @param x the players current X coord
309-- @param y the players current Y coord
310-- @param z the players current Z coord
311-- @table p The new player object
312-- @return p The new player object
313function newPlayerData(name,x,y,z)
314 local p = {
315 name = name, -- the players minecraft name (String)
316 x = x, -- the players last X coord
317 y = y, -- the players last Y coord
318 z = z -- the players last Z coord
319 }
320 return p
321end
322
323--return a list of all players in the game world as player objects
324local function getAllPos(selector)
325 --debugT("starting getAllPos with selector: "..selector)
326 --commands.gamerule("logAdminCommands",true) --it seems we need this to be on for successful tp message back
327 local result, message = commands.tp("@a["..selector.."]","~ ~ ~")
328 --commands.gamerule("logAdminCommands",false) --and then disable back off to prevent the game from flooding the chat log
329 --local result, message = commands.exec("tp @a["..selector.."] ~ ~ ~")
330 local names = {}
331 if result == true then
332 for i,result in ipairs(message) do
333 --debugT("starting message decoding: "..result)
334 local wordpattern = "[^, ]+"
335 local numberpattern = "[%-% ]%d+[%.]%d+"
336 local words,numbers = {},{}
337 --debugT("finding words: "..result)
338 for word in string.gmatch(result, wordpattern) do
339 table.insert(words,word)
340 end
341 --debugT("finding numbers: "..result)
342 for number in string.gmatch(result, numberpattern) do
343 table.insert(numbers,number)
344 end
345
346 local coords = {
347 x = math.floor(numbers[1]),
348 y = math.floor(numbers[2]),
349 z = math.floor(numbers[3])
350 }
351 local name = words[2]
352 --debugT("inserting into names list: "..name)
353 table.insert(names,newPlayerData(name,coords.x,coords.y,coords.z))
354 end
355 end
356 --debugT("getAllPos completed")
357 return names
358end
359
360
361--returns a list of player objects containing all players who are standing on the given block, and who also are in the given selection
362local function getAllOnBlockType(block,selector)
363 local result, message = commands.exec("execute @a["..selector.."] ~ ~ ~ detect ~ ~-1 ~ "..block.." -1 tp @p[r=1] ~ ~ ~")
364 local names = {}
365 if result == true then
366 for i,result in ipairs(message) do
367 local wordpattern = "[^, ]+"
368 local numberpattern = "[%-% ]%d+[%.]%d+"
369 local words,numbers = {},{}
370
371 for word in string.gmatch(result, wordpattern) do
372 table.insert(words,word)
373 end
374 for number in string.gmatch(result, numberpattern) do
375 table.insert(numbers,number)
376 end
377
378 if numbers[1] and numbers[2] and numbers[3] then
379 local coords = {
380 x = math.floor(numbers[1]),
381 y = math.floor(numbers[2]),
382 z = math.floor(numbers[3])
383 }
384 local name = words[2]
385 table.insert(names,newPlayerData(name,coords.x,coords.y,coords.z))
386 --print("Found a player - getOnBlock")
387 debugT("player "..name.." is on "..block.." at: "..coords.x..", "..coords.y..", "..coords.z)
388 else
389 debugT("Error in getAllOnBlockType: Coordinate Numbers were missing")
390 end
391 end
392 end
393 return names
394end
395
396---BEGIN HOMECOMER RELATED FUNCTIONS
397
398--gives a player a HOMECOMER egg with their name on it. Removes all spawn eggs first
399local function giveHomecomer(name)
400 commands.clear(name,'spawn_egg')
401
402 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]}}')
403end
404
405--returns a list of HOMECOMER entities and their names
406local function getHomecomers()
407 local result, message = commands.exec("execute @e[type="..registry.HOMECOMER_TYPE.."] ~ ~ ~ tp @e[r=1] ~ ~ ~")
408 local names = {}
409 if result == true then
410 for i,result in ipairs(message) do
411 local wordpattern = "[^, ]+"
412 local numberpattern = "[%-% ]%d+[%.]%d+"
413 local words,numbers = {},{}
414
415 for word in string.gmatch(result, wordpattern) do
416 table.insert(words,word)
417 --print(word)
418 end
419 for number in string.gmatch(result, numberpattern) do
420 table.insert(numbers,number)
421 end
422
423 if numbers[1] and numbers[2] and numbers[3] then
424 local coords = {
425 x = math.floor(numbers[1]),
426 y = math.floor(numbers[2]),
427 z = math.floor(numbers[3])
428 }
429 local name = words[4]
430 table.insert(names,newPlayerData(name,coords.x,coords.y,coords.z))
431 debugT("homecomer '"..name.."' is at: "..coords.x..", "..coords.y..", "..coords.z)
432 else
433 --print("Error: Coordinate Numbers were missing")
434 end
435 end
436 end
437 return names
438end
439
440--- Counts active players
441-- Now that we changed how player lists work, just counting the length
442-- does not reveal how many players are currently playing
443-- we need to count based on status
444-- @param kew A Kew object
445-- @return result The playercount as an Int
446local function countActivePlayers(kew)
447 local result = 0
448 for name, playerdata in pairs(kew.playerlist) do
449 if playerdata.status == registry.PLAYER_STATUS.IN_GAME then
450 result = result + 1
451 end
452 end
453 return result
454end
455
456--- Removes a player from a game queue
457-- As playerlists are now playername indexed, we can just check if the
458-- player is in the list. If the player is in the list, we want to change
459-- his status to 1 (left with Homecomer). End the phase if this was the
460-- last player.
461-- @param game The game object
462-- @param playername The players name as a string
463local function removePlayerFromKew(game,playername)
464 for _,kew in pairs(game.queues) do
465 if (kew.playerlist[playername]) then
466 debugT("changing status for player '"..playername.."' at buildzone "..kew.buildzone.locid.." to 1 - left-with-homemcomer")
467 kew.playerlist[playername].status = registry.PLAYER_STATUS.LEFT_WITH_HOMECOMER
468 end
469 local number_of_active_players = countActivePlayers(kew)
470 debugT("players left in buildzone "..kew.buildzone.locid..": "..number_of_active_players)
471 if number_of_active_players == 0 and kew.phase == registry.QUEUE_PHASE.PLAYING then
472 --game can be ended as no players are left
473 kew.timer = 0
474 end
475 end
476end
477
478--teleports a player ot the spawn area
479local function movePlayerToSpawn(playername)
480 resetPlayer(playername)
481 --print("before TP")
482 commands.tp(playername,registry.SPAWN.x,registry.SPAWN.y,registry.SPAWN.z,registry.SPAWN.a1,registry.SPAWN.a2)
483 --print("after TP")
484 if registry.ANNOUNCE_ENGLISH then
485 commands.async.tellraw(playername,'["",{"text":"You used your HOMECOMER and TELEPORTED BACK TO SPAWN","color":"white"}]')
486 end
487 if registry.ANNOUNCE_GERMAN then
488 commands.async.tellraw(playername,'["",{"text":"Du hast den HEIMKEHRER benutzt um dich zurück zum Anfang zu teleportieren.","color":"gold"}]')
489 end
490end
491
492--takes a list of homecomers and deals with all those players, moving them back to spawn and removing them from game
493--also gives them a new homecomer
494local function dealWithHomecomers(game, homecomers)
495 for _,homecomer in pairs(homecomers) do
496 --remove player from current game if in game
497 removePlayerFromKew(game,homecomer.name)
498 --teleport them back to spawn
499 movePlayerToSpawn(homecomer.name)
500 --give a new homecomer
501 giveHomecomer(homecomer.name)
502 --particle effects
503 --teleport message
504 end
505 --kill all homecomers
506 if #homecomers>0 then
507 commands.tp("@e[type="..registry.HOMECOMER_TYPE.."]",100000,200,100000)
508 commands.kill("@e[type="..registry.HOMECOMER_TYPE.."]")
509 os.sleep(#homecomers*0.2)
510 end
511end
512
513---
514function checkForHomecomers(game)
515 --execute on all homecomers
516 local homecomers = getHomecomers()
517 dealWithHomecomers(game,homecomers)
518end
519
520--- END OF HOMECOMER FUNCTIONS
521
522
523
524
525
526
527
528--creates a villager with special items
529local function spawnVillager(x,y,z)
530 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"]}}} ]}}')
531end
532
533
534--displays a time as experience points to a selection of players
535local function displayTime(selector,minutes,seconds)
536 --commands.title("@a["..selector.."]","subtitle",'{text:"Time left: '..minutes..":"..seconds..'",color:red,bold:false,underlined:false,italic:false,strikethrough:false,obfuscated:false}')
537 commands.async.xp("-1000000L","@a["..selector.."]")
538 local secondstot = (minutes * 60) + seconds
539 commands.async.xp(tostring(secondstot).."L","@a["..selector.."]")
540end
541
542--simply runs displayTime on a list of players
543local function displayTimeToGroup(playerlist,minutes,seconds)
544 for name,player in pairs(playerlist) do
545 displayTime("name="..player.name,minutes,seconds)
546 end
547end
548
549--- Updates the Time (XP bar) for players in a buildzone
550--Uses a new playerlist object to only send the time to the active players
551-- @param A new playerlist object
552-- @param minutes How many minutes left
553-- @param seconds How many seconds are left
554local function displayTimeToPlayers(playerlist,minutes,seconds)
555 local activeList = {}
556 for name,player in pairs(playerlist) do
557 if player.status == registry.PLAYER_STATUS.IN_GAME then
558 table.insert(activeList,player)
559 end
560 end
561 displayTimeToGroup(activeList,minutes,seconds)
562end
563
564local function playerListToString(playerlist)
565 --debugT("converting list to string: "..json.encode(playerlist))
566 local s = ""
567 if tablelength(playerlist) > 0 then
568 --debugT("it comes to the for loop")
569 for name, player in pairs(playerlist) do
570 --debugT("adding name: "..player.name)
571 if (player.status) then
572 s = s..", "..player.name.."["..player.status.."]"
573 else
574 s = s..", "..player.name
575 end
576 end
577 end
578 --if
579 --string.sub(m, 4)
580 return s:sub(3)
581end
582
583--displays a title to a selection of players
584local function displayTitle(selector,text,subtext)
585 commands.async.title("@a["..selector.."]","subtitle",'{text:"'..subtext..'"}')
586 commands.async.title("@a["..selector.."]","title",'{text:"'..text..'"}')
587end
588
589--simply runs displayTitle on a list of players
590local function displayTitleToGroup(playerlist,text,subtext)
591 debugT("displayTitleToGroup: "..text.."-"..subtext)
592 for name,player in pairs(playerlist) do
593 displayTitle("name="..player.name,text,subtext)
594 end
595end
596
597--- Sends a title message to active players in a buildzone
598--Uses a new playerlist object to only send the title to the active players
599-- @param A new playerlist object
600-- @param text The message to send
601local function displayTitleToPlayers(playerlist,text,subtext)
602 debugT("displayTitleToPlayers: "..text.."-"..subtext)
603 local activeList = {}
604 for name,player in pairs(playerlist) do
605 if player.status == registry.PLAYER_STATUS.IN_GAME then
606 table.insert(activeList,player)
607 end
608 end
609 displayTitleToGroup(activeList,text,subtext)
610end
611
612--teleports a list of players to an exact place and sends them a message about it
613local function teleportToPoint(x,y,z,playerlist,clear,textEN, textDE)
614 for name,player in pairs(playerlist) do
615 player.x = x
616 player.y = y
617 player.z = z
618 --commands.async.gamemode(2,player.name)
619 if clear then
620 commands.async.clear(player.name,"wool")
621 commands.async.clear(player.name,"stone_pickaxe")
622 end
623 commands.tp("@a[name="..player.name.."]",x,y,z)
624 if registry.ANNOUNCE_ENGLISH then
625 --print("text is: ", textEN)
626 commands.async.tellraw(player.name,'["",{"text":"'..textEN..'","color":"white"}]')
627 end
628 if registry.ANNOUNCE_GERMAN then
629 commands.async.tellraw(player.name,'["",{"text":"'..textDE..'","color":"gold"}]')
630 end
631 end
632end
633
634--- Teleports a list of players randomly about a buildzone
635-- @param buildzone the target buildzone object
636-- @param playerlist a name or number indexed playerlist
637-- @param textEN An english message to send to english players
638-- @param textEN An german message to send to german players
639local function scatterIntoZone(buildzone,playerlist,textEN, textDE)
640 debugT("teleporting to buildzone #"..buildzone.locid.." the players: "..playerListToString(playerlist))
641 local minX = buildzone.x
642 local minZ = buildzone.z
643 local maxX = buildzone.x+buildzone.w
644 local maxZ = buildzone.z+buildzone.w
645 for name,player in pairs(playerlist) do
646 local targetX = math.floor(math.random(minX,maxX))
647 local targetZ = math.floor(math.random(minZ,maxZ))
648 --print("targetX", targetX)
649 --print("targetZ", targetZ)
650 --print("buildzone.y+5", (buildzone.y+5))
651 --print("player ",player)
652 --print(json.encodePretty(player))
653 --print("textEN", textEN)
654 --print("textDE", textDE)
655 --debugT("teleport from scatterIntoZone")
656 teleportToPoint(targetX, buildzone.y+5, targetZ, {player}, false, textEN, textDE)
657 end
658 debugT("teleporting players to buildzone #"..buildzone.locid.." complete")
659end
660
661--- Teleports a list of players to the centre of a given buildzone
662-- @param buildzone the target buildzone object
663-- @param playerlist a name or number indexed playerlist
664-- @param textEN An english message to send to english players
665-- @param textEN An german message to send to german players
666local function teleportToZone(buildzone,playerlist,textEN, textDE)
667 teleportToPoint(buildzone.x+2+(buildzone.w/2),buildzone.y+5,buildzone.z+2+(buildzone.w/2),playerlist,true,textEN, textDE)
668
669end
670
671--gives the same list of items to a list of players
672local function giveItems(playerlist,itemlist)
673 debugT("Giving initial inventory items to players: "..playerListToString(playerlist))
674 local given = 0
675 for name,player in pairs(playerlist) do
676 --commands.async.clear(player.name)
677 for j,item in ipairs(itemlist) do
678 commands.async.give("@a[name="..player.name.."]",item)
679 given = given +1
680 end
681 giveHomecomer(player.name)
682 debugT("Gave initial inventory items to '"..player.name.."'")
683 end
684 debugT("Giving initial inventory items to players...DONE!")
685 return given
686end
687
688--a catalogue slot constructor. Enforces some data structure
689function newCatalogueElement(id, x,y,z,sizeX, sizeZ) --, reward,nameEN, nameDE, typeEN, typeDE, height, slots,green,greenSlots, rewardUrban, rewardCount)
690 local nvz = {}
691 nvz.id = id
692 nvz.keyX ,nvz.keyY ,nvz.keyZ = x,y,z
693 nvz.sizeX, nvz.sizeZ = sizeX, sizeZ
694 nvz.elementX = nvz.keyX - nvz.sizeX - registry.GRIDCELL_SIZE --
695 nvz.elementY = nvz.keyY
696 nvz.elementZ = nvz.keyZ
697 nvz.elementCenterX = nvz.elementX + math.floor ( nvz.sizeX/2 )
698 nvz.elementCenterY = nvz.elementY
699 nvz.elementCenterZ = nvz.elementZ + math.floor ( nvz.sizeZ/2 )
700 nvz.keyCenterX = nvz.keyX + math.floor ( nvz.sizeX/2 )
701 nvz.keyCenterY = nvz.keyY
702 nvz.keyCenterZ = nvz.keyZ + math.floor ( nvz.sizeZ/2 )
703
704 -- nvz.nameEN = nameEN
705-- nvz.reward = reward
706-- --- new stuff
707--
708-- nvz.nameDE = nameDE
709-- nvz.typeEN = typeEN
710-- nvz.typeDE = typeDE
711-- nvz.height = height
712-- nvz.slots = slots
713-- nvz.green = green
714-- nvz.greenSlots = greenSlots
715-- nvz.rewardUrban = rewardUrban
716-- nvz.rewardCount = rewardCount
717--
718 --print(json.encodePretty(nvz))
719 return nvz
720
721end
722
723
724--a multi builder which uses the vocab constructor to create sets of vocab
725local function initCatalogueElements(countX, countZ, sizeX, sizeZ)
726 local x,y,z = registry.FIRST_ELEMENT.x, registry.FIRST_ELEMENT.y, registry.FIRST_ELEMENT.z
727 local result = {}
728 local id = 1
729 for i=0,countZ-1 do
730 for k=0,countX-1 do
731
732 local xpos = x - k * ( registry.GRIDCELL_SIZE * ( 2*registry.CATALOGUE_SLOTSIZE.x + 2 + registry.CATALOGUE_SLOT_OFFSET))
733 local ypos = y
734 local zpos = z + i*( registry.GRIDCELL_SIZE * ( registry.CATALOGUE_SLOTSIZE.z + 1 + registry.CATALOGUE_SLOT_OFFSET))
735 --print("adding an element",i,k)
736 local nextElement = newCatalogueElement(
737 id,
738 xpos,ypos,zpos,
739 sizeX, sizeZ
740 )
741
742 -- registry.REWARDS[id] or registry.DEFAULT_REWARD,
743 -- registry.VOCABS_DATA[id].nameEN or registry.DEFAULT_NAME,
744 -- registry.VOCABS_DATA[id].nameDE or registry.DEFAULT_NAME,
745 -- registry.VOCABS_DATA[id].typeEN,
746 -- registry.VOCABS_DATA[id].typeDE,
747 -- registry.VOCABS_DATA[id].height,
748 -- registry.VOCABS_DATA[id].slots,
749 -- registry.VOCABS_DATA[id].green,
750 -- registry.VOCABS_DATA[id].greenSlots,
751 -- registry.VOCABS_DATA[id].rewardUrban,
752 -- registry.VOCABS_DATA[id].rewardCount
753 -- )
754 table.insert(result,nextElement)
755 id = id +1
756 end
757 end
758 --print(json.encodePretty(result))
759 return result
760end
761
762--- Finds the next free location for a buildzone in a given list of spots.
763-- Searches each zone in zones for an empty location.
764-- Zone are absolute positions (0,0),(0,1) ect.
765-- This uses the coords of FIRST_ZONE as given in the registry to determine
766-- the position of the zones for checking.
767-- @param zones A list of zones from BuildLocs()
768-- @param force Boolean. True will mean it returns the first location no matter what
769-- @return x The X value of the next unused location in world-coordinates
770-- @return y The Y value of the next unused location in world-coordinates
771-- @return z The Z value of the next unused location in world-coordinates
772-- @return locid The index of the returned zone
773local function findNextLoc(zones,force)
774 local x,y,z,locid = 0,0,0,1
775 for i,loc in ipairs(LOCS) do
776 locid = i
777 -- these are the coordinates of this LOC in the minecraft world
778 x = registry.FIRST_ZONE.x+(loc.x*( registry.BUILDZONE_WIDTH + registry.BUILDZONE_OFFSET*registry.GRIDCELL_SIZE))
779 y = registry.FIRST_ZONE.y
780 z = registry.FIRST_ZONE.z+(loc.z*( registry.BUILDZONE_WIDTH + registry.BUILDZONE_OFFSET*registry.GRIDCELL_SIZE))
781 --print("testing for available zone at: "..x..", "..y..", "..z)
782 --print("which is at grid cell at: "..loc.x..", "..loc.z)
783 --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
784 local result = true
785 if loc.played then
786 result = false
787 --print("zone has been played")
788 end
789 --print("testing done")
790 --force the first zone to be selected unless it is taken in the "zones" parameter
791 if force then result = true end
792 --checks if the zone is already in the list of unavailable zones passed as parameter
793 local zonefree = true
794 for i,zone in ipairs(zones) do
795 if zone.x == x and zone.z == z then
796 zonefree = false
797 end
798 end
799 --print("next position free is ",loc.x*width,oy,loc.z*width)
800 --if result then print("true") else print("false") end
801 if result and zonefree then
802 debugT("Using loc at: "..x..", "..y..", "..z.." with locid: "..locid)
803 return x,y,z,locid --returns the coordinates of the new zone, plus its id in the LOCS table
804 end
805 end
806 debugT("ERROR: findNextLoc failed")
807 return nil,nil,nil, nil --returns empty if no zone is available
808
809end
810
811--[[ looks like the moveBuildzone is not used anymore
812--- Assigns the next unused available play area to an abstract buildzone object
813-- This takes a Buildzone and an array of Absolute references
814-- from BuildGrid(). It finds the next area by using FindNextLoc().
815-- When we change a buildzones coords it is important to update locid and
816-- the selector so that all systems know where the buildzone is.
817-- @param buildzone the buildzone object to be moved
818-- @param zones a list of zones to choose from
819-- @return a boolean result. True if the buildzone was moved
820function moveBuildzone(buildzone,zones)
821 local x,y,z,locid = findNextLoc(zones)
822 if x and y and z and locid then
823 --print("moved buildzone from "..buildzone.x..","..buildzone.z.." to "..x..","..y)
824 local w = buildzone.w
825 buildzone.x,buildzone.y,buildzone.z = x,y,z
826 buildzone.locid = locid --reassign the location id corresponding to the LOCS item for the grid cell of the moved zone
827 buildzone.selector = "x="..x..",y="..tostring(y-1)..",z="..z..",dx="..w..",dy=256,dz="..w
828 ---buildzone.structures = {} --a list of all vocabularies which have been contructed
829 return true
830 end
831 return false
832end
833--]]
834
835--multi builder to create sets of buildzones using the buildzone constructor
836local function initBuildzones(quant,elements)--,width)--,floorHeight)
837 local result = {}
838 for i=1,quant do
839 --print("locating available slot")
840 local x,y,z,locid = findNextLoc(result)
841 if x and y and z and locid then
842
843 table.insert(result,newBuildZone(x,y,z,elements,locid))
844 debugT("Created a new Buildzone #"..locid.." at:"..x..", "..y..", "..z)
845 else
846 --print("failed to make new buildzone")
847 end
848 end
849
850 local remaining = registry.NUMBER_OF_BUILDZONES - #result
851 --print("doing this remaining thing")
852 for i=1, remaining do
853 local x,y,z,locid = findNextLoc(result,true)
854 if x and y and z and locid then
855 debugT("forced new buildzone #"..locid.." at:"..x..", "..y..", "..z)
856 table.insert(result,newBuildZone(x,y,z,elements,locid))
857 else
858 debugT("failed to force init a new buildzone")
859 end
860 end
861
862
863 return result
864end
865
866
867
868--- Buildzone constructor. Enforces some data structure
869-- Buildzones record information about the structures which are built
870-- inside them, as well as which elements of the catalogue are
871-- available to be built in them. Buildzones record where they are in
872-- three ways (locid, selector and coords). Buildzones do not know
873-- who is playing in them right now, or how long is left in a game
874-- @param x Where you first want to place the buildzone
875-- @param y Where you first want to place the buildzone
876-- @param z Where you first want to place the buildzone
877-- @param elementZones An array of catalogue items that can be built here
878-- @param locid The buildzones starting location in the buildLocs() array
879function newBuildZone(x,y,z,elementZones,locid)
880 local nbz = {}
881 nbz.x ,nbz.y ,nbz.z ,nbz.w = x,y,z,registry.BUILDZONE_WIDTH
882 nbz.selector = "x="..(nbz.x-2)..",y="..(nbz.y-3)..",z="..(nbz.z-2)..",dx="..(nbz.w+4)..",dy=256,dz="..(nbz.w+4)
883 nbz.structures = {} --a list of all vocabularies names which have been contructed
884 nbz.buildings = {} --a list of all vocabularies with full data (x,y,z,id,name) which have been contructed
885 nbz.filledSlots = 0 --to count how many slots have been filled with buildings. the matrix is 7x7x20 slots. one slot is 9x9x9 blocks big
886 nbz.greenSlots = 0 --to count how many of the slots are green. the matrix is 7x7x20 slots. one slot is 9x9x9 blocks big
887 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
888 nbz.waitingForCheck = {}
889 nbz.highest = 0
890 nbz.elements = elementZones
891 nbz.locid = locid
892 return nbz
893end
894
895--- Kew constructor. Enforces some data structure
896-- Kews are used for timekeeping and to record which players are
897-- interacting where. Each queue can only have 1 buildzone, but may have
898-- many players. Buildzones may move location, but Kews do not.
899-- @param buildzone The buildzone object to associate with this Kew
900-- @param maxplayers How many players are allowed to play in the buildzone
901-- @return The constructed Kew Object
902function newQueue(buildzone,maxplayers,portal)
903 debugT("creating a new game queue for buildzone #"..buildzone.locid)
904 local q = {}
905 q.timer = 1
906 q.phase = registry.QUEUE_PHASE.DORMANT
907
908 q.victory = false
909 q.phases = {
910 {
911 name = "Selecting Players",
912 length = registry.PHASE_LENGTH_WAIT
913 --displaylength = 15 --this field is not used
914 },
915 {
916 name = "Game In Progress",
917 length = registry.PHASE_LENGTH_GAME
918 --displaylength = 70 --this field is not used
919 },
920 {
921 name = "Round Complete",
922 length = registry.PHASE_LENGTH_OVER
923 --displaylength = 5 --this field is not used
924 }
925 }
926 q.playerlist = {}
927 q.maxplayers = maxplayers
928 q.portal = portal
929 q.buildzone = buildzone
930 local timestamp = math.floor(os.clock())
931 q.filename = timestamp.."at"..q.buildzone.x.."_"..q.buildzone.z
932 return q
933end
934
935--checks if the game has been run, if the game area has been created already and if it there is a change of the parameters that define it
936function checkForGameAreaParametersChanges()
937 --1. assemble a json object of the current settings
938 local currentSettings = {}
939 currentSettings.versionNumber = VERSION_NUMBER
940 currentSettings.versionName = VERSION_NAME
941 currentSettings.computer = registry.computer
942 --spawn area
943 currentSettings.spawnzoneSize = registry.SPAWNZONE_SIZE
944 --currentSettings.spawnzoneOffset = registry.SPAWNZONE_OFFSET
945 --currentSettings.spawnzoneDistance = registry.SPAWNZONE_DISTANCE
946 --play area
947 currentSettings.gridCellSize = registry.GRIDCELL_SIZE
948 currentSettings.gridCellCount = registry.GRIDCELL_COUNT
949 --[[
950 PLAY_AREA_OFFSET.x
951 PLAY_AREA_OFFSET.z
952 TRENCHES.DEPTH
953 GAME_FIELD.CountX
954 GAME_FIELD.CountZ
955 BUILDZONE_OFFSET
956
957 -- catalogue area
958 CATALOGUE_SLOTCOUNT.x
959 CATALOGUE_SLOTCOUNT.z
960 CATALOGUE_SLOTSIZE.x
961 CATALOGUE_SLOTSIZE.z
962 CATALOGUE_SLOT_OFFSET
963 --]]
964
965 --2. open a file and
966 --3. read a json object of the settigns used last time
967
968 --4. compare the two json objects
969 local result = 0
970 --if file doenst exist make result = 14 and return
971 --if file exists and there is change in catalogue area add 8 to result for partial rebuild only the catalogue areas
972 --if file exists and there is change in play area add 4 to result for partial rebuild only the play areas
973 --if file exists and there is change in spawn area add 2 to result for partial rebuild only the spawn areas
974 --if file exist and no change return result as 0 for no action
975 return result
976end
977
978--- Creates a game object and initializes game management functions
979-- This function is a bit large, and should be placed in its own module
980-- at some point.
981-- The game object creates and stores the complete Catalogue
982-- The game object creates and stores a complete list of all Buildzones
983-- The game object creates and stores a complete list of all Kews
984-- The game object only tracks players who are NOT in a Kew
985-- The game object knows where the spawn is
986-- This function rebuilds the Catalogue and Buildzones in the gameworld
987-- if any settings have changed.
988-- This function adds scoreboards to your Minecraft world so that 20kb
989-- operates correctly.
990-- This function runs GameRule commands to set up your Minecraft world.
991-- If DEBUG_MODE is on, then Kews are fixed to Play Phase with a large
992-- time limit
993-- This function kills all Villagers; it is the only way to be sure.
994-- @return game The Game container object.
995function setup()
996 debugT("Starting Setup function.")
997
998 local registryOK = registry.checkRegistryIntegrity()
999 if not registryOK then
1000 --if not registry.FINE then
1001 debugT("setup failed")
1002 return false
1003 end
1004 --print ("actual SPAWNZONE_OFFSET is:", registry.SPAWNZONE_OFFSET)
1005
1006 local game = {}
1007 --game.elements = {} --moved to a local variable to avoid recursive containment in the "game" table
1008 --game.builds = {} --moved to a local variable to avoid recursive containment in the "game" table
1009 game.queues = {}
1010 --game.waitlist = {}
1011 --game.spawn = registry.SPAWN
1012 game.lastClock = os.clock()
1013 game.nowTime = os.clock()
1014
1015 ---SETTINGS = registry.loadCustomSettings()
1016
1017 debugT("Game object created")
1018
1019 --SET UP THE GAME AREA
1020 debugT("setting up game area...")
1021 --do a file check to see if setting up the game area is needed
1022 debugT("checking for changed settings...NOT YET IMPLEMENTED")
1023 local rebuild = checkForGameAreaParametersChanges()
1024
1025 --set up chunk loaders
1026 --commands.setblock(-71, 56, -511, "neotech:chunkLoader", 0, "replace", '{id:"neotech:chunkLoader", Diameter:1}')
1027 --get the chunk of the computer
1028
1029 --the total size of game area
1030 --spawn zones
1031 --main spawn zone
1032 --check if spawn area needs rebuilding
1033 if BitAND(rebuild,2)==2 then
1034 debugT("building spawn areas...")
1035 --debugT("fillSmart from inside setup")
1036 --rebuild spawn area
1037 fillSmartOffsetXZ(
1038 registry.MAIN_SPAWN_AREA.x,
1039 registry.MAIN_SPAWN_AREA.y,
1040 registry.MAIN_SPAWN_AREA.z,
1041 registry.MAIN_SPAWN_AREA.w,
1042 -registry.TRENCHES.DEPTH,
1043 registry.MAIN_SPAWN_AREA.l,
1044 registry.TRENCHES.WIDTH,
1045 true,
1046 registry.BLOCKS.AIR
1047 )
1048 fillSmart(
1049 registry.MAIN_SPAWN_AREA.x,
1050 registry.MAIN_SPAWN_AREA.y,
1051 registry.MAIN_SPAWN_AREA.z,
1052 registry.MAIN_SPAWN_AREA.w,
1053 -registry.MAIN_SPAWN_AREA.y,
1054 registry.MAIN_SPAWN_AREA.l,
1055 registry.BLOCKS.DARK_GRID
1056 )
1057 --fill the grid
1058 fillGrid(
1059 registry.MAIN_SPAWN_AREA.x,
1060 registry.MAIN_SPAWN_AREA.y,
1061 registry.MAIN_SPAWN_AREA.z,
1062 registry.SPAWNZONE_SIZE,
1063 registry.MAIN_SPAWN_AREA.l,
1064 registry.BLOCKS.WHITE_GRID
1065 )
1066
1067 --fill the glass border
1068 fillRing(
1069 registry.MAIN_SPAWN_AREA.x,
1070 registry.MAIN_SPAWN_AREA.y+1,
1071 registry.MAIN_SPAWN_AREA.z,
1072 registry.MAIN_SPAWN_AREA.w,
1073 registry.MAIN_SPAWN_AREA.l,
1074 registry.MAIN_SPAWN_AREA.h,
1075 registry.BLOCKS.SPAWNZONE_FENCE
1076 )
1077
1078 local padx = registry.computer.x + math.floor(registry.SPAWNZONE_SIZE/2) + registry.PORTAL.OFFSET
1079 local pady = registry.computer.y - 1
1080 local padz = registry.computer.z - math.floor(registry.GAME_FIELD_MAX.countZ/2) * registry.PORTAL.OFFSET - math.floor((registry.GAME_FIELD_MAX.countZ/2) * registry.PORTAL.SIZE)
1081 for ix = 0, registry.GAME_FIELD_MAX.countZ - 1 do
1082 for iz=0, registry.GAME_FIELD_MAX.countZ - 1 do
1083 local xpos = padx + ix * (registry.PORTAL.OFFSET + registry.PORTAL.SIZE)
1084 local ypos = pady
1085 local zpos = padz + iz * (registry.PORTAL.OFFSET + registry.PORTAL.SIZE)
1086 --print("making pads", ix, iz, xpos, ypos, zpos)
1087 if registry.GAME_FIELD.countZ > iz and registry.GAME_FIELD.countX > ix then
1088 --create the tp pads openings
1089 --fillSmart( registry.BLOCKS.AIR.block, xpos, ypos, zpos, registry.PORTAL.SIZE, 1, registry.PORTAL.SIZE )
1090 --create the tp pads
1091 --fillSmart( registry.BLOCKS.DETECT.block, xpos, ypos-1, zpos, registry.PORTAL.SIZE, 1, registry.PORTAL.SIZE )
1092 --place the tp pad markers
1093 --fillSmart( registry.BLOCKS.DETECT.block, xpos+math.floor(registry.PORTAL.SIZE/2), ypos, zpos+math.floor(registry.PORTAL.SIZE/2), 1, 1, 1 )
1094 else
1095 --mark the builzone pad as inactive
1096 --debugT("fillSmart from mark buildzone inactive")
1097 fillSmart( xpos, ypos, zpos, registry.PORTAL.SIZE, 1, registry.PORTAL.SIZE,registry.BLOCKS.WHITE_GRID )
1098 end
1099 end
1100 end
1101 end
1102
1103
1104 --this section below is the actual creation of the catalogue slots in the world
1105 --check if catalogue area needs rebuilding
1106 if BitAND(rebuild,8)==8 then
1107 debugT("building catalogue slots...")
1108 --debugT("fillSmart from inside build catalogue slots")
1109 local x,y,z = registry.FIRST_ELEMENT.x, registry.FIRST_ELEMENT.y, registry.FIRST_ELEMENT.z
1110 fillSmartOffsetXZ(
1111 registry.CATALOG_AREA.x ,
1112 registry.CATALOG_AREA.y,
1113 registry.CATALOG_AREA.z,
1114 registry.CATALOG_AREA.dx,
1115 -registry.TRENCHES.DEPTH,
1116 registry.CATALOG_AREA.dz,
1117 registry.TRENCHES.WIDTH,
1118 true,
1119 registry.BLOCKS.AIR
1120 )
1121 --[[
1122 print("catalog area:",registry.CATALOG_AREA.x ,
1123 registry.CATALOG_AREA.y,
1124 registry.CATALOG_AREA.z,
1125 registry.CATALOG_AREA.dx,
1126 registry.CATALOG_AREA.dz)
1127 --]]
1128 fillSmart(
1129 registry.CATALOG_AREA.x ,
1130 registry.CATALOG_AREA.y,
1131 registry.CATALOG_AREA.z,
1132 registry.CATALOG_AREA.dx,
1133 -registry.CATALOG_AREA.y,
1134 registry.CATALOG_AREA.dz,
1135 registry.BLOCKS.DARK_GRID
1136 )
1137 fillGrid(
1138 registry.CATALOG_AREA.x ,
1139 registry.CATALOG_AREA.y,
1140 registry.CATALOG_AREA.z,
1141 registry.CATALOG_AREA.dx,
1142 registry.CATALOG_AREA.dz,
1143 registry.BLOCKS.WHITE_GRID
1144 )
1145 --print("element size",registry.ELEMENT.sizeX )
1146 for i=0,registry.CATALOGUE_SLOTCOUNT.z - 1 do
1147 for k=0,registry.CATALOGUE_SLOTCOUNT.x - 1 do
1148 --local xpos = x - k*( 2*registry.ELEMENT.sizeX + registry.GRIDCELL_SIZE + 2 )
1149 local xpos = x - k * ( registry.GRIDCELL_SIZE * ( 2*registry.CATALOGUE_SLOTSIZE.x + 2 + registry.CATALOGUE_SLOT_OFFSET))
1150
1151 local ypos = y - 1
1152 local zpos = z + i*( registry.GRIDCELL_SIZE * ( registry.CATALOGUE_SLOTSIZE.z + 1 + registry.CATALOGUE_SLOT_OFFSET))
1153
1154 --fill white grid ring slot
1155 --debugT("fillSmart from inside white grid ring")
1156 fillRing(
1157 xpos-registry.GRIDCELL_SIZE*(registry.CATALOGUE_SLOTSIZE.x+1)-1-math.floor(registry.GRIDCELL_SIZE/2),
1158 ypos,
1159 zpos-1-math.floor(registry.GRIDCELL_SIZE/2),
1160 2*(registry.CATALOGUE_SLOTSIZE.x+1)*registry.GRIDCELL_SIZE + 1,
1161 (registry.CATALOGUE_SLOTSIZE.x + 1)*registry.GRIDCELL_SIZE + 1,
1162 registry.BLOCKS.WHITE_GRID
1163 )
1164 fillRing(
1165 xpos-registry.GRIDCELL_SIZE*(registry.CATALOGUE_SLOTSIZE.x+1)-1-math.floor(registry.GRIDCELL_SIZE/2),
1166 ypos,
1167 zpos-1-math.floor(registry.GRIDCELL_SIZE/2),
1168 (registry.CATALOGUE_SLOTSIZE.x + 1)*registry.GRIDCELL_SIZE + 1,
1169 (registry.CATALOGUE_SLOTSIZE.x + 1)*registry.GRIDCELL_SIZE + 1,
1170 registry.BLOCKS.WHITE_GRID
1171 )
1172 --fill white grid ring element
1173 fillRing(
1174 xpos-registry.GRIDCELL_SIZE*(registry.CATALOGUE_SLOTSIZE.x+1)-1,
1175 ypos,
1176 zpos-1,
1177 registry.ELEMENT.sizeX+2,
1178 registry.ELEMENT.sizeZ+2,
1179 registry.BLOCKS.WHITE_GRID
1180 )
1181 --fill white grid key
1182 fillSmart(xpos, ypos, zpos, registry.ELEMENT.sizeX, 1, registry.ELEMENT.sizeZ,registry.BLOCKS.WHITE_GRID.block, registry.BLOCKS.WHITE_GRID.data )
1183 --fill dark grid slot
1184 fillGrid(
1185 xpos-registry.GRIDCELL_SIZE*(registry.CATALOGUE_SLOTSIZE.x+1)-1-math.floor(registry.GRIDCELL_SIZE/2),
1186 ypos,
1187 zpos-1-math.floor(registry.GRIDCELL_SIZE/2),
1188 2*(registry.CATALOGUE_SLOTSIZE.x+1)*registry.GRIDCELL_SIZE + 1,
1189 (registry.CATALOGUE_SLOTSIZE.x + 1)*registry.GRIDCELL_SIZE + 1,
1190 registry.BLOCKS.DARK_GRID
1191 )
1192 --fill plug grid key
1193 fillGrid(xpos, ypos, zpos, registry.ELEMENT.sizeX, registry.ELEMENT.sizeZ,registry.BLOCKS.PLUG)
1194 --fill plug grid element
1195 fillGrid(
1196 xpos-registry.GRIDCELL_SIZE*(registry.CATALOGUE_SLOTSIZE.x+1)-1,
1197 ypos,
1198 zpos-1,
1199 registry.ELEMENT.sizeX+2,
1200 registry.ELEMENT.sizeZ+2,
1201 registry.BLOCKS.PLUG
1202 )
1203 --fillSmart(registry.BLOCKS.VOCAB_REPLACE.block, xpos, ypos, zpos, registry.ELEMENT.sizeX, 1, registry.ELEMENT.sizeZ)
1204 --fillSmart(registry.BLOCKS.VOCAB_DETECT.block, xpos - registry.ELEMENT.sizeX - 1, ypos, zpos, registry.ELEMENT.sizeX, 1, registry.ELEMENT.sizeZ)
1205 end
1206 end
1207 --fillSmart(registry.BLOCKS.RING.block, registry.FIRST_ELEMENT.x, registry.FIRST_ELEMENT.y-1, registry.FIRST_ELEMENT.z, 1, 1, 1)
1208 end
1209
1210 if BitAND(rebuild,4)==4 then
1211 debugT("building play area...")
1212 --debugT("fillSmart from inside build play area")
1213 fillSmartOffsetXZ(
1214 registry.PLAY_AREAS.x ,
1215 registry.PLAY_AREAS.y,
1216 registry.PLAY_AREAS.z,
1217 registry.PLAY_AREAS.dx,
1218 -registry.TRENCHES.DEPTH,
1219 registry.PLAY_AREAS.dz,
1220 registry.TRENCHES.WIDTH,
1221 true,
1222 registry.BLOCKS.AIR
1223 )
1224 fillSmart(
1225 registry.PLAY_AREAS.x ,
1226 registry.PLAY_AREAS.y,
1227 registry.PLAY_AREAS.z,
1228 registry.PLAY_AREAS.dx,
1229 -registry.PLAY_AREAS.y,
1230 registry.PLAY_AREAS.dz,
1231 registry.BLOCKS.DARK_GRID
1232 )
1233 local x,y,z = registry.FIRST_ZONE.x, registry.FIRST_ZONE.y, registry.FIRST_ZONE.z
1234 for i=0,registry.GAME_FIELD.countZ - 1 do
1235 for k=0,registry.GAME_FIELD.countX - 1 do
1236 local xpos = x + k*( registry.BUILDZONE_WIDTH + registry.BUILDZONE_OFFSET*registry.GRIDCELL_SIZE)
1237 local ypos = y
1238 local zpos = z + i*( registry.BUILDZONE_WIDTH + registry.BUILDZONE_OFFSET*registry.GRIDCELL_SIZE)
1239 fillRing(xpos,ypos,zpos,registry.BUILDZONE_WIDTH,registry.BUILDZONE_WIDTH,registry.BLOCKS.WHITE_GRID)
1240 end
1241 end
1242 fillGrid(
1243 registry.PLAY_AREAS.x ,
1244 registry.PLAY_AREAS.y,
1245 registry.PLAY_AREAS.z,
1246 registry.PLAY_AREAS.dx,
1247 registry.PLAY_AREAS.dz,
1248 registry.BLOCKS.WHITE_GRID
1249 )
1250 end
1251
1252 --vocabzone creation
1253 debugT("initializing catalogue elements...")
1254 --the following is a set of abstract objects, not the actual creating of the catalgoue slots in the world
1255 --it should run always
1256 --game.elements = initCatalogueElements(
1257 catalogue_elements = initCatalogueElements(
1258 registry.CATALOGUE_SLOTCOUNT.x,
1259 registry.CATALOGUE_SLOTCOUNT.z,
1260 registry.ELEMENT.sizeX,
1261 registry.ELEMENT.sizeZ
1262 )
1263
1264 debugT("Testing for properly setup catalogue elements")
1265
1266 --for i,vz in ipairs(game.elements) do
1267 for i,vz in ipairs(catalogue_elements) do
1268 local detector, message1 = commands.testforblock(vz.keyCenterX,vz.keyCenterY,vz.keyCenterZ,registry.BLOCKS.DETECT.block,registry.BLOCKS.DETECT.data)
1269 local blocker, message2 = commands.testforblock(vz.keyCenterX,vz.keyCenterY,vz.keyCenterZ,registry.BLOCKS.DETECT_DEAD.block,registry.BLOCKS.DETECT_DEAD.data)
1270 --print("element",i,message1, message2)
1271 if not (detector or blocker) then
1272 commands.setblock(vz.keyCenterX,vz.keyCenterY,vz.keyCenterZ,registry.BLOCKS.DETECT_DEAD.block,registry.BLOCKS.DETECT_DEAD.data)
1273 end
1274 --commands.setblock(vz.elementCenterX,vz.elementCenterY,vz.elementCenterZ,registry.BLOCKS.DARK_GRID.block)
1275 --commands.setblock(vz.keyX,vz.keyY,vz.keyZ,registry.BLOCKS.DARK_GRID.block)
1276 --commands.setblock(vz.elementX,vz.elementY,vz.elementZ,registry.BLOCKS.WHITE_GRID.block)
1277 end
1278
1279 --[[
1280
1281 if true then
1282 return
1283 end
1284
1285 ]]--
1286
1287 --generate catalogue rewards
1288 for i=1,#registry.VOCABS_DATA do
1289 if registry.VOCABS_DATA[i].rewardUrban then
1290 registry.REWARDS[i] = registry.houseBlock(registry.VOCABS_DATA[i].rewardCount)
1291 else
1292 registry.REWARDS[i] = registry.gardenBlock(registry.VOCABS_DATA[i].rewardCount)
1293 end
1294 end
1295 debugT("Catalogue definitions successfully initialized!")
1296
1297 --kill all villagers
1298 commands.exec("kill @e[type=Villager]")
1299 debugT("Villagers destroyed")
1300
1301 --buildzone creation
1302 debugT("Making building zone objects.")
1303 --game.builds appears to store the games currently in progress
1304 --game.builds = initBuildzones(registry.NUMBER_OF_BUILDZONES,game.elements,registry.BUILDZONE_WIDTH)--,registry.BUILDZONE_FLOOR_HEIGHT)
1305 buildzones = initBuildzones(registry.NUMBER_OF_BUILDZONES,catalogue_elements,registry.BUILDZONE_WIDTH)--,registry.BUILDZONE_FLOOR_HEIGHT)
1306 --for i,build in ipairs(game.builds) do
1307 for i,build in ipairs(buildzones) do
1308 table.insert(game.queues,newQueue(build, registry.MAX_PLAYERS_PER_GAME, registry.PORTALS_LOCATIONS[i]))
1309
1310 end
1311 debugT("starting the building of portals...")
1312 for i, kew in ipairs(game.queues) do
1313 debugT("building of portal:",i)
1314 drawPortal( kew.portal, kew.phase)
1315 end
1316 debugT("building of portals DONE!")
1317
1318 ----
1319 debugT("Adding scoreboards.")
1320 --print(#registry.VOCABS_DATA)
1321 for i=1,#registry.VOCABS_DATA do
1322 commands.scoreboard("objectives","add","building_"..i,"dummy")
1323 end
1324 commands.scoreboard("objectives","add","highscores","dummy","Best Neighbourhoods")
1325 if registry.SHOW_HIGHSCORES then
1326 commands.scoreboard("objectives","setdisplay","sidebar","highscores")
1327 end
1328 commands.scoreboard("objectives","add","VillagerLife","dummy")
1329 commands.scoreboard("objectives","add","built","dummy", "Structures Built")
1330 commands.scoreboard("objectives","add","highest","dummy", "Personal Highest")
1331 commands.scoreboard("objectives","add","played","dummy","Games Played")
1332 commands.scoreboard("objectives","setdisplay","list","played")
1333
1334 commands.title("@a","times",0,30,30) -- what does this do?
1335
1336 ----
1337 debugT("setting the needed preferences of the minecraft world.")
1338 commands.gamerule("doDaylightCycle",false) -- this is included in the spawn controller script
1339 commands.gamerule("keepInventory",true)
1340 --commands.gamerule("doTileDrops",false) -- this is included in the spawn controller script
1341 commands.gamerule("sendCommandFeedback", true)
1342 commands.gamerule("logAdminCommands",true) -- it looks like this needs to be on for the tp funtion used in getAllPos to return its message properly
1343 commands.gamerule("commandBlockOutput",false) -- this is included in the spawn controller script
1344 commands.time("set",6000) -- this is included in the spawn controller script
1345
1346 ---
1347 math.randomseed( os.time() )
1348 debugT("Computer clock is: "..os.clock())
1349 debugT("20.000 BLOCKS is ready to run!")
1350
1351 return game
1352end
1353
1354--- Runs a single step of the game loop
1355-- Runs the game object through each of these update steps in order.
1356-- See the comments on each of the steps.
1357-- @param game The game object as created in setup()
1358function update(game)
1359 --debugT("updating game state")
1360 local elapsed = updateClock(game)
1361 --update players
1362 debugT("update: will checkPlayers")
1363 checkPlayers(game)
1364 debugT("update: will doTimerUpdates")
1365 doTimerUpdates(game,elapsed)
1366 debugT("update: will doPhaseUpdates")
1367 doPhaseUpdates(game)
1368 debugT("update: will doPhaseEnds")
1369 doPhaseEnds(game)
1370 debugT("update: will checkBoundaries")
1371 checkBoundaries(game)
1372 debugT("update: will checkForHomecomers")
1373 checkForHomecomers(game)
1374 debugT("update: will allocateWaiters")
1375 allocateWaiters(game)
1376end
1377
1378--- Calculates elapsed time during a game tick
1379-- Updates the given game objects clock using the operating system time
1380-- @param game The game object as created in setup()
1381-- @return elapsed How much time has elapsed since time was updated
1382function updateClock(game)
1383 game.nowTime = os.clock()
1384 local elapsed = game.nowTime - game.lastClock
1385 game.lastClock = game.nowTime
1386 return elapsed
1387end
1388
1389--- Updates all kews in the game object based on elapsed time
1390-- Since Kews do the timekeeping, let them know how much time as elapsed.
1391-- @param game The game object as created in setup()
1392-- @param elapsed Seconds as calculated by updateClock()
1393function doTimerUpdates(game,elapsed)
1394 for i,kew in ipairs(game.queues) do
1395 --print("updating timer for zone: ", i)
1396 --print("timer is : ", kew.timer)
1397 --print("phase is : ", kew.phase)
1398 kew.timer = kew.timer - elapsed
1399 end
1400end
1401
1402--- Deal with players who should be in Buildzones
1403-- Checks all Kews (and therefore Buildzones). If the Kew is in Phase 2
1404-- (Play Phase), then check on all players. If those players are out
1405-- of bounds, move them back to the closest edge of the buildzone.
1406-- This also sends them a bi-lingual warning if there were moved.
1407-- @param game The game object as created in setup()
1408function checkBoundaries(game)
1409 --debugT("begin checking boundaries")
1410 for i,kew in ipairs(game.queues) do
1411 --debugT("checking boundaries for buildzone #"..kew.buildzone.locid)
1412 if kew.phase == registry.QUEUE_PHASE.PLAYING then
1413 --boundaries
1414 --debugT("buildzone #"..kew.buildzone.locid.." is in phase 2")
1415 local x_min = kew.buildzone.x-2
1416 local x_max = kew.buildzone.x+kew.buildzone.w+4
1417 local z_min = kew.buildzone.z-2
1418 local z_max = kew.buildzone.z+kew.buildzone.w+4
1419
1420 --local toBeCorrected = {}
1421
1422 for name,player in pairs(kew.playerlist) do
1423 --debugT("checking boundaries for player "..player.name.." indexed as "..name)
1424 if player.status == registry.PLAYER_STATUS.IN_GAME then
1425 --debugT("player "..name.." is active")
1426 --debugT("calling getAllPos for player "..player.name)
1427 local listOfOne = getAllPos('m=2,name='..player.name)
1428 --debugT("getAllPos for player "..player.name.." completed")
1429 if listOfOne and listOfOne[1] then
1430 --debugT("getAllPos for player "..player.name.." returned a value")
1431 player.x = listOfOne[1].x
1432 player.y = listOfOne[1].y
1433 player.z = listOfOne[1].z
1434 local changed = false
1435 if player.x > x_max then
1436 changed = true
1437 player.x = x_max-4
1438 end
1439 if player.x < x_min then
1440 changed = true
1441 player.x = x_min+4
1442 end
1443 if player.z > z_max then
1444 changed = true
1445 player.z = z_max-4
1446 end
1447 if player.z < z_min then
1448 changed = true
1449 player.z = z_min+4
1450 end
1451 if changed then
1452 teleportToPoint(player.x,kew.buildzone.y+2,player.z,{player},false,
1453 "TELEPORTED BACK TO GAME: Please stay inside the building zone or use HOMECOMER to leave the game!",
1454 "Zurück ins Spiel teleportiert: Bitte bleib innerhalb des Baufeldes oder nutze den HEIMKEHRER um das Spiel zu verlassen!")
1455 end
1456 end
1457 end
1458 --debugT("Completed: checking boundaries for player "..player.name.." indexed as "..name)
1459 end
1460 end
1461 end
1462 --debugT("end checking boundaries")
1463end
1464
1465
1466--- Check if a Buildzone is worth saving
1467-- Many plays of a buildzone will be junk. Here you can set the logic
1468-- which determines if a buildzone was valuable.
1469-- Currently always returns that the zone is valuable.
1470-- @param buildzone The buildzone to check.
1471-- @return True is the buildzone was junk
1472function checkIfBuildzoneIsCrap(buildzone)
1473 debugT("Buildzone was ok")
1474 return false
1475end
1476
1477
1478--- Everything that happens when the Play Phase finishes due to time limit.
1479-- Removes the ring which denotes a game in progress.
1480-- Removes Detector Blocks and then fills the floor in.
1481-- Checks if the buildzone was valuable and if so it will clean it.
1482-- Sets the zone played state based on value. A played zone will not be
1483-- overwritten by future buildzones.
1484-- Bilingual messages are sent to players to let them know what happened.
1485-- @param kew A kew object that contains a Buildzone
1486function cleanAfterGameOver(kew)
1487 local buildzone = kew.buildzone
1488 debugT("cleanAfterGameOver started for buildzone #".. buildzone.locid)
1489 commands.async.setblock(buildzone.x,buildzone.y,buildzone.z,registry.BLOCKS.VICTORY_MARKER.block)
1490 fillRing("minecraft:air",buildzone.x,buildzone.y,buildzone.z,buildzone.w,1)
1491 --for each level remove playing blocks like detectors
1492 for h=1,256-buildzone.y do
1493 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)
1494 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)
1495 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)
1496 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)
1497 end
1498 --replaces air on the bottom level with flooring to show the area is completed
1499 commands.async.fill(buildzone.x,buildzone.y,buildzone.z,buildzone.x+buildzone.w,buildzone.y,buildzone.z+buildzone.w,registry.BLOCKS.CAMP_FLOOR.block,registry.BLOCKS.CAMP_FLOOR.data,"replace",registry.BLOCKS.PLUG.block,registry.BLOCKS.PLUG.data)
1500 commands.async.fill(buildzone.x,buildzone.y+1,buildzone.z,buildzone.x+buildzone.w,buildzone.y,buildzone.z+buildzone.w,registry.BLOCKS.PHVFLOOR.block,registry.BLOCKS.PHVFLOOR.data,"replace","minecraft:air","0")
1501
1502 ---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
1503
1504 local wasCrap = checkIfBuildzoneIsCrap(buildzone)
1505 local gameovermessageEN
1506 local gameovermessageDE
1507 if wasCrap then
1508 --mark this buildzone for replacement
1509 --change the flag to played=false
1510 debugT("cleanAfterGameOver is crap ".. buildzone.locid)
1511 updateZonePlayedState(buildzone,false)
1512 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"
1513 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"
1514
1515 else
1516 --change the flag to played=true
1517 debugT("cleanAfterGameOver otherwise ".. buildzone.locid)
1518 updateZonePlayedState(buildzone,true)
1519
1520 gameovermessageEN = "Thank you for playing IBA_GAME! Play another game or look for your game result at: www.20000blocks.com"
1521 gameovermessageDE = "Vielen Dank, dass du IBA_GAME gespielt hast! Starte eine neue Runde oder schau dir deine Spielergebnisse an unter: www.20000blocks.com"
1522 end
1523
1524 for name, player in pairs(kew.playerlist) do
1525 if registry.ANNOUNCE_ENGLISH then
1526 -- announce success in English
1527 commands.async.tellraw(player.name,'["",{"text":"'..gameovermessageEN..'","color":"white"}]')
1528 end
1529 if registry.ANNOUNCE_GERMAN then
1530 -- announce success in German
1531 commands.async.tellraw(player.name,'["",{"text":"'..gameovermessageDE..'","color":"gold"}]')
1532 end
1533 end
1534
1535end
1536
1537--- Changes a given locid to played.
1538-- Strange that the LOCS object is a global and not in the registry.
1539-- That is probably bad. This function also saves the given location to
1540-- file.
1541-- @param buildzone The buildzone that was just finished
1542-- @param newstate Boolean Has the location been played successfully?
1543function updateZonePlayedState(buildzone, newstate)
1544 debugT("updating buildzone's #"..buildzone.locid.." played stated to: "..tostring(newstate))
1545 --change the flag to played=newstate
1546 LOCS[buildzone.locid].played = newstate
1547 --and write the LOCS object to the json file
1548 writeGridToFile()
1549end
1550
1551--- Process timekeeping and update Phases of Kews.
1552-- Run every game step. Iterate over all Kews and update their phase
1553-- based on the remaining time in kew.timer.
1554-- If the kew is on the Play Phase, then do the gameplay logic
1555-- stored in updatePlayedZone().
1556-- Update player timers (xp bar timer) and give players a large warning
1557-- title if the game is almost over.
1558-- @param game The game object as created by setup()
1559function doPhaseUpdates(game)
1560 for i,kew in ipairs(game.queues) do
1561
1562 local minutes = string.format("%02d",math.floor(kew.timer/60))
1563 local seconds = string.format("%02d",math.floor(kew.timer - (minutes*60)))
1564 if kew.timer <= 0 then
1565 minutes = "00"
1566 seconds = "00"
1567 end
1568
1569 if kew.phase == registry.QUEUE_PHASE.DORMANT then
1570 --waiting phase
1571 -- Never start the wait phase until someone joins
1572 if countActivePlayers(kew) == 0 then
1573 kew.timer = kew.phases[kew.phase].length
1574 else
1575 -- but if we have a player then start now
1576 kew.timer = 0
1577 end
1578
1579 --displayTitleToPlayers(kew.playerlist,"Game starting!")
1580 displayTimeToPlayers(kew.playerlist,minutes,seconds)
1581 elseif kew.phase == registry.QUEUE_PHASE.PLAYING then
1582 --playing phase
1583 --debugT("buildzone #"..)
1584 local victory = updatePlayedZone(kew) --currently victory updatePlayedZone returns always false
1585 displayTimeToPlayers(kew.playerlist,minutes,seconds)
1586 if victory then
1587 kew.timer = -2
1588 end
1589
1590 -- unless finish if all players quit
1591 if countActivePlayers(kew) == 0 then
1592 kew.timer = -1
1593 end
1594 elseif kew.phase == registry.QUEUE_PHASE.GAMEOVER then
1595 --end phase
1596 displayTitleToPlayers(kew.playerlist,"Times Up!","Use HOMECOMER to return to spawn")
1597 displayTimeToPlayers(kew.playerlist,minutes,seconds)
1598 end
1599 end
1600end
1601
1602--- Update the Minecraft Scoreboards based on what players built.
1603-- This runs after a buildzone is completed it currently rewards all
1604-- participants with more played score.
1605-- @param kew A Kew object which has just moved from Phase PLAYING to GAMEOVER.
1606function processHighscores(kew)
1607 local buildzone = kew.buildzone
1608 for name,player in pairs(kew.playerlist) do
1609 commands.async.scoreboard("players","add",player.name,"played",1)
1610 end
1611end
1612
1613--- Export a Kews buildzone detail once it is complete
1614-- Requires a kew so it can access the playerlist.
1615-- @param kew The Kew object which contains players and a buildzone
1616-- @param saveOnline Boolean true: save the game also online to the database; false: save only in a file locally
1617local function exportKewData(kew, saveOnline)
1618 local buildzone = kew.buildzone
1619 local saved = {}
1620 saved.position =
1621 {
1622 x=buildzone.x,
1623 y=buildzone.y,
1624 z=buildzone.z
1625 }
1626
1627 saved.players = {}
1628 for name, player in pairs(kew.playerlist) do
1629 table.insert(saved.players,player.name)
1630 end
1631 --saved.structures = buildzone.structures
1632 saved.buildings = buildzone.buildings
1633 saved.totals = tracker.tallyTable(buildzone.buildings)
1634 --saved.highest = buildzone.highest
1635 saved.stats = {
1636 cityVersion = registry.CITY_VERSION,
1637 height = buildzone.highest,
1638 densityIndex = math.floor(100*buildzone.filledSlots/49), -- the density index is made from built area (filledSlots) over the ground area (7x7 slots = 49)
1639 greenIndex = math.floor(100*buildzone.greenSlots/49), --the green index is made from green area (greenSlots) over the ground area (7x7 slots = 49)
1640 variety = tablelength(buildzone.variety),
1641 timeCompleted = timestamp,
1642 gameLength = registry.PHASE_LENGTH_GAME
1643 }
1644
1645 fs.makeDir("/records")
1646 local file = fs.open("/records/"..kew.filename..".json","w")
1647 local filecontent = json.encodePretty(saved)
1648 debugT("saving the game at buildzone "..kew.buildzone.locid.." to local file...")
1649 file.write(filecontent)
1650 file.close()
1651 debugT("local file saved: ".."/records/"..kew.filename..".json")
1652 if saveOnline then --for now this is disabled until we figure out how things look in the webGL viewer
1653
1654 debugT("saving the game at buildzone "..kew.buildzone.locid.." online...")
1655 --writeToDatabase(kew.filename,filecontent,saved.position.x,saved.position.z)
1656 --debugT("saved")
1657 end
1658end
1659
1660-- this function writes to the online database that we use to display models in the webGL viewer
1661local function writeToDatabase(name,data,x,z)
1662
1663 -- the user agent needs to be renamed otherwise the dfeult one is Java and that is blocked by the .htaccess file on the website
1664 local headers = {
1665 [ "User-Agent" ] = "20.000 BLOCKS"
1666 }
1667
1668 local link = http.post(
1669 "http://www.20000blocks.com/DatabaseAccess/UploadModel.php",
1670 "name="..textutils.urlEncode(name).."&"..
1671 "content="..textutils.urlEncode(data).."&"..
1672 "x="..x.."&"..
1673 "z="..z,
1674 headers
1675 )
1676 local linkURL = link.readAll()
1677 if linkURL then
1678 --message texts
1679 local msg_EN = 'The latest game result has been uploaded to the webviewer.\n'
1680 local msg_DE = 'Die neueste Runde wurde in den Webviewer geladen.\n'
1681 local linkText_EN = 'See it and share it!'
1682 local linkText_DE = 'Schau es und teile es!'
1683 local hoverText_EN = 'Click here to see and share the last game!'
1684 local hoverText_DE = 'Click here to see and share the last game!'
1685 --message text with the link
1686 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}]'
1687
1688 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}]'
1689 -- announce success in English
1690 commands.async.tellraw("@a",linkmsg_EN)
1691 if registry.ANNOUNCE_GERMAN then
1692 --announce success in German
1693 commands.async.tellraw("@a",linkmsg_DE)
1694 end
1695 end
1696end
1697
1698function 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)
1699 http.post(
1700 "http://www.20000blocks.com/DatabaseAccess/SwitchVersion.php",
1701 "id="..textutils.urlEncode(id).."&"..
1702 "relative="..textutils.urlEncode(relative)
1703 )
1704end
1705
1706--- Update the blocks in a wait area
1707-- Takes three parameters which decide the base, top and middle blocks
1708-- of the portal. This is also used to "close" a portal to players
1709-- @param portal_location A x,y,z coord object. A kew.portal can be passed
1710-- @param queue_phase The phase in which the game queue of the buildzone corresponding to this portal is currently in
1711function drawPortal(portal_location,queue_phase) --baseblock,middleblock,topblock)
1712 debugT("drawing a portal at: "..portal_location.x..", "..portal_location.y..", "..portal_location.z)
1713 if queue_phase == registry.QUEUE_PHASE.DORMANT then
1714 --fill with air the ground level
1715 params = {
1716 portal_location.x,
1717 portal_location.y,
1718 portal_location.z,
1719 portal_location.x+registry.PORTAL.SIZE-1,
1720 portal_location.y,
1721 portal_location.z+registry.PORTAL.SIZE-1
1722 }
1723 debugT("drawing portal with params: "..json.encode(params))
1724 --[[
1725 fillSmart(
1726 portal_location.x,
1727 portal_location.y,
1728 portal_location.z,
1729 portal_location.x+registry.PORTAL.SIZE-1,
1730 portal_location.y,
1731 portal_location.z+registry.PORTAL.SIZE-1,
1732 registry.BLOCKS.AIR
1733 )
1734 --fill with detector blocks one level below ground
1735 fillSmart(
1736 portal_location.x,
1737 portal_location.y-1,
1738 portal_location.z,
1739 portal_location.x+registry.PORTAL.SIZE-1,
1740 portal_location.y-1,
1741 portal_location.z+registry.PORTAL.SIZE-1,
1742 registry.BLOCKS.DETECT
1743 )
1744 --]]
1745 elseif queue_phase == registry.QUEUE_PHASE.PLAYING then
1746 --fill with air the ground level
1747 --fillSmart(portal_location.x,portal_location.y,portal_location.z,portal_location.x+registry.PORTAL.SIZE-1,portal_location.y,portal_location.z+registry.PORTAL.SIZE-1,registry.BLOCKS.AIR )
1748 --fill with detector blocks one level below ground
1749 --fillSmart(portal_location.x,portal_location.y-1,portal_location.z,portal_location.x+registry.PORTAL.SIZE-1,portal_location.y-1,portal_location.z+registry.PORTAL.SIZE-1,registry.BLOCKS.DETECT )
1750 --mark with a construnction block the middle point
1751 params = {
1752 portal_location.x,
1753 portal_location.y,
1754 portal_location.z,
1755 portal_location.x+registry.PORTAL.SIZE-1,
1756 portal_location.y,
1757 portal_location.z+registry.PORTAL.SIZE-1
1758 }
1759 debugT("drawing portal with params: "..json.encode(params))
1760 commands.async.setblock(portal_location.x+(registry.PORTAL.SIZE/2),portal_location.y,portal_location.z+(registry.PORTAL.SIZE/2),registry.BLOCKS.CONSTRUCTION.block,registry.BLOCKS.CONSTRUCTION.data)
1761 elseif queue_phase == registry.QUEUE_PHASE.GAMEOVER then
1762 --fill with construction block the ground level
1763 params = {
1764 portal_location.x,
1765 portal_location.y,
1766 portal_location.z,
1767 portal_location.x+registry.PORTAL.SIZE-1,
1768 portal_location.y,
1769 portal_location.z+registry.PORTAL.SIZE-1
1770 }
1771 debugT("drawing portal with params: "..json.encode(params))
1772 --fillSmart(portal_location.x,portal_location.y,portal_location.z,portal_location.x+registry.PORTAL.SIZE-1,portal_location.y,portal_location.z+registry.PORTAL.SIZE-1,registry.BLOCKS.CONSTRUCTION )
1773 end
1774 --[[
1775 if baseblock then
1776 commands.async.fill(zone.x,zone.y,zone.z,zone.x+registry.PORTAL.SIZE-1,zone.y,zone.z+registry.PORTAL.SIZE-1,baseblock.block,baseblock.data)
1777 end
1778
1779 if topblock then
1780 commands.async.fill(zone.x,zone.y+1,zone.z,zone.x+registry.PORTAL.SIZE-1,zone.y+1,zone.z+registry.PORTAL.SIZE-1,topblock.block,topblock.data)
1781 end
1782
1783 if middleblock then
1784 commands.async.setblock(zone.x+(registry.PORTAL.SIZE/2),zone.y+1,zone.z+(registry.PORTAL.SIZE/2),middleblock.block,middleblock.data)
1785 end
1786 --]]
1787
1788end
1789
1790--- Move from the Wait Phase to the Play Phase
1791-- Anything that needs to be done ONCE before the Play Phase starts
1792-- happens here.
1793-- The buildzone is moved to a clean area.
1794-- The players in the Kew are moved to the buildzone.
1795-- The buildzone is cleaned up (now that the chunks are loaded
1796-- The buildzone is prepares (detector blocks placed)
1797-- Players recieve starting items
1798-- A friendly message is shown for them to begin
1799-- Kew Phase and Timer are updated
1800-- @param kew The Kew to update
1801function endWaitPhase(kew)--, game)
1802 --moveBuildzone(kew.buildzone,game.builds)
1803 --teleportToZone(kew.buildzone,kew.playerlist,"Your game has started! BUILD A HOUSE!", "Das Spiel hat begonnen! BAUE EIN HAUS!")--teleport selected players
1804 debugT("ending the wait phase for buildzone "..kew.buildzone.locid)
1805 cleanBuildzone(kew.buildzone)
1806 prepareBuildzone(kew.buildzone)--prepare build zone
1807 --giveItems(kew.playerlist,registry.STARTING_ITEMS) --Starting items are given as players join now, not all at once
1808 displayTitleToPlayers(kew.playerlist,"BUILD!","Place your resources and stand on a detector block.")
1809 kew.victory = false
1810 --displayTime(kew.buildzone.selector,0,0)
1811 kew.phase = registry.QUEUE_PHASE.PLAYING
1812 kew.timer = kew.phases[kew.phase].length
1813 drawPortal(kew.portal,kew.phase)
1814 debugT("wait phase for buildzone "..kew.buildzone.locid.. " - ended")
1815end
1816
1817--- Move from the Play Phase to the End Phase
1818-- Anything that needs to be done ONCE before the End Phase starts
1819-- happens here.
1820-- Player highscores are updated
1821-- The Kew and Buildzone data is saved to file
1822-- The buildzone is given a clean and checked for value
1823-- Kew Phase and Timer are updated
1824-- @param kew The Kew to update
1825function endPlayPhase(kew)
1826 debugT("ending the play phase for buildzone "..kew.buildzone.locid)
1827 processHighscores(kew)
1828 --exportKewData(kew,true) -- saves the final state of the game and writes it to the online database -- DISABLED BY BEN
1829 cleanAfterGameOver(kew)
1830 kew.phase = registry.QUEUE_PHASE.GAMEOVER
1831 --displayTime(kew.buildzone.selector,0,0)
1832 kew.timer = kew.phases[kew.phase].length
1833 drawPortal(kew.portal,kew.phase)
1834 debugT("play phase for buildzone "..kew.buildzone.locid.. " - ended")
1835end
1836
1837--- Move from the End Phase to the Wait Phase
1838-- Anything that needs to be done ONCE before the End Phase starts
1839-- happens here.
1840-- Players are removed from the Kews playerlist
1841-- Kew Phase and Timer are updated
1842-- @param kew The Kew to update
1843function endEndPhase(kew)
1844 debugT("ending the final phase for buildzone "..kew.buildzone.locid)
1845 removePlayersFromKew(kew)
1846 kew.phase = registry.QUEUE_PHASE.DORMANT
1847 --displayTime(kew.buildzone.selector,0,0)
1848 kew.timer = kew.phases[kew.phase].length
1849 drawPortal(kew.portal, kew.phase)
1850 debugT("final phase for buildzone "..kew.buildzone.locid.. " - ended")
1851end
1852
1853--- Calculate end of Phase and change to next Phase
1854-- This code runs ONCE at the end of each phase and what actually
1855-- happens is specific to which phase the given kew is in.
1856-- Kew timers are filled and Kew Phases are updated here only.
1857-- @param game The game object as created in setup()
1858function doPhaseEnds(game)
1859 for i,kew in ipairs(game.queues) do
1860 if kew.timer <= 0 then
1861 if kew.phase == registry.QUEUE_PHASE.DORMANT then
1862 --waiting phase ends goto play phase
1863 endWaitPhase(kew) --, game)
1864 elseif kew.phase == registry.QUEUE_PHASE.PLAYING then
1865 --playing phase ends goto end phase
1866 endPlayPhase(kew)
1867 elseif kew.phase == registry.QUEUE_PHASE.GAMEOVER then
1868 --end phase ends goto waiting phase
1869 endEndPhase(kew)
1870 end
1871 end
1872 end
1873end
1874
1875
1876-- Replaces important blocks such as Detectors
1877-- Replaces everything that is needed to start the game. Does not
1878-- rebuild the floor, or clear anything away. based on the settings it
1879-- creates a full grid, or a partial grid, or no grid of Detectors
1880-- it also places the ring, although this is disabled for now
1881-- @param buildzone The buildzone to prepare
1882function prepareBuildzone(buildzone)
1883 debugT("Preparing buildzone "..buildzone.locid.." ...")
1884 local bz = buildzone
1885 local x,y,z,w = bz.x,bz.y,bz.z,bz.w
1886 --debugT("fillSmart from inside prepareBuildzone")
1887 --place the white grid accross the full buildzone as a base
1888 --commands.async.fill(x,y,z,x+w,y,z+w,registry.BLOCKS.CAMP_FLOOR.block)
1889 fillSmart(x,y,z,w,1,w,registry.BLOCKS.CAMP_FLOOR)
1890 --fillRing(buildzone.x,buildzone.y-1,buildzone.z,buildzone.w,registry.BLOCKS.CONSTRUCTION.block) --this draws the construction stripe around the buildzone
1891 --create the grid of detectors surrounded by plus plugs
1892 if registry.DO_GRID then
1893 --fillGrid()
1894 local halfCell = math.floor(registry.GRIDCELL_SIZE/2)
1895 for x=0,registry.GRIDCELL_COUNT-1 do
1896 for z=0,registry.GRIDCELL_COUNT-1 do
1897 local rand = math.random()*100
1898 if rand > registry.GRID_HOLE_CHANCE then --and result then
1899 commands.async.setblock(bz.x+(x*registry.GRIDCELL_SIZE)+halfCell,bz.y+1,bz.z+(z*registry.GRIDCELL_SIZE)+halfCell,registry.BLOCKS.DETECT.block,registry.BLOCKS.DETECT.data,"replace","minecraft:air")
1900 commands.async.fill(bz.x+(x*registry.GRIDCELL_SIZE)+halfCell-registry.PLUG_LENGTH, bz.y,bz.z+(z*registry.GRIDCELL_SIZE)+halfCell,bz.x+(x*registry.GRIDCELL_SIZE)+halfCell+registry.PLUG_LENGTH,bz.y,bz.z+(z*registry.GRIDCELL_SIZE)+halfCell,registry.BLOCKS.PLUG.block)
1901 commands.async.fill(bz.x+(x*registry.GRIDCELL_SIZE)+halfCell,bz.y,bz.z+(z*registry.GRIDCELL_SIZE)+halfCell-registry.PLUG_LENGTH,bz.x+(x*registry.GRIDCELL_SIZE)+halfCell,bz.y,bz.z+(z*registry.GRIDCELL_SIZE)+halfCell+registry.PLUG_LENGTH,registry.BLOCKS.PLUG.block)
1902 end
1903 end
1904 end
1905 end
1906 --mark the game in the LOCS array as not available anymore, and save the updated game grid to the grid file
1907 --change the flag to played=true
1908 --print("prepareBuildzone", buildzone.locid)
1909 updateZonePlayedState(buildzone,true)
1910 debugT("Buildzone "..buildzone.locid.." is prepared.")
1911end
1912
1913-- Removes everything in a buildzone
1914-- Literally replaces every block inside the buildzone with air and
1915-- then removes all floating items.
1916-- @param buildzone A buildzone to clean
1917function cleanBuildzone(buildzone)
1918 debugT("Cleaning buildzone "..buildzone.locid.." ...")
1919 --for each level, remove all blocks
1920 debugT("fillSmart from inside cleanBuildzone")
1921 fillSmart(buildzone.x-1, buildzone.y, buildzone.z-1, buildzone.w+2, 256-buildzone.y, buildzone.w+2, registry.BLOCKS.AIR)
1922 --for h=buildzone.y,255 do
1923 -- commands.async.fill(buildzone.x,h,buildzone.z,buildzone.x+buildzone.w,h,buildzone.z+buildzone.w,"minecraft:air")
1924 --end
1925 --remove all floating items in the loaded part of the world
1926 commands.async.kill("@e[type=item]")
1927 debugT("Buildzone "..buildzone.locid.." is cleaned.")
1928end
1929
1930--- Takes players out of a Kews playerlist
1931-- Gives all players a message telling them to return to spawn and
1932-- then removes them from the playerlist. This releases them from being
1933-- trapped in the buildzone boundary.
1934-- @param kew The Kew to clear players from
1935function removePlayersFromKew(kew)
1936 debugT("removing all players from the list for buildzone "..kew.buildzone.locid.." ...")
1937 for name, player in pairs(kew.playerlist) do
1938 if registry.ANNOUNCE_ENGLISH then
1939 -- announce success in English
1940 commands.async.tellraw(player.name,'["",{"text":"TIME OUT! GAME COMPLETE! Use your HOMECOMER to return to spawn.","color":"white"}]')
1941 end
1942 if registry.ANNOUNCE_GERMAN then
1943 -- announce success in German
1944 commands.async.tellraw(player.name,'["",{"text":"ENDE! SPIEL ABGESCHLOSSEN! Nutze den HEIMKEHRER um zum Anfang zurück zu kehren.","color":"gold"}]')
1945 end
1946 end
1947 kew.playerlist = {}
1948 debugT("Player list for buildzone "..kew.buildzone.locid.." is now empty")
1949end
1950
1951--- Resets a player to be harmless
1952-- Use this when you want to be sure a player cannot modify blocks
1953-- and that they have no wool to place. You can also send them a friendly
1954-- message about why you took their stuff!
1955-- @param playername String of the players name
1956-- @param message String A friendly message for the given player
1957function resetPlayer(playername,message)
1958 debugT("resetPlayer: reseting player '"..playername.."' to adventure mdoe with no wool and pickaxe")
1959 commands.tell(playername,message)
1960 commands.async.gamemode(2,playername)
1961 commands.async.clear(playername,"minecraft:wool")
1962 commands.async.clear(playername,"minecraft:stone_pickaxe")
1963end
1964
1965--- Checks if given player is in a buildzone
1966-- Uses a playerdata object and the buildzone selector
1967-- @param player A playerdata object from newPlayerData()
1968-- @param buildzone A buildzone object to get the selector from
1969-- @return result True if the player is in the buildzone
1970function checkForPlayerInBuildzone(player,buildzone)
1971 local result,message = commands.testfor('@a[name='..player.name..','..buildzone.selector..']')
1972 return result
1973end
1974
1975--- Checks if a given player is in a given waiting area
1976-- Builds a selector for the portal from the given portal
1977-- @param player A playerdata object from newPlayerData()
1978-- @param portal A portal from a Kew object
1979-- @return result True if the player is in the wait area
1980function checkForPlayerInPortal(player,portal) --currently not used anywhere
1981 local selector = "x="..portal.x..",y="..portal.y..",z="..portal.z..",dx="..registry.PORTAL.SIZE..",dy=1,dz="..registry.PORTAL.SIZE
1982 local result,message = commands.testfor('@a[name='..player.name..','..selector..']')
1983 return result
1984end
1985
1986--- Checks if a given player is in a given area
1987-- Builds a selector for the area from the given area parameters
1988-- @param player A playerdata object from newPlayerData()
1989-- @param area Specified as a table with x,y,z,w,l,h fields
1990-- @return result True if the player is in the wait area
1991function checkForPlayerInArea(player,area) --currently not used anywhere
1992 local selector = "x="..area.x..",y="..area.y..",z="..area.z..",dx="..area.w..",dy="..area.h..",dz="..area.l
1993 local result,message = commands.testfor('@a[name='..player.name..','..selector..']')
1994 return result
1995end
1996
1997--- Gets a list of all players in a given Portal
1998-- Builds a selector for the portal from the given portal
1999-- @param portal A portal from a Kew object
2000-- @return result A list of playerdata objects from GetAllPos()
2001function checkPortal(portal)
2002 local selector = "x="..portal.x..",y="..portal.y..",z="..portal.z..",dx="..registry.PORTAL.SIZE..",dy=1,dz="..registry.PORTAL.SIZE
2003 local result = getAllPos('m=2,'..selector)
2004 return result
2005end
2006
2007
2008--- Updates our information on all players we think are in the game
2009-- Checks the waitlist and all kews. Each player is checked if they are
2010-- still online and if they are playing in a buildzone.
2011-- @param game A game object as created by setup()
2012function checkPlayers(game)
2013 --local selector = "x="..registry.WAITZONE.x..",y="..(registry.WAITZONE.y-2)..",z="..registry.WAITZONE.z..",dx="..registry.WAITZONE.w..",dy=256,dz="..registry.WAITZONE.l
2014 --local loggedIn = getAllPos('m=2,'..selector)
2015 --get all players in adventure mode and sort them around
2016 local loggedIn = getAllPos('m=2')
2017 number_of_players_adventure = tablelength(loggedIn)
2018 number_of_players_creative = tablelength(getAllPos('m=1'))
2019 --refresh waitlist
2020 --game.waitlist = loggedIn --the .waitlist property is not used anymore
2021 --check currently playing players
2022 for l,kew in ipairs(game.queues) do
2023 for name,builder in pairs(kew.playerlist) do
2024 local isPlaying = checkForPlayerInBuildzone(builder,kew.buildzone)
2025 --remove players who are already in kews from the waitlist
2026 for j, player in ipairs(loggedIn) do
2027 if player.name == builder.name then
2028 table.remove(loggedIn,j)
2029 end
2030 end
2031 --if the game is in progress and the player is not found then remove them from the gamekew
2032 if not isPlaying and kew.phase == registry.QUEUE_PHASE.PLAYING then
2033 --table.remove(kew.playerlist,i)
2034 kew.playerlist[builder.name].status = registry.PLAYER_STATUS.LEFT_OTHERWISE
2035 --print("Removed "..builder.name.." from game in progress")
2036 end
2037 end
2038 end
2039 --check if players are in the spawn area
2040 for j, player in ipairs(loggedIn) do
2041 local isInSpawnArea = checkForPlayerInArea(player, registry.MAIN_SPAWN_AREA)
2042 if isInSpawnArea then
2043 table.remove(loggedIn,j)
2044 end
2045 end
2046 --if there are still players in loggedIn then they must be force moved to spawn
2047 if tablelength(loggedIn) > 0 then
2048 debugT("teleporting free roaming players back to spawn area: "..playerListToString(loggedIn))
2049 teleportToPoint(registry.SPAWN.x,registry.SPAWN.y,registry.SPAWN.z,loggedIn,true,"You have been teleported to the spawn area. Use the portals to join a game", "(Translate to DE)You have been teleported to the spawn area. Use the portals to join a game")
2050 end
2051end
2052
2053-- Status ( 0 = in game, 1 = quit with homecomer, 2 = left game )
2054
2055function addToGame(kew,waiter)
2056 -- check if player already exists in playerlist
2057 -- teleport them
2058 -- add to playerlist if not
2059 -- add starting items if not
2060 -- update player status if they exist
2061 -- if playerlist is full, fill in the portal
2062 local buildzone = kew.buildzone
2063 local textEN = "You should never see this text"
2064 local textDE = "You should never see this text (in German)"
2065 if (#kew.playerlist > 0 and kew.playerlist[waiter.name]) then
2066 -- player has been here before so just change status to 0 and tp them
2067 --print("player was here before")
2068 kew.playerlist[waiter.name].status = registry.PLAYER_STATUS.IN_GAME
2069 textEN = "Welcome back to the buildzone."
2070 textDE = "Welcome back to the buildzone (TRANSLATE)"
2071 debugT("Adding '"..waiter.name.."' as RETURNING player to buildzone #"..buildzone.locid)
2072 else
2073 --print("player was NOT here before")
2074 kew.playerlist[waiter.name] = waiter
2075 kew.playerlist[waiter.name].status = registry.PLAYER_STATUS.IN_GAME
2076 giveItems({waiter},registry.STARTING_ITEMS)
2077 textEN = "Welcome to the buildzone. Build your first structure."
2078 textDE = "Welcome to the buildzone. Build your first structure. (TRANSLATE)"
2079 debugT("Adding '"..waiter.name.."' as NEW player to buildzone #"..buildzone.locid)
2080 end
2081 --print("buildzone", buildzone)
2082 --print("waiter", waiter)
2083 --print("textEN", textEN)
2084 --print("textDE", textDE)
2085 scatterIntoZone(buildzone, {waiter}, textEN, textDE)
2086 --teleportToPoint(buildzone.x+2+(buildzone.w/2),buildzone.y+5,buildzone.z+2+(buildzone.w/2),{waiter},false,textEN, textDE)
2087end
2088
2089function inZoneChangeMode(zone,mode)
2090 --debugT("changing every in wait zone to adventure mode")
2091 local selector = "x="..zone.x..",y="..zone.y..",z="..zone.z..",dx="..registry.PORTAL.SIZE..",dy=1,dz="..registry.PORTAL.SIZE
2092 commands.async.gamemode(mode,"@a["..selector.."]")
2093end
2094
2095-- Adds waiting players to Kew playerlists
2096-- checks which players are in which portals and adds them to games
2097-- based on that. If there are any creative mode players in the zone,
2098-- change them to adventure mode.
2099-- @param game the game object as created by setup()
2100function allocateWaiters(game)
2101 for i, kew in ipairs(game.queues) do
2102 debugT("allocateWaiters: the queues for loop start")
2103 if (kew.phase == registry.QUEUE_PHASE.DORMANT or kew.phase == registry.QUEUE_PHASE.PLAYING) and countActivePlayers(kew) < kew.maxplayers then
2104 debugT("allocateWaiters: the if statement start")
2105 inZoneChangeMode(kew.portal,2)
2106 local waiters = checkPortal(kew.portal)
2107 if #waiters > 0 then debugT("found player in launchpad") end
2108 for index,waiter in ipairs(waiters) do
2109 debugT("allocateWaiters: the waiters for loop start")
2110 addToGame(kew,waiter)
2111 debugT("allocateWaiters: the waiters for loop end")
2112 end
2113 debugT("allocateWaiters: the if statement end")
2114 end
2115 debugT("allocateWaiters: the queues for loop end")
2116 end
2117 debugT("allocateWaiters: completed")
2118end
2119
2120--- Adds a new request to check a Detector Block safely
2121-- Makes sure that incoming check requests dont exist already
2122-- Players can still have multiple requests, but no two requests from
2123-- the same tile will exist. Requests are processed at a rate of one per
2124-- game step, so its important that we dont have duplicates as it slows
2125-- the game loop down a lot.
2126-- @param player A playerdata Object as created by newPlayerData().
2127-- @param buildzone A buildzone to which the request should be added.
2128function addToChecklist(player,buildzone)
2129 for _, detector in ipairs(buildzone.waitingForCheck) do
2130 if detector.x == player.x and detector.y == player.y and detector.z == player.z then
2131 return false
2132 end
2133 end
2134 table.insert(buildzone.waitingForCheck,player)
2135 return true
2136end
2137
2138--- Cleans barriers from a buildzone
2139-- removes all barrier blocks from a buildzone which are used to
2140-- carve space in Elements.
2141-- TODO This should be updated to only clean an area
2142-- the size of an element specified with x,y,z.
2143-- @param buildzone A buildzone that should be cleaned
2144function cleanBarriers(buildzone)
2145 --debugT()
2146 for h=0,200 do
2147 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")
2148 end
2149end
2150
2151--- Update a buildzone that is in the Play Phase
2152-- Detection and Game Logic is mostly kept here. Meat and Potatoes time.
2153-- Wow, actually most of this is disabled for now.
2154-- If there are waiting requests for checking a detector block, do
2155-- the first one in the list.
2156-- Check it against the catalogue we have stored in the buildzone
2157-- (not the complete catalogue). If it matches then clone in the new
2158-- Building, give Rewards and clean Barriers.
2159-- Currently always returns False for victory.
2160-- @param kew a Kew object which contains a Buildzone
2161-- @return victory Boolean, Did this placement cause a victory?
2162function updatePlayedZone(kew)
2163 local buildzone = kew.buildzone
2164 local victory = false
2165 local buildzoneSelector = buildzone.selector
2166 --get all players on a detector block, add them to the list of things to check
2167 local detectLocations = getAllOnBlockType(registry.BLOCKS['DETECT'].block,buildzoneSelector)
2168 --print(#detectLocations.." Players standing on detectors")
2169 for _, player in ipairs(detectLocations) do
2170 addToChecklist(player,buildzone)
2171 end
2172
2173 --DEAL WITH THE DETECTOR AT THE TOP OF THE LIST IF THERE IS ONE
2174 if #buildzone.waitingForCheck > 0 then
2175 --DO PARTICLE EFFECTS IF A DETECTING BLOCK THAT IS DETECTING
2176 for i,loc in ipairs(buildzone.waitingForCheck) do
2177 searchParticle(loc.x,loc.y+1,loc.z)
2178 end
2179 local totalResult = false
2180 local checked = table.remove(buildzone.waitingForCheck,1)
2181 local x,y,z,name = checked.x,checked.y,checked.z,checked.name
2182 for i,element in pairs(buildzone.elements) do
2183 local result,message = commands.testforblocks( element.keyX, element.keyY, element.keyZ, element.keyX+element.sizeX, element.keyY+registry.VOCAB_HEIGHT, element.keyZ+element.sizeZ, x-math.floor(element.sizeX/2), y-1, z-math.floor(element.sizeZ/2),"masked")
2184 if result then
2185 --clone in the correct vocab
2186 local cloneres,clonemes = commands.clone( element.elementX, element.elementY, element.elementZ, element.elementX+element.sizeX, element.elementY+registry.VOCAB_HEIGHT, element.elementZ+element.sizeZ, x-math.floor(element.sizeX/2), y-1, z-math.floor(element.sizeZ/2),"masked")
2187 --debugT(clonemes[1])
2188 commands.async.give(name,element.reward)
2189 debugT("successful detect of element #"..i.." in buildzone #"..buildzone.locid.." at:"..x..", "..y..", "..z)
2190 -- announce success in English
2191 local rewardType = 'nature'
2192 local rewardTypeDE = 'grüne'
2193 if element.rewardUrban then
2194 rewardType = 'urban'
2195 rewardTypeDE = 'urbane'
2196 end
2197
2198 if registry.ANNOUNCE_ENGLISH then
2199 --announce success in English
2200 commands.async.tellraw(name,'["",{"text":"You built a '..element.nameEN.. ' ('..element.typeEN..'), which is '..element.height..'m tall and gives '..element.slots..' density pts, '..element.greenSlots..' green pts and '..element.rewardCount..'x '..rewardType..' resource!","color":"white"}]')
2201 end
2202 if registry.ANNOUNCE_GERMAN then
2203 -- announce success in German
2204 commands.async.tellraw(name,'["",{"text":"Du hast ein '..element.typeDE..' | '..element.nameDE.. ' gebaut, das '..element.height..' Meter hoch ist und dir '..element.slots..' Punkte für die Dichte einbringt, jedoch '..element.greenSlots..' Punkte für Grünflächen und '..element.rewardCount..' '..rewardTypeDE..' Ressourcen!","color":"gold"}]')
2205 end
2206
2207 --clear out barrier blocks
2208 cleanBarriers(buildzone)
2209
2210 --ADD THE NEW STRUCTURE TO THE RECORDS
2211 --table.insert(buildzone.structures,element.nameEN)
2212 --record the place of the element as x, y, z and element id
2213 local building = {id=element.id,xpos=x,ypos=y-1,zpos=z,name=element.nameEN,time=os.clock(),player=name}
2214 table.insert(buildzone.buildings, building)
2215
2216 --[[ DISABLED UNTIL THE NEW GOAL SYSTEM IS IMPLEMENTED
2217
2218 buildzone.greenSlots = buildzone.greenSlots + element.greenSlots
2219
2220 buildzone.filledSlots = buildzone.filledSlots + element.slots
2221 local newHeight = y + element.height - buildzone.y-1 -- the Y coordinate of the highest block above the ground. Our world has its ground at 55 which is in buildzone.y, subtracting 1 to compensate for player height
2222 if newHeight > buildzone.highest then
2223 buildzone.highest = newHeight
2224 end
2225
2226 -- count variety
2227 local varietyId = element.id -- we use a variety id instead of the element id because vocab id 1,2,3,4 for example are all houses so it is the same variety
2228 --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
2229 --]]
2230 --[[
2231 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
2232 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
2233 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
2234 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
2235 if varietyId ~= 17 and varietyId ~= 18 then --skip the two riser as they are not buildings
2236 --]]
2237 --[[
2238 if buildzone.variety[varietyId] then
2239 --print("increasing existing item")
2240 buildzone.variety[varietyId] = buildzone.variety[varietyId] + 1
2241 else
2242 --print("adding new item")
2243 buildzone.variety[varietyId] = 1
2244 end
2245 --end
2246
2247 --save the game incrementally
2248 exportKewData(kew,false) -- saves the new state of the game locally to a file, doenst write to the database yet
2249
2250 --- CHECK FOR PERSONAL RECORDS
2251 --- check if the new structure is the highest
2252 --- CHANGE here to live detect the contribution of the new structure to the 4 goals and update them
2253
2254 local personalbest = tracker.getScore(name,"highest")
2255 if personalbest.count < newHeight then
2256 --commands.async.tell(name,"You just topped your personal record for highest structure!")
2257 commands.async.scoreboard("players","add",name,"highest",1)
2258 if registry.ANNOUNCE_ENGLISH then
2259 -- announce success in English
2260 commands.async.tellraw(name,'["",{"text":"You just topped your personal record for highest neighbourhood!","color":"green"}]')
2261 end
2262 if registry.ANNOUNCE_GERMAN then
2263 -- announce success in German
2264 commands.async.tellraw(name,'["",{"text":"Du hast soeben deinen persönlichen Rekord für die höchste Nachbarschaft gebrochen!","color":"gold"}]')
2265 end
2266 end
2267
2268 ---
2269 ---
2270 -- CHECK if placing the current structure would result in beating a server-wide record on the 4 goals
2271 --calculate total slots - FOR GOAL "DENSEST NEIGHBOURHOOD"
2272 local most = tracker.getScore("Densest_[points]","highscores")
2273 local Kint = math.floor(100 * buildzone.filledSlots / 49) -- Kint is the density index made from built area (filledSlots) over ground area (7x7 slots = 49)
2274 if Kint > most.count then
2275 commands.async.scoreboard("players","set","Densest_[points]","highscores",Kint)
2276 if registry.ANNOUNCE_ENGLISH then
2277 -- announce success in English
2278 commands.async.tellraw("@a",'["",{"text":"Great! '..name.. ' just topped the record for the DENSEST NEIGHBOURHOOD!","color":"green"}]')
2279 end
2280 if registry.ANNOUNCE_GERMAN then
2281 -- announce success in German
2282 commands.async.tellraw("@a",'["",{"text":"Sehr gut! '..name.. ' hat einen neuen Rekord für die DICHTESTE NACHBARSCHAFT aufgestellt!","color":"gold"}]')
2283 end
2284 end
2285
2286 -- FOR THE GOAL "MOST DIVERSE NEIGHBOURHOOD"
2287 -- here we need to count how many varieties of buildings there are
2288 --local structures = tracker.tallyTable(buildzone.structures) -- this counts the variety of buildings in a game
2289 local mostDiverse = tracker.getScore("Most-Diverse_[out-of-26]","highscores")
2290 local typeCount = tablelength(buildzone.variety)
2291 --print("variety count is: "..typeCount)
2292 if typeCount > mostDiverse.count then
2293 commands.async.scoreboard("players","set","Most-Diverse_[out-of-26]","highscores", typeCount)
2294 if registry.ANNOUNCE_ENGLISH then
2295 -- announce success in English
2296 commands.async.tellraw("@a",'["",{"text":"Wow! '..name.. ' just topped the record for the MOST DIVERSE NEIGHBOURHOOD!","color":"green"}]')
2297 end
2298 if registry.ANNOUNCE_GERMAN then
2299 -- announce success in German
2300 commands.async.tellraw("@a",'["",{"text":"Wow! '..name.. ' hat soeben einen neuen Rekord für die VIELSEITIGSTE NACHBARSCHAFT aufgestellt!","color":"gold"}]')
2301 end
2302 end
2303
2304 -- FOR THE GOAL "GREENEST NEIGHBOURHOOD"
2305 -- here we need to count the number of green vocabs
2306 local greenest = tracker.getScore("Greenest_[points]","highscores")
2307 local Gint = math.floor(100*buildzone.greenSlots/49) --Gint is the green index, made from green area (greenSlots) over ground area (7x7 slots = 49)
2308 if Gint > greenest.count then
2309 commands.async.scoreboard("players","set","Greenest_[points]","highscores",Gint)
2310 if registry.ANNOUNCE_ENGLISH then
2311 -- announce success in English
2312 commands.async.tellraw("@a",'["",{"text":"Awesome! '..name.. ' just topped the record for the GREENEST NEIGHBOURHOOD!","color":"green"}]')
2313 end
2314 if registry.ANNOUNCE_GERMAN then
2315 -- announce success in German
2316 commands.async.tellraw("@a",'["",{"text":"Klasse! '..name.. ' hat einen neuen Rekord für die GRÜNSTE NACHBARSCHAFT aufgestellt!","color":"gold"}]')
2317 end
2318 end
2319
2320 --calculate highest placement -- FOR THE GOAL "TALLEST NEIGHBOURHOOD"
2321 local highest = tracker.getScore("Tallest_[meters]","highscores")
2322 if buildzone.highest > highest.count then
2323 commands.async.scoreboard("players","set","Tallest_[meters]","highscores",buildzone.highest)
2324 if registry.ANNOUNCE_ENGLISH then
2325 -- announce success in English
2326 commands.async.tellraw("@a",'["",{"text":"Incredible! '..name..' just topped the record for TALLEST NEIGHBOURHOOD!","color":"green"}]')
2327 end
2328 if registry.ANNOUNCE_GERMAN then
2329 -- announce success in German
2330 commands.async.tellraw("@a",'["",{"text":"Unglaublich! '..name..' hat einen neuen Rekord für die HÖCHSTE NACHBARSCHAFT aufgestellt!","color":"gold"}]')
2331 end
2332 end
2333
2334
2335 --increase the "how many structures did i build" score for the building player
2336 commands.async.scoreboard("players","add",name,"built",1)
2337 commands.async.scoreboard("players","add",name,"building_"..element.id,1)
2338 --]]
2339 totalResult = true
2340 break
2341
2342 end
2343 end
2344 if totalResult then
2345 --yey win, do a happy time
2346 successParticle(x,y,z)
2347
2348 else
2349 --no vocab found so do a fail particle
2350 --announce in English
2351 commands.async.tellraw(name,'["",{"text":"The shape you have built does not match any shape in the catalogue, try a different one.","color":"red"}]')
2352 if registry.ANNOUNCE_GERMAN then
2353 -- announce in German
2354 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"}]')
2355 end
2356 failParticle(x,y-1,z)
2357 debugT("The key that player activated is not in the catalogue.")
2358 end
2359 end
2360 return victory
2361end
2362
2363local symbols = {
2364 "(X) ",
2365 "(-) "
2366 }
2367local spin = 1
2368
2369--- Display game information in the terminal
2370-- Slated for removal
2371function debugDisplayTerminal(game)
2372 --print("starting debug display")
2373 term.clear()
2374 term.setTextColor(colors.green)
2375 print("20.000 BLOCKS IS ACTIVE (HOLD CTRL+T TO QUIT) "..symbols[spin])
2376 if number_of_players_adventure and number_of_players_creative then
2377 print("TOTAL PLAYERS: "..(number_of_players_adventure + number_of_players_creative).." - ADVENTURE [ "..number_of_players_adventure.." ], CREATIVE [ "..number_of_players_creative.." ]")
2378 end
2379 term.setTextColor(colors.white)
2380 spin = spin +1
2381 if spin > #symbols then spin = 1 end
2382
2383
2384
2385 local line = 2
2386 --print("running with "..registry.NUMBER_OF_BUILDZONES.." buildzones")
2387 --print("Player Status Key ( 0 = in game, 1 = quit with homecomer, 2 = left game )")
2388 for i,kew in ipairs(game.queues) do
2389 --monitor.setCursorPos(1,line)
2390 local minutes = string.format("%02d",math.floor(kew.timer/60))
2391 local seconds = string.format("%02d",math.floor(kew.timer - (minutes*60)))
2392 print("BUILDZONE "..i.." | PHASE: "..kew.phase.." | TIME: "..kew.timer.." | PLAYERS: "..tablelength(kew.playerlist))
2393 print("-LIST: "..playerListToString(kew.playerlist))
2394 --print(json.encode(kew.playerlist))
2395 end
2396
2397 --use the remaining terminal lines to show the log
2398 -- the terminal has 19 lines, 18 available for printing, last one stays clear at all times
2399 term.setTextColor(colors.red)
2400 print("===== LATEST LOG:")
2401 for i=13-(2*#game.queues), 0, -1 do
2402 if #debug_log > i then
2403 print(debug_log[#debug_log-i].message:sub (1,51))
2404 end
2405 end
2406 term.setTextColor(colors.white)
2407
2408end
2409
2410
2411-- the main function
2412function MAIN()
2413
2414
2415
2416 term.clear()
2417 term.setTextColor(colors.blue)
2418 debugT("Starting 20.000 Blocks")
2419 debugT("Version: "..VERSION_NUMBER.." - "..VERSION_NAME)
2420 debugT("Lua v.".._G._VERSION)
2421 term.setTextColor(colors.white)
2422
2423 LOCS = buildGrid(registry.GAME_FIELD.countX, registry.GAME_FIELD.countZ)
2424
2425 local game = setup(LOCS)
2426 if not game then
2427 return
2428 end
2429
2430 --print("20.000 BLOCKS is running...")
2431 --local skip = false
2432 --if not skip then
2433 debugT("20.000 BLOCKS is running...")
2434 while true do
2435 --debugT("beginning of main game loop")
2436 if registry.DEBUG_MODE == false then
2437 term.clear()
2438 term.setTextColor(colors.green)
2439 print("20,000 Blocks is active (hold CTRL+T to quit) "..symbols[spin])
2440 term.setTextColor(colors.white)
2441 spin = spin +1
2442 if spin > #symbols then spin = 1 end
2443 else
2444 --debugT("begin debug info display")
2445 debugDisplayTerminal(game)
2446 --debugT("end debug info display")
2447 end
2448
2449 --debugT("begin update(game)")
2450 --debugT(utils.to_string(game))
2451 update(game)
2452 --debugT("end update(game)")
2453 --debugT(utils.to_string(game))
2454 --make always nice weather
2455 --commands.async.weather("clear",10000)
2456 os.sleep(0.4)
2457 --debugT("end of main game loop")
2458 end
2459 --end
2460end
2461
2462-- process command line parameters
2463local tArgs = { ... } -- get the command line arguments
2464if #tArgs == 1 then
2465 cleanbuildzone = tArgs[1]
2466elseif #tArgs > 1 then
2467 print("Usage: play <true(cleans buildzone)/false(default, doesnt clean)>")
2468 return
2469end
2470-- run the game
2471MAIN()