# Stone Code
local Chat = class.Chat.new();
local Player = class.Player.new();
local Backpack = class.Backpack.new();
local Block = class.Block.new();
local World = class.World.new();
local WorldContainer = class.WorldContainer.new()
local Trigger = class.Trigger.new()
local TItem = Trigger.Item
--TESTING--
local isDebug = false --Do not commit true --
local function DEBUG(text)
if isDebug then
Chat:sendChat(text)
end
end
local function sendSystemMsg(text)
Chat:sendSystemMsg(text.." ")
end
--END--
--Const def
local SEPARATE_DISTANCE = 2
local BLOCK_AIR = 0
local BLOCK_ACTIVE = 415
local BLOCK_NORMAL = 667
local BLOCK_FLOOR = 503
local BLOCK_ERROR = 681
local BLOCK_SWITCH = 724
local BLOCK_BOX = 801
local BLOCK_ANSWER = { 671, 672 }
local DATA_ANSWER = { 2, 8 }
local ERROR_TIME = 2
local TOOLS_ID_SEPARATE = 4112
local TOOLS_ID_DYE = { 4111, 4113 }
local TOOLS_ID_DIG = 4114
local TOOLS_STRING = {
{ id = 4097 , string = "Dyeing Gun-Yellow:Dye the stone yellow. " },
{ id = 4098 , string = "Dyeing Gun-Brown:Dye the stone brown. " },
{ id = 4099 , string = "Separating Gun:Separate multiple connected stones. " },
{ id = 4101 , string = "Destroying Gun:Break the extra stones. " },
}
local ALLOWED_ITEMS = {
12963, 12550, 12003, 12050, 12574, 4100,
TOOLS_ID_SEPARATE, TOOLS_ID_DYE[1], TOOLS_ID_DYE[2], TOOLS_ID_DIG, }
local function POSITION(x, y, z) return { x = x, y = y, z = z } end
local LEVEL_CONFIG = {
{
--1
PUZZLE = { POSITION(-51, 10, 17), POSITION(-51, 10, 17) },
ANSWER = { POSITION(-39, 8, 12), POSITION(-39, 8, 12) },
RESET = { POS = POSITION(-48, 10, 17) },
REWARD = { POS = POSITION(-53, 8, 20), ID = 12963 },
},
{
--2
PUZZLE = { POSITION(-60, 10, 13), POSITION(-56, 10, 13) },
ANSWER = { POSITION(-38, 9, 16), POSITION(-34, 9, 16) },
RESET = { POS = POSITION(-63, 10, 15) },
REWARD = { POS = POSITION(-58, 8, 11), ID = 12550 },
},
{
--3
PUZZLE = { POSITION(-59, 10, 27), POSITION(-57, 13, 27) },
ANSWER = { POSITION(-39, 9, 24), POSITION(-37, 12, 24) },
RESET = { POS = POSITION(-62, 10, 31) },
REWARD = { POS = POSITION(-60, 9, 31), ID = 12003 },
},
{
--4
PUZZLE = { POSITION(-51, 10, 25), POSITION(-51, 13, 27) },
ANSWER = { POSITION(-33, 8, 22), POSITION(-33, 11, 24) },
RESET = { POS = POSITION(-48, 10, 27) },
REWARD = { POS = POSITION(-48, 9, 24), ID = 12050 },
},
{
--5
PUZZLE = { POSITION(-78, 12, 23), POSITION(-76, 16, 25) },
ANSWER = { POSITION(-78, 2, 23), POSITION(-76, 6, 25) },
RESET = { POS = POSITION(-89, 10, 24) },
REWARD = { POS = POSITION(-89, 9, 27), ID = 12574},
},
{
--6
PUZZLE = { POSITION(-112, 24, -48), POSITION(-107, 28, -45) },
ANSWER = { POSITION(-111, 40, -46), POSITION(-106, 44, -43) },
RESET = { POS = POSITION(-129, 12, -22) },
MONSTER = {
POS = { POSITION( -135, 16, -20 ), POSITION(-135, 16, -71),
POSITION(-84, 16, -71), POSITION(-84, 16, -20) },
ID = 2, NUM = 1 },
TRANSFER = { POS = POSITION( -129, 5, -26) },
REWARD = { POS = POSITION(-110, 19, -45), ID = 4100},
},
{
--7 Ostrich
PUZZLE = { POSITION(7, 14, -49), POSITION(11, 18, -47) },
ANSWER = { POSITION(10, 33, -50), POSITION(14, 37, -48) },
RESET = { POS = POSITION(-8, 6, -45) },
MONSTER = {
POS = { POSITION( 4, 3, -39 ), POSITION(16, 3, -39),
POSITION(16, 3, -57), POSITION(4, 3, -57) },
ID = 3, NUM = 1 },
TRANSFER = { POS = POSITION( -11, 1, -48) },
REWARD = { POS = POSITION(9, 9, -48), ID = 4100},
},
{
--8
PUZZLE = { POSITION(-51, 15, -45), POSITION(-44, 22, -43) },
ANSWER = { POSITION(-53, 31, -45), POSITION(-46, 38, -43) },
RESET = { POS = POSITION(-71, 15, -43) },
MONSTER = {
POS = { POSITION( -34, 8, -58), POSITION(-61, 8, -58),
POSITION(-61, 8, -31), POSITION(-34, 8, -31) },
ID = 4, NUM = 1 },
TRANSFER = { POS = POSITION( -71, 11, -45) },
REWARD = { POS = POSITION(-47, 9, -44), ID = 4100},
},
}
local errorPoints = {}
local areas = {}
local ID_prompts = {
{
{
3989, 3988, 3987, 3986, 3985, 3984, 3983, 3982, 3981, 3979, 3978, 3977,
3976, 3975, 3974, 3973, 3972, 3969, 3968, 3967, 3966, 3965, 3964, 3963,
zero = 3990
},
{
3961, 3960, 3959, 3958, 3957, 3956, 3955, 3954, 3953, 3951, 3950, 3949,
3948, 3947, 3946, 3945, 3944, 3941, 3940, 3939, 3938, 3937, 3936, 3935,
zero = 3962
}
},
{
{
3894, 3893, 3892, 3891, 3890, 3889, 3888, 3887, 3886, 3878, 3880, 3879,
3881, 3882, 3883, 3884, 3885, 3877, 3876, 3875, 3874, 3873, 3872, 3871,
zero = 3870
},
{
3868, 3867, 3866, 3865, 3864, 3863, 3862, 3861, 3860, 3852, 3853, 3854,
3855, 3856, 3857, 3858, 3859, 3851, 3850, 3849, 3848, 3847, 3846, 3845,
zero = 3869
}
},
{
{
3999, 3998, 3997, 3996, 3995, 3994, 3993, 3992, 3991, 3934, 3933, 3932,
3931, 3930, 3929, 3928, 3927, 3926, 3925, 3924, 3923, 3922, 3921, 3920,
zero = 4000
},
{
3918, 3917, 3916, 3915, 3914, 3913, 3912, 3911, 3910, 3909, 3908, 3907,
3906, 3905, 3904, 3903, 3902, 3901, 3900, 3899, 3898, 3897, 3896, 3895,
zero = 3919
}
},
max = 4000, min = 3845
}
--Const end
local function isPrompt(id)
for i = 1, #ID_prompts do
local side = ID_prompts[i]
for j = 1, #side do
local color = side[j]
if color.zero == id then
return i
end
for k = 1, #color do
if color[k] == id then
return i
end
end
end
end
return 0
end
local function isInclude(value, _table)
if not _table then
return
end
for k,v in ipairs(_table) do
if v == value then
return true
end
end
return false
end
function updateAreaFlats(area)
local p1 = area.curPos[1]
local p2 = area.curPos[2]
area.flats = {}
table.insert(area.flats, { POSITION(p1.x, p2.y, p2.z), POSITION(p1.x, p1.y, p1.z) })
table.insert(area.flats, { POSITION(p2.x, p2.y, p2.z), POSITION(p2.x, p1.y, p1.z) })
table.insert(area.flats, { POSITION(p2.x, p2.y, p1.z), POSITION(p1.x, p1.y, p1.z) })
table.insert(area.flats, { POSITION(p2.x, p2.y, p2.z), POSITION(p1.x, p1.y, p2.z) })
table.insert(area.flats, { POSITION(p1.x, p1.y, p1.z), POSITION(p2.x, p1.y, p2.z) })
table.insert(area.flats, { POSITION(p2.x, p2.y, p2.z), POSITION(p1.x, p2.y, p1.z) })
end
function updateAreaSize(area)
local p1 = area.curPos[1]
local p2 = area.curPos[2]
area.size = {}
area.size.length = math.abs(p1.x - p2.x) + 1
area.size.width = math.abs(p1.z - p2.z) + 1
area.size.height = math.abs(p1.y - p2.y) + 1
end
function getAnswerID(area)
local answer = area.answer
area.ids = {}
for y = answer.curPos[1].y, answer.curPos[1].y+answer.size.height-1 do
for x = answer.curPos[1].x, answer.curPos[1].x+answer.size.length-1 do
for z = answer.curPos[1].z, answer.curPos[1].z+answer.size.width-1 do
local ret, id = Block:getBlockID(x, y, z)
if id ~= 0 and not isInclude(id, area.ids) then
table.insert(area.ids, id);
end
end
end
end
end
function cheat(area)
local puzzle = area.puzzle
for y = puzzle.curPos[1].y, puzzle.curPos[1].y+puzzle.size.height-1 do
for x = puzzle.curPos[1].x, puzzle.curPos[1].x+puzzle.size.length-1 do
for z = puzzle.curPos[1].z, puzzle.curPos[1].z+puzzle.size.width-1 do
local ret, blockID = Block:getBlockID(x, y, z)
local targetPoint = mapping(area, POSITION(x, y, z))
local ret, targetID = Block:getBlockID(targetPoint.x, targetPoint.y, targetPoint.z)
if blockID ~= targetID then
Block:setBlockAll(x, y, z, targetID)
end
end
end
end
checkCompletion(area)
end
function checkCompletion(area)
if area.state == 1 then
return
end
local num = 0
local puzzle = area.puzzle
for y = puzzle.curPos[1].y, puzzle.curPos[1].y+puzzle.size.height-1 do
for x = puzzle.curPos[1].x, puzzle.curPos[1].x+puzzle.size.length-1 do
for z = puzzle.curPos[1].z, puzzle.curPos[1].z+puzzle.size.width-1 do
local ret, id = Block:getBlockID(x, y, z)
if id == BLOCK_NORMAL or id == BLOCK_ERROR then
return
end
end
end
end
local axes = { 'x', 'x', 'z', 'z', 'y', 'y'}
if hasSeparatePart(area.separation, axes[1]) then
local history = area.separation.history[axes[1]]
if history then
local p1 = puzzle.flats[history.flatIndex][1]
local p2 = puzzle.flats[history.flatIndex][2]
local values = { 1, -1, 1, -1, 1, -1}
if history.num > 1 then
for i = 1, history.num-2 do
p1[axes[history.dir]] = p1[axes[history.dir]] - values[history.dir]
p2[axes[history.dir]] = p2[axes[history.dir]] - values[history.dir]
end
end
local size = POSITION(puzzle.size.length+2, puzzle.size.height+2, puzzle.size.width+2)
move({p1, p2}, history.dir, history.num, size, history.vertex)
area.separation.times[axes[1]] = 0
area.separation.history[axes[1]] = nil
end
end
if hasSeparatePart(area.separation, axes[3]) then
local history = area.separation.history[axes[3]]
if history then
local p1 = puzzle.flats[history.flatIndex][1]
local p2 = puzzle.flats[history.flatIndex][2]
local values = { 1, -1, 1, -1, 1, -1}
if history.num > 1 then
for i = 1, history.num-2 do
p1[axes[history.dir]] = p1[axes[history.dir]] - values[history.dir]
p2[axes[history.dir]] = p2[axes[history.dir]] - values[history.dir]
end
end
local size = POSITION(puzzle.size.length+2, puzzle.size.height+2, puzzle.size.width+2)
move({p1, p2}, history.dir, history.num, size, history.vertex)
area.separation.times[axes[3]] = 0
area.separation.history[axes[3]] = nil
end
end
updateAreaFlats(puzzle)
updateAreaSize(puzzle)
for y = puzzle.curPos[1].y, puzzle.curPos[1].y+puzzle.size.height-1 do
for x = puzzle.curPos[1].x, puzzle.curPos[1].x+puzzle.size.length-1 do
for z = puzzle.curPos[1].z, puzzle.curPos[1].z+puzzle.size.width-1 do
if not verifySingleBlockInAnswerArea(area, POSITION(x, y, z)) then
return
end
end
end
end
for j = 1, #area.answer.flats do
cleanDigitalPrompt(area, area.puzzle.flats[j], j)
end
for y = puzzle.curPos[1].y, puzzle.curPos[1].y+puzzle.size.height-1 do
for x = puzzle.curPos[1].x, puzzle.curPos[1].x+puzzle.size.length-1 do
for z = puzzle.curPos[1].z, puzzle.curPos[1].z+puzzle.size.width-1 do
local ret, id = Block:getBlockID(x, y, z)
if isPrompt(id) ~= 0 then
Block:setBlockAll(x, y, z, BLOCK_AIR)
end
end
end
end
sendSystemMsg("Congratulations! You have completed the mission and a new gift show up in the Storage Box. ")
area.state = 1
if LEVEL_CONFIG[area.index].MONSTER then
generateMonster(area)
end
if LEVEL_CONFIG[area.index].TRANSFER then
local tr = LEVEL_CONFIG[area.index].TRANSFER.POS
Block:setBlockAll(tr.x, tr.y, tr.z, BLOCK_ACTIVE)
sendSystemMsg("A new portal is activated. ")
end
if LEVEL_CONFIG[area.index].REWARD then
generateReward(area)
end
end
function generateMonster(area)
local monster = LEVEL_CONFIG[area.index].MONSTER
local pos = monster.POS
area.monsterids = {}
for i = 1, #pos do
local p = pos[i]
local ret, objids = World:spawnCreature(p.x, p.y, p.z, monster.ID, monster.NUM)
if objids then
table.insert(area.monsterids, objids[1])
end
end
sendSystemMsg("Crazy animals appear! Beat them! ")
end
function generateReward(area)
local reward = LEVEL_CONFIG[area.index].REWARD
local pos = reward.POS
local ret, id = Block:getBlockID(pos.x, pos.y, pos.z)
if id ~= BLOCK_BOX then
Block:setBlockAll(pos.x, pos.y, pos.z, BLOCK_BOX)
end
local ret, itemCnt = WorldContainer:addItemToContainer(pos.x, pos.y, pos.z, reward.ID, 1)
if ret == ErrorCode.OK then
end
end
function isInArea(point, area)
local curP1 = area.curPos[1]
local curP2 = area.curPos[2]
if point.x >= math.min(curP1.x, curP2.x) and point.x <= math.max(curP1.x, curP2.x) then
if point.y >= math.min(curP1.y, curP2.y) and point.y <= math.max(curP1.y, curP2.y) then
if point.z >= math.min(curP1.z, curP2.z) and point.z <= math.max(curP1.z, curP2.z) then
return true
end
end
end
return false
end
function isInFlat(point, flat)
local curP1 = flat[1]
local curP2 = flat[2]
if point.x >= math.min(curP1.x, curP2.x) and point.x <= math.max(curP1.x, curP2.x) then
if point.y >= math.min(curP1.y, curP2.y) and point.y <= math.max(curP1.y, curP2.y) then
if point.z >= math.min(curP1.z, curP2.z) and point.z <= math.max(curP1.z, curP2.z) then
return true
end
end
end
return false
end
function updateSidesDigitalPrompt(pos)
local axes = { 'x', 'x', 'z', 'z', 'y', 'y' }
local values = { -1, 1, -1, 1, 1, -1 }
local dirs = { 4, 1, 2, 3, 1, 1 }
for i = 1, 6 do
local p = POSITION(pos.x, pos.y, pos.z)
p[axes[i]] = p[axes[i]] + values[i]
local ret, id = Block:getBlockID(p.x, p.y, p.z)
local side = isPrompt(id)
if side ~= 0 then
local ret, data = Block:getBlockData(p.x, p.y, p.z)
if data == dirs[i] then
if side == 1 and i <= 4 or side == 3 and i == 5 or side == 2 and i == 6 then
Block:setBlockAll(pos.x, pos.y, pos.z, id, data)
Block:destroyBlock(p.x, p.y, p.z)
local value = -values[i]
local tp = POSITION(pos.x, pos.y, pos.z)
while(true) do
tp[axes[i]] = tp[axes[i]] + value
local ret, nid = Block:getBlockID(tp.x, tp.y, tp.z)
local ret, ndata = Block:getBlockData(tp.x, tp.y, tp.z)
local side = isPrompt(nid)
if side ~= 0 then
if data + ndata == 5 then
Block:setBlockAll(pos.x, pos.y, pos.z, BLOCK_AIR)
Block:setBlockAll(tp.x, tp.y, tp.z, BLOCK_AIR)
break
else
end
else
break
end
end
break
end
end
else
end
end
end
function updateDigitalPrompt(area, flat, dir)
local size
local isInit = false
local prompt = {}
local count = 0
if area.prompts and area.prompts[dir] then
size = area.puzzle.size
prompt = area.prompts[dir]
count = 1
else
if not area.prompts then
area.prompts = {}
end
size = area.answer.size
area.prompts[dir] = prompt
isInit = true
end
local p1, p2
local function CONFIG(axes, signs)
local c = { axes = axes, signs = signs }
return c
end
local c = {
CONFIG({'y', 'z', 'x'}, {-1, -1, 1}),
CONFIG({'y', 'z', 'x'}, {-1, -1, -1}),
CONFIG({'y', 'x', 'z'}, {-1, -1, 1}),
CONFIG({'y', 'x', 'z'}, {-1, -1, -1}),
CONFIG({'x', 'z', 'y'}, {-1, -1, 1}),
CONFIG({'x', 'z', 'y'}, {-1, -1, -1}),
}
local limits = POSITION(size.length, size.height, size.width)
c = c[dir]
p1 = flat[1]
p2 = flat[2]
for i = 1, #c.axes-1 do
local axis = c.axes[i]
if p1[axis] > p2[axis] then
c.signs[i] = -1
elseif p1[axis] < p2[axis] then
c.signs[i] = 1
else
c.signs[i] = 0
end
end
local blockid = BLOCK_ANSWER[dir % 2 + 1]
local t1 = POSITION(p1.x, p1.y, p1.z)
for i = 1, limits[c.axes[1]] do
local t2 = POSITION(t1.x, t1.y, t1.z)
for j = 1, limits[c.axes[2]] do
local t3 = POSITION(t2.x, t2.y, t2.z)
if isInit then
local part = 0
local isContinuous = false
local num = 0
local promptID
for k = 1, limits[c.axes[3]] do
local ret, id = Block:getBlockID(t3.x, t3.y, t3.z)
if id == blockid then
num = num + 1
if not isContinuous then
part = part + 1
isContinuous = true
end
elseif isContinuous then
isContinuous = false
end
t3[c.axes[3]] = t3[c.axes[3]] + c.signs[3]
end
local index1 = 1
if dir <= 4 then
else
index1 = dir - 3
end
if num == 0 then
promptID = ID_prompts[index1][dir%2+1].zero
else
local partToNum = { 0, 8, 15, 24}
promptID = ID_prompts[index1][dir%2+1][num+partToNum[part]]
end
table.insert(prompt, promptID)
else
local setP = POSITION(t2.x, t2.y, t2.z)
local toFace = { 4, 1, 2, 3, 1, 1 }
local toCoord = { 'x', 'x', 'z', 'z', 'y', 'y' }
local toSign = { -1, 1, -1, 1, -1, 1 }
setP[toCoord[dir]] = setP[toCoord[dir]] + toSign[dir]
Block:setBlockAll(setP.x, setP.y, setP.z, prompt[count], toFace[dir])
count = count + 1
end
t2[c.axes[2]] = t2[c.axes[2]] + c.signs[2]
end
t1[c.axes[1]] = t1[c.axes[1]] + c.signs[1]
end
end
function cleanDigitalPrompt(area, flat, dir)
local size
local isInit = false
local prompt = {}
local count = 0
if area.prompts and area.prompts[dir] then
size = area.puzzle.size
prompt = area.prompts[dir]
count = 1
else
if not area.prompts then
area.prompts = {}
end
size = area.answer.size
area.prompts[dir] = prompt
isInit = true
end
local p1, p2
local function CONFIG(axes, signs)
local c = { axes = axes, signs = signs }
return c
end
local c = {
CONFIG({'y', 'z', 'x'}, {-1, -1, 1}),
CONFIG({'y', 'z', 'x'}, {-1, -1, -1}),
CONFIG({'y', 'x', 'z'}, {-1, -1, 1}),
CONFIG({'y', 'x', 'z'}, {-1, -1, -1}),
CONFIG({'x', 'z', 'y'}, {-1, -1, 1}),
CONFIG({'x', 'z', 'y'}, {-1, -1, -1}),
}
local limits = POSITION(size.length, size.height, size.width)
c = c[dir]
p1 = flat[1]
p2 = flat[2]
for i = 1, #c.axes-1 do
local axis = c.axes[i]
if p1[axis] > p2[axis] then
c.signs[i] = -1
elseif p1[axis] < p2[axis] then
c.signs[i] = 1
else
c.signs[i] = 0
end
end
local blockid = BLOCK_ANSWER[dir % 2 + 1]
local t1 = POSITION(p1.x, p1.y, p1.z)
for i = 1, limits[c.axes[1]] do
local t2 = POSITION(t1.x, t1.y, t1.z)
for j = 1, limits[c.axes[2]] do
local t3 = POSITION(t2.x, t2.y, t2.z)
if isInit then
local part = 0
local isContinuous = false
local num = 0
local promptID
for k = 1, limits[c.axes[3]] do
local ret, id = Block:getBlockID(t3.x, t3.y, t3.z)
if id == blockid then
num = num + 1
if not isContinuous then
part = part + 1
isContinuous = true
end
elseif isContinuous then
isContinuous = false
end
t3[c.axes[3]] = t3[c.axes[3]] + c.signs[3]
end
local index1 = 1
if dir <= 4 then
else
index1 = dir - 3
end
if num == 0 then
promptID = ID_prompts[index1][dir%2+1].zero
else
local partToNum = { 0, 8, 15, 24}
promptID = ID_prompts[index1][dir%2+1][num+partToNum[part]]
end
table.insert(prompt, promptID)
else
local setP = POSITION(t2.x, t2.y, t2.z)
local toFace = { 4, 1, 2, 3, 1, 1 }
local toCoord = { 'x', 'x', 'z', 'z', 'y', 'y' }
local toSign = { -1, 1, -1, 1, -1, 1 }
setP[toCoord[dir]] = setP[toCoord[dir]] + toSign[dir]
Block:setBlockAll(setP.x, setP.y, setP.z, BLOCK_AIR, toFace[dir])
count = count + 1
end
t2[c.axes[2]] = t2[c.axes[2]] + c.signs[2]
end
t1[c.axes[1]] = t1[c.axes[1]] + c.signs[1]
end
end
function mapping(area, pos)
local puzzle = area.puzzle
local offsetP = POSITION(pos.x, pos.y, pos.z)
local values = { 1, -1, 1, -1, 1, -1}
local separation = area.separation
if hasSeparatePart(separation, 'x') then
local history = separation.history.x
local coord = 'z'
if offsetP[coord] >= math.min(history.vertex[coord], history.vertex[coord]-values[history.dir]*history.num)
and offsetP[coord] <= math.max(history.vertex[coord], history.vertex[coord]-values[history.dir]*history.num) then
offsetP[coord] = offsetP[coord] - values[history.dir] * SEPARATE_DISTANCE
end
end
if hasSeparatePart(separation, 'z') then
local history = separation.history.z
local coord = 'x'
if offsetP[coord] >= math.min(history.vertex[coord], history.vertex[coord]-values[history.dir]*history.num)
and offsetP[coord] <= math.max(history.vertex[coord], history.vertex[coord]-values[history.dir]*history.num) then
offsetP[coord] = offsetP[coord] - values[history.dir] * SEPARATE_DISTANCE
end
end
local puzzlePoint = puzzle.srcPos[1]
local answerPoint = area.answer.srcPos[1]
local vector = {}
vector.x = offsetP.x - puzzlePoint.x
vector.y = offsetP.y - puzzlePoint.y
vector.z = offsetP.z - puzzlePoint.z
return POSITION(vector.x + answerPoint.x, vector.y + answerPoint.y, vector.z + answerPoint.z)
end
function hasSeparatePart(separation, axis)
if separation.times[axis] and separation.times[axis] ~= 0 then
return true
else
return false
end
end
function separate(area, hitP, hitDir)
--hitDir = hitDir + 1
local text = ""
local puzzle = area.puzzle
local flats = puzzle.flats
local axes = { 'x', 'x', 'z', 'z', 'y', 'y'}
local verticalAxes = { 'z', 'z', 'x', 'x', 'y', 'y'}
local isCenter = false
if hitP[verticalAxes[hitDir]] == puzzle.midValues[hitDir] then
isCenter = true
text = text .. " isCenter"
end
if puzzle.size.width == 1 and puzzle.size.height == 1 and puzzle.size.length == 1 then
return
elseif puzzle.size.length == 1 and hitDir >= 3 and hitDir <= 4 then
return
elseif puzzle.size.width == 1 and hitDir >= 1 and hitDir <= 2 then
return
end
if hasSeparatePart(area.separation, axes[hitDir]) then
local history = area.separation.history[axes[hitDir]]
if history then
local p1 = flats[history.flatIndex][1]
local p2 = flats[history.flatIndex][2]
local flat = { POSITION(p1.x, p1.y, p1.z), POSITION(p2.x, p2.y, p2.z) }
local values = { 1, -1, 1, -1, 1, -1}
if history.num > 1 then
for i = 1, history.num-2 do
p1[axes[history.dir]] = p1[axes[history.dir]] - values[history.dir]
p2[axes[history.dir]] = p2[axes[history.dir]] - values[history.dir]
end
end
local size = POSITION(puzzle.size.length+2, puzzle.size.height+2, puzzle.size.width+2)
move({p1, p2}, history.dir, history.num, size, history.vertex)
area.separation.times[axes[hitDir]] = 0
area.separation.history[axes[hitDir]] = nil
end
else
local verticalAxis = verticalAxes[hitDir]
local value
local num
if isCenter then
local randomNum = { 2, 1 }
math.randomseed(tostring(os.time()):reverse():sub(1, 7))
value = randomNum[math.random(2)]
num = 0
else
if hitP[verticalAxis] > puzzle.midValues[hitDir] then
value = 2
else
value = 1
end
num = 1
end
if value == 2 then
num = num + math.max(puzzle.curPos[1][verticalAxis], puzzle.curPos[2][verticalAxis]) - hitP[verticalAxis]
else
num = num + hitP[verticalAxis] - math.min(puzzle.curPos[1][verticalAxis], puzzle.curPos[2][verticalAxis])
end
num = num + 1
local moveDir = { { 3, 4 }, { 3, 4 }, { 1, 2 }, { 1, 2 } }
local flat = flats[moveDir[hitDir][value]]
local p1 = POSITION(flat[1].x, flat[1].y, flat[1].z)
local p2 = POSITION(flat[2].x, flat[2].y, flat[2].z)
local vertex
for i = 1, #puzzle.curPos do
if p1.x == puzzle.curPos[i].x and p1.y == puzzle.curPos[i].y and p1.z == puzzle.curPos[i].z
or p2.x == puzzle.curPos[i].x and p2.y == puzzle.curPos[i].y and p2.z == puzzle.curPos[i].z then
vertex = puzzle.curPos[i]
end
end
local toAxes = { 'x', 'x', 'z', 'z', 'y', 'y'}
local values = { 1, -1, 1, -1, 1, -1}
local axis = toAxes[moveDir[hitDir][value]]
local moveValue = values[moveDir[hitDir][value]]
local size = POSITION(puzzle.size.length+2, puzzle.size.height+2, puzzle.size.width+2)
p1[axis] = p1[axis] - moveValue
p2[axis] = p2[axis] - moveValue
move({p1, p2}, moveDir[hitDir][value], num, size, vertex)
area.separation.times[axes[hitDir]] = 1
local history = { num = num, flatIndex = moveDir[hitDir][value], vertex = vertex }
if value == 1 then
value = 2
else
value = 1
end
history.dir = moveDir[hitDir][value]
area.separation.history[axes[hitDir]] = history
end
updateAreaFlats(puzzle)
updateAreaSize(puzzle)
end
function move(flat, dir, num, size, vertex)
text = string.format("Move dir=%d, num=%d", dir, num)
local p1 = POSITION(flat[1].x, flat[1].y, flat[1].z)
local p2 = POSITION(flat[2].x, flat[2].y, flat[2].z)
local toAxes = { 'x', 'x', 'z', 'z', 'y', 'y'}
local values = { 1, -1, 1, -1, 1, -1}
local axis = toAxes[dir]
local axes = { 'x', 'y', 'z'}
local axisIndex
for i = 1, #axes do
if axis == axes[i] then
axisIndex = i
else
if p1[axes[i]] > p2[axes[i]] then
p1[axes[i]] = p1[axes[i]] + 1
p2[axes[i]] = p2[axes[i]] + -1
elseif p1[axes[i]] < p2[axes[i]] then
p1[axes[i]] = p1[axes[i]] + -1
p2[axes[i]] = p2[axes[i]] + 1
else
p1[axes[i]] = p1[axes[i]] + 1
p2[axes[i]] = p2[axes[i]] + -1
end
end
end
table.remove(axes, axisIndex)
values = { values[dir] }
for i = 1, #axes do
if p1[axes[i]] > p2[axes[i]] then
values[i+1] = -1
else
values[i+1] = 1
end
end
local limits = size
local t1 = POSITION(p1.x, p1.y, p1.z)
for i = 1, num do
local t2 = POSITION(t1.x, t1.y, t1.z)
for j = 1, limits[axes[1]] do
local t3 = POSITION(t2.x, t2.y, t2.z)
for k = 1, limits[axes[2]] do
local moveP = POSITION(t3.x, t3.y, t3.z)
local ret, id = Block:getBlockID(t3.x, t3.y, t3.z)
local ret, data = Block:getBlockData(t3.x, t3.y, t3.z)
moveP[axis] = moveP[axis] - values[1] * SEPARATE_DISTANCE
if id == BLOCK_ERROR then
id = BLOCK_NORMAL
end
Block:setBlockAll(moveP.x, moveP.y, moveP.z, id, data)
Block:destroyBlock(t3.x, t3.y, t3.z, false)
t3[axes[2]] = t3[axes[2]] + values[3]
end
t2[axes[1]] = t2[axes[1]] + values[2]
end
t1[axis] = t1[axis] + values[1]
end
vertex[axis] = vertex[axis] - values[1] * SEPARATE_DISTANCE
end
function dye(area, pos, tool)
local index = 0
if tool == TOOLS_ID_DYE[1] then
index = 1
else
index = 2
end
Block:setBlockAll(pos.x, pos.y, pos.z, BLOCK_ANSWER[index], DATA_ANSWER[index])
if verifySingleBlockInAnswerArea(area, pos) then
checkCompletion(area)
else
setErrorBlock(pos)
end
end
function setErrorBlock(pos)
Block:setBlockAll(pos.x, pos.y, pos.z, BLOCK_ERROR)
table.insert(errorPoints, {pos = pos, time = os.time()})
end
function verifySingleBlockInAnswerArea(area, pos)
local targetPoint = mapping(area, pos)
local ret, hitID = Block:getBlockID(pos.x, pos.y, pos.z)
local ret, targetID = Block:getBlockID(targetPoint.x, targetPoint.y, targetPoint.z)
local text = tostring(hitID) .. " : " .. tostring(targetID)
if isPrompt(hitID) ~= 0 then
hitID = BLOCK_AIR
end
if hitID == targetID then
return true
else
--Block:setBlockAll(pos.x, pos.y, pos.z, BLOCK_ERROR)
--table.insert(errorPoints, {pos = pos, time = os.time()})
return false
end
end
function startReset(area)
local puzzle = area.puzzle
area.reset.num = 1
area.reset.pos = POSITION(puzzle.srcPos[1].x, puzzle.srcPos[1].y, puzzle.srcPos[1].z)
local axes = { 'x', 'x', 'z', 'z', 'y', 'y'}
if hasSeparatePart(area.separation, axes[1]) then
local history = area.separation.history[axes[1]]
if history then
local p1 = puzzle.flats[history.flatIndex][1]
local p2 = puzzle.flats[history.flatIndex][2]
local values = { 1, -1, 1, -1, 1, -1}
if history.num > 1 then
for i = 1, history.num-2 do
p1[axes[history.dir]] = p1[axes[history.dir]] - values[history.dir]
p2[axes[history.dir]] = p2[axes[history.dir]] - values[history.dir]
end
end
local size = POSITION(puzzle.size.length+2, puzzle.size.height+2, puzzle.size.width+2)
move({p1, p2}, history.dir, history.num, size, history.vertex)
area.separation.times[axes[1]] = 0
area.separation.history[axes[1]] = nil
end
end
if hasSeparatePart(area.separation, axes[3]) then
local history = area.separation.history[axes[3]]
if history then
local p1 = puzzle.flats[history.flatIndex][1]
local p2 = puzzle.flats[history.flatIndex][2]
local values = { 1, -1, 1, -1, 1, -1}
if history.num > 1 then
for i = 1, history.num-2 do
p1[axes[history.dir]] = p1[axes[history.dir]] - values[history.dir]
p2[axes[history.dir]] = p2[axes[history.dir]] - values[history.dir]
end
end
local size = POSITION(puzzle.size.length+2, puzzle.size.height+2, puzzle.size.width+2)
move({p1, p2}, history.dir, history.num, size, history.vertex)
area.separation.times[axes[3]] = 0
area.separation.history[axes[3]] = nil
end
end
updateAreaFlats(puzzle)
updateAreaSize(puzzle)
area.isResetting = true
area.state = 0
end
function resetting(area)
if area.reset.num <= #area.reset.data then
local data = area.reset.data[area.reset.num]
local pos = data.pos
local id = data.id
Block:setBlockAll(pos.x, pos.y, pos.z, BLOCK_NORMAL)
area.reset.num = area.reset.num + 1
else
for i = 1, #area.puzzle.flats do
updateDigitalPrompt(area, area.puzzle.flats[i], i)
end
local resetP = LEVEL_CONFIG[area.index].RESET.POS
local ret, data = Block:getBlockData(resetP.x, resetP.y, resetP.z)
if data >= 8 then
data = data - 8
end
Block:setBlockAll(resetP.x, resetP.y, resetP.z, BLOCK_SWITCH, data)
area.isResetting = false
end
end
function initAreaAttributes(areaPos)
local attributes = {}
local p1 = areaPos[1]
local p2 = areaPos[2]
attributes.srcPos = areaPos
attributes.curPos = { POSITION(p1.x, p1.y, p1.z), POSITION(p2.x, p2.y, p2.z) }
updateAreaSize(attributes)
updateAreaFlats(attributes)
attributes.midValues = {}
local int, _ = math.modf((attributes.flats[1][1].z+ attributes.flats[1][2].z)/2)
table.insert(attributes.midValues, int)
int, _ = math.modf((attributes.flats[2][1].z+ attributes.flats[2][2].z)/2)
table.insert(attributes.midValues, int)
int, _ = math.modf((attributes.flats[3][1].x+ attributes.flats[3][2].x)/2)
table.insert(attributes.midValues, int)
int, _ = math.modf((attributes.flats[4][1].x+ attributes.flats[4][2].x)/2)
table.insert(attributes.midValues, int)
return attributes;
end
function initializeLevel()
for i = 1, #LEVEL_CONFIG do
local config = LEVEL_CONFIG[i]
local area = {}
area.index = i
area.puzzle = initAreaAttributes(config.PUZZLE)
area.answer = initAreaAttributes(config.ANSWER)
area.state = 0
area.separation = {}
area.separation.times = {}
area.separation.history = {}
area.num = 0
area.reset = {}
area.reset.data = {}
for x = area.puzzle.curPos[1].x, area.puzzle.curPos[1].x+area.puzzle.size.length-1 do
for y = area.puzzle.curPos[1].y, area.puzzle.curPos[1].y+area.puzzle.size.height-1 do
for z = area.puzzle.curPos[1].z, area.puzzle.curPos[1].z+area.puzzle.size.width-1 do
local ret, id = Block:getBlockID(x, y, z)
table.insert(area.reset.data, {id = id, pos = POSITION(x, y, z)})
if id ~= BLOCK_AIR then
area.num = area.num + 1
end
end
end
end
for j = 1, #area.answer.flats do
updateDigitalPrompt(area, area.answer.flats[j], j)
updateDigitalPrompt(area, area.puzzle.flats[j], j)
end
if config.TRANSFER then
local tr = config.TRANSFER.POS
local _, id = Block:getBlockID(tr.x, tr.y, tr.z)
if id == BLOCK_ACTIVE then
Block:setBlockAll(tr.x, tr.y, tr.z, BLOCK_AIR)
end
end
table.insert(areas, area)
end
end
function listenevents_minesweeper()
--local ScriptSupportEvent = class.ScriptSupportEvent.new()
local listenBlocks = { { BLOCK_NORMAL }, { BLOCK_FLOOR }
, {668}, {669}, {670}
, {671}, {672}, {673}
, {674}, {675}, {676}
, {677}, {678}, {679}
, {680}, {681}, {682}
, {1080}, { BLOCK_ACTIVE }, {724} }
for i = ID_prompts.min, ID_prompts.max do
table.insert(listenBlocks, {i})
end
ScriptSupportEvent:registerEvent("Game.Start", minesweeper_game_started)
ScriptSupportEvent:registerEvent("Game.Run", minesweeper_game_run)
ScriptSupportEvent:registerEvent('Actor.Projectile.Hit', minesweeper_actor_projectilehit)
ScriptSupportEvent:registerEvent_Block('Block.DestroyBy', minesweeper_block_destroyedby, listenBlocks)
ScriptSupportEvent:registerEvent_Block('Block.Trigger', minesweeper_block_trigger, listenBlocks)
ScriptSupportEvent:registerEvent("Player.PickUpItem", minesweeper_player_pickupitem)
ScriptSupportEvent:registerEvent("Actor.Die", minesweeper_monster_dead)
ScriptSupportEvent:registerEvent("Player.SelectShortcut", minesweeper_player_selectshortcut)
--ScriptSupportEvent:listenEventList()
end
minesweeper_game_started = function()
sendSystemMsg("Stone Code - You wake up in a mysterious maze and a boy stands in front of you. ")
local ret, num, array = World:getAllPlayers()
initializeLevel()
end
minesweeper_game_run = function()
if #errorPoints > 0 then
local curTime = os.time()
local i = 1
while i <= #errorPoints do
local ep = errorPoints[i]
if curTime - ep.time > ERROR_TIME then
local ret, id = Block:getBlockID(ep.pos.x, ep.pos.y, ep.pos.z)
if id == BLOCK_ERROR then
Block:setBlockAll(ep.pos.x, ep.pos.y, ep.pos.z, BLOCK_NORMAL)
end
table.remove(errorPoints, i)
else
break
end
end
end
for i = 1, #areas do
if areas[i].isResetting then
resetting(areas[i])
end
end
end
minesweeper_actor_projectilehit = function(params)
local blockid, x, y, z, eventobjid = params.blockid, params.x, params.y, params.z, params.eventobjid;
local text = string.format("Hit %d,%d,%d,%d",blockid, x, y, z)
local tool = TItem:getItemID(eventobjid)
local dir
TItem:destroyProjectile(eventobjid)
local ret, data = Block:getBlockData(x, y, z)
local bIsPrompt = false
local pos = POSITION(x, y, z)
local side
if blockid ~= BLOCK_NORMAL then
side = isPrompt(blockid)
if side ~= 0 then
local config = { {'x', 1}, {'x', -1}, {'z', 1}, {'z', -1}, {'y', 1}, {'y', -1} }
if side == 1 then
local ret, data = Block:getBlockData(x, y, z)
local face = { 4, 1, 2, 3 }
for i = 1, #face do
if data == face[i] then
pos[config[i][1]] = pos[config[i][1]] + config[i][2]
dir = i
break
end
end
elseif side == 2 then
pos[config[5][1]] = pos[config[5][1]] + config[5][2]
elseif side == 3 then
pos[config[6][1]] = pos[config[6][1]] + config[6][2]
end
local ret, id = Block:getBlockID(pos.x, pos.y, pos.z)
bIsPrompt = true
blockid = id
end
end
local area
for i = 1, #areas do
if isInArea(pos, areas[i].puzzle) then
area = areas[i]
if bIsPrompt and dir then
else
for j = 1, #area.puzzle.flats do
if isInFlat(pos, area.puzzle.flats[j]) then
local flat = area.puzzle.flats[j]
dir = j
if dir >= 5 then
dir = dir - 3
end
break
end
end
end
break
end
end
if not area then
return
elseif area.isResetting or area.state ~= 0 then
return
end
if tool == TOOLS_ID_SEPARATE and dir then
separate(area, pos, dir)
elseif isInclude(tool, TOOLS_ID_DYE) then
if blockid == BLOCK_NORMAL then
dye(area, pos, tool)
end
elseif tool == TOOLS_ID_DIG then
if blockid == BLOCK_NORMAL then
Block:destroyBlock(pos.x, pos.y, pos.z, false)
if verifySingleBlockInAnswerArea(area, pos) then
updateSidesDigitalPrompt(pos)
checkCompletion(area)
else
setErrorBlock(pos)
end
end
end
end
minesweeper_block_destroyedby = function(param)
local blockid, x, y, z = param.blockid, param.x, param.y, param.z;
local area
for i = 1, #areas do
if isInArea(POSITION(x, y, z), areas[i].puzzle) then
area = areas[i]
break
end
end
if not area then
local data = 0
if blockid == BLOCK_SWITCH then
local axes = { 'x', 'x', 'z', 'z' }
local values = { 1, -1, 1, -1 }
local toFace = { 1, 0, 3, 2 }
for i = 1, #axes do
local pos = POSITION(x, y, z)
pos[axes[i]] = pos[axes[i]] + values[i]
local ret, id = Block:getBlockID(pos.x, pos.y, pos.z)
if id ~= BLOCK_AIR then
data = toFace[i]
break
end
end
end
Block:setBlockAll(x, y, z, blockid, data)
return
end
if blockid ~= BLOCK_NORMAL or area.isResetting then
if area.isResetting then
blockid = BLOCK_NORMAL
end
Block:setBlockAll(x, y, z, blockid)
else
if verifySingleBlockInAnswerArea(area, POSITION(x, y, z)) then
updateSidesDigitalPrompt(POSITION(x, y, z))
end
end
end
minesweeper_monster_dead = function(params)
local objid = params.eventobjid
local toobjid = params.toobjid
for i = 1, #areas do
local area = areas[i]
if area.monsterids then
local index
for j = 1, #area.monsterids do
if objid == area.monsterids[j] then
index = j
break
end
end
if index then
table.remove(area.monsterids, index)
if #area.monsterids == 0 then
if LEVEL_CONFIG[area.index].TRANSFER then
local tr = LEVEL_CONFIG[area.index].TRANSFER.POS
--Block:setBlockAll(tr.x, tr.y, tr.z, BLOCK_ACTIVE)
--sendSystemMsg("A new portal is activated. ")
end
end
end
end
end
end
minesweeper_block_trigger = function(params)
local blockid, x, y, z, eventobjid = params.blockid, params.x, params.y, params.z, params.eventobjid;
local ret, data = Block:getBlockData(x, y, z)
for i = 1, #areas do
local area = areas[i]
local resetP = LEVEL_CONFIG[i].RESET.POS
if data >= 8 and x == resetP.x and y == resetP.y and z == resetP.z then
if not area.isResetting then
startReset(area)
end
break
end
end
Player:notifyGameInfo2Self(eventobjid, "Successfully restored ")
end
minesweeper_player_pickupitem = function(params)
local itemid, itemnum, toobjid, eventobjid = params.itemid, params.itemnum, params.toobjid, params.eventobjid
if not isInclude(itemid, ALLOWED_ITEMS) then
Backpack:removeGridItemByItemID(eventobjid, itemid)
else
end
end
minesweeper_player_selectshortcut = function(params)
local itemid, itemnum, eventobjid = params.itemid, params.itemnum, params.eventobjid
for i = 1, #TOOLS_STRING do
if TOOLS_STRING[i].id == itemid then
Player:notifyGameInfo2Self(eventobjid, TOOLS_STRING[i].string)
break
end
end
end
listenevents_minesweeper()
← 坦克对战 五子棋(Gomoku) →