Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- local pkg = {}
- require "mickkay.wol.Spell"
- local setmultimap = require "adrodoc55.setmultimap"
- local store
- local width = 16
- local frequency = 1
- local heads = {}
- -- Mapping of chunk vector to all head positions with areas that intersect the chunk
- local headsByChunk = {}
- local datastore = require "mickkay.datastore"
- function saveData()
- datastore.save(store, heads)
- end
- local getChunksIntersecting
- function loadData()
- data = datastore.load(store) or {}
- heads = {}
- for i,headPos in pairs(data) do
- table.insert(heads,Vec3.new(headPos))
- end
- headsByChunk = {}
- for i,head in pairs(heads) do
- local chunks = getChunksIntersecting(head)
- for j,chunk in pairs(chunks) do
- setmultimap.put(headsByChunk, chunk, head)
- end
- end
- end
- local updatePlayer
- local removeBrokenHeads
- local onRightClickBlockEvent
- function pkg.start(options)
- spell:singleton("mickkay.claim")
- store = spell.pos
- options = options or {}
- width = options.width or width
- frequency = options.frequency or frequency
- loadData()
- local queue = Events.connect("RightClickBlockEvent")
- while true do
- local players = Entities.find("@a")
- for i,player in pairs(players) do
- updatePlayer(player)
- end
- local dirty = removeBrokenHeads()
- local event = queue:pop(1)
- if event ~= nil then
- dirty = dirty or onRightClickBlockEvent(event)
- end
- if dirty then
- saveData()
- end
- end
- end
- Help.on(pkg.start) [[
- Allows players to 'claim' an area by placing their own head in the center of it. An area is protected by setting other players that enter the area into adventure mode. Areas can be shared between multiple players by placing multiple heads on top of each other (same x and z coordinate). If two areas overlap each other, the intersection is protected from both players.
- Options:
- - width: How many blocks around the skull are protected. Default is 16 which results in 33x33 areas.
- - frequency: Every 'frequency' ticks the gamemodes of players are updated and all skulls are checked to make sure they are still there.
- ]]
- function pkg.stop()
- spell:singleton("mickkay.claim")
- end
- Help.on(pkg.stop) [[
- Disables 'claiming' of areas. Existing areas are kept persistent.
- ]]
- local isHeadOfOwner
- function updatePlayer(player)
- if player.dimension ~= 0 then
- return -- claiming is only supported in the overworld
- end
- local pos = player.pos
- local chunkX = pos.x // 16
- local chunkZ = pos.z // 16
- local chunk = chunkX..'/'..chunkZ
- local heads = headsByChunk[chunk]
- local ownHeadsXZ = {}
- local foreignHeadsXZ = {}
- if heads then
- for i,head in pairs(heads) do
- if head.x - width < pos.x
- and head.z - width < pos.z
- and head.x + width + 1 > pos.x
- and head.z + width + 1 > pos.z
- then
- local xz = head.x.."/"..head.z
- if isHeadOfOwner(head, player.name) then
- ownHeadsXZ[xz] = true
- else
- foreignHeadsXZ[xz] = true
- end
- end
- end
- end
- for xz,_ in pairs(ownHeadsXZ) do
- foreignHeadsXZ[xz] = nil
- end
- local mayBuild = next(foreignHeadsXZ) == nil
- if not mayBuild and player.gamemode == "survival" then
- player.gamemode = "adventure"
- elseif mayBuild and player.gamemode == "adventure" then
- player.gamemode = "survival"
- end
- end
- local getBlock
- local isPlayerHead
- function removeBrokenHeads()
- local dirty = false
- for i=#heads,1,-1 do
- local head = heads[i]
- local block = getBlock(head)
- if not isPlayerHead(block) then
- table.remove(heads, i)
- local chunks = getChunksIntersecting(head)
- for i,chunk in pairs(chunks) do
- setmultimap.remove(headsByChunk, chunk, head)
- end
- dirty = true
- end
- end
- return dirty
- end
- function isHeadOfOwner(pos, owner)
- local block = getBlock(pos)
- return isPlayerHead(block) and block.nbt.Owner.Name == owner
- end
- function getBlock(pos)
- spell.pos = pos
- return spell.block
- end
- function isPlayerHead(block)
- return block.name == "skull" and block.nbt.Owner
- end
- local cities = require "mickkay.cities"
- local citiesProxy = cities.proxy()
- function onRightClickBlockEvent(event)
- if event.player.dimension ~= 0 then
- return false -- claiming is only supported in the overworld
- end
- spell.pos = event.pos
- spell:move(event.face)
- local block = spell.block
- if block.name ~= "skull" or not block.nbt.Owner then
- return false -- not dirty
- end
- local head = spell.pos
- if not citiesProxy.isInsideCityCenter(head) then
- spell:execute("setblock "..head.x.." "..head.y.." "..head.z.." air 0 destroy")
- return false -- not dirty
- end
- table.insert(heads, head)
- local chunks = getChunksIntersecting(head)
- for i,chunk in pairs(chunks) do
- setmultimap.put(headsByChunk, chunk, head)
- end
- return true -- dirty
- end
- function getChunksIntersecting(head)
- local minChunkX = (head.x - width) // 16
- local maxChunkX = (head.x + width + 1) // 16
- local minChunkZ = (head.z - width) // 16
- local maxChunkZ = (head.z + width + 1) // 16
- local chunks = {}
- for chunkX=minChunkX,maxChunkX,1 do
- for chunkZ=minChunkZ,maxChunkZ,1 do
- table.insert(chunks, chunkX..'/'..chunkZ)
- end
- end
- return chunks
- end
- return pkg
Add Comment
Please, Sign In to add comment