Rune Match!
by cbrm
written with Sublime Text 3 on TFS 1.2, 10.95
powered by 1337 technology
2~4 multiplayer event
not recommended for players with poor memory
based on the traditional Concentration game
by cbrm
written with Sublime Text 3 on TFS 1.2, 10.95
powered by 1337 technology
2~4 multiplayer event
not recommended for players with poor memory
based on the traditional Concentration game
Goal: Player must compete with other ones by searching pairs of runes in the board. Using look (Shift+Left Click) on the blank runes, the player will reveal the rune there, and then must find its pair to make a match and score points.
Map Layout:
Setup:
add@data/lib/core/position.lua
Lua:
function Position:isInRange(fromPosition, toPosition)
return (self.x >= fromPosition.x and self.y >= fromPosition.y and self.z >= fromPosition.z and self.x <= toPosition.x and self.y <= toPosition.y and self.z <= toPosition.z)
end
add@data/movements/movements.xml
XML:
<!-- Rune Match event-->
<movevent event="StepOut" actionid="1337" script="match.lua"/>
create@data/movements/scripts/match.lua
Lua:
--[[
////////////////////////
Rune Match event
written by cbrm
for TFS 1.2
@otland.net
\\\\\\\\\\\\\\\\\\\\\\\\
]]--
MATCH = {
-- do not touch --
POS = {},
LAST = {},
COUNT = {},
SCORE = {},
SHOWN = {},
EXHAUST = {},
EVENT = {},
SIZE = {7, 8},
DELAY = 1,
HIDDEN = 2260,
TIMER = 0,
DURATION = 15,
------------------
BOARD = {
from = Position(32393, 32191, 7),
to = Position(32399, 32198, 7)
},
PLAYERS = {
from = {
[1] = Position(32395, 32189, 7),
[2] = Position(32396, 32189, 7),
[3] = Position(32397, 32189, 7),
[4] = Position(32398, 32189, 7)
},
to = {
[1] = Position(32392, 32193, 7),
[2] = Position(32400, 32193, 7),
[3] = Position(32392, 32196, 7),
[4] = Position(32400, 32196, 7)
}
},
EXIT = Position(32400, 32204, 7)
}
for rune = 2261, 2316 do
table.insert(MATCH.SHOWN, rune)
end
pause = coroutine.yield
function doSleep(co)
if coroutine.status(co) ~= 'dead' then
local _, delay = coroutine.resume(co)
addEvent(doSleep, delay, co)
end
end
function enableSleep(f)
if type(f) == 'function' then
local co = coroutine.create(f)
doSleep(co)
end
end
function getValue(v)
return v or 0
end
function table.getIndex(t, v)
for index, value in ipairs(t) do
if v == value then
return index
end
end
end
function table.shuffle(t)
local j
for i = #t, 2, -1 do
j = math.random(i)
t[i], t[j] = t[j], t[i]
end
end
function isMatchRune(id)
return isInArray(MATCH.SHOWN, id) or (id == MATCH.HIDDEN)
end
function hideRune(p)
local v = Tile(p):getTopVisibleThing(false)
return isMatchRune(v:getId()) and v:transform(MATCH.HIDDEN), p:sendMagicEffect(10)
end
function controlMatchRune(cid, id)
if getValue(MATCH.LAST[cid]) == id then
p = MATCH.POS[cid]
hideRune(p)
MATCH.POS[cid] = 0
MATCH.LAST[cid] = 0
MATCH.COUNT[cid] = 0
doCreatureSay(cid, "TIMEOUT", TALKTYPE_MONSTER_SAY, false, nil, p)
end
end
function showRune(cid, rune)
local p, u = rune:getPosition(), rune:getAttribute(ITEM_ATTRIBUTE_CHARGES)
rune:transform(u)
p:sendMagicEffect(10)
local v = getValue(MATCH.LAST[cid])
if v > 0 then
return v == u
end
MATCH.LAST[cid] = u
MATCH.POS[cid] = p
MATCH.EVENT[cid] = addEvent(controlMatchRune, 4200, cid, u)
return nil
end
function cleanBoard()
for x = MATCH.BOARD.from.x, MATCH.BOARD.to.x do
for y = MATCH.BOARD.from.y, MATCH.BOARD.to.y do
for _, item in ipairs(Tile(Position(x, y, MATCH.BOARD.from.z)):getItems()) do
item:remove()
end
end
end
end
function endMatch()
local h = {}
for _, pos in ipairs(MATCH.PLAYERS.to) do
local player = Tile(pos):getTopCreature()
if player ~= nil then
local cid = player:getId()
table.insert(h, {player:getName(), getValue(MATCH.SCORE[cid])})
MATCH.SCORE[cid] = nil
player:teleportTo(MATCH.EXIT)
end
end
table.sort(h, function(a, b) return a[2] > b[2] end)
local str = "Rune Match event finished!"
for _, highscore in ipairs(h) do
str = str .. "\n" .. highscore[1] .. " scored " .. math.floor((highscore[2]/(MATCH.SIZE[1]*MATCH.SIZE[2]))*100) .. "%"
end
Game.broadcastMessage(str, MESSAGE_EVENT_ADVANCE)
cleanBoard()
stopEvent(MATCH.TIMER)
end
function newBoard()
cleanBoard()
table.shuffle(MATCH.SHOWN)
MATCH.TIMER = addEvent(endMatch, MATCH.DURATION*60*1000)
local i, k, l, x, y = 0, {}, MATCH.SIZE[1]*MATCH.SIZE[2], MATCH.BOARD.from.x, MATCH.BOARD.from.y
for index = 1, (MATCH.SIZE[1]*MATCH.SIZE[2])/2 do
table.insert(k, MATCH.SHOWN[index])
table.insert(k, MATCH.SHOWN[index])
end
while i < l do
v, p = k[math.random(#k)], Position(x, y, MATCH.BOARD.from.z)
p:sendMagicEffect(10)
table.remove(k, table.getIndex(k, v))
r = Item(doCreateItemEx(MATCH.HIDDEN, 1))
r:setActionId(31337)
r:setAttribute(ITEM_ATTRIBUTE_CHARGES, v)
doTileAddItemEx(p, r:getUniqueId())
i = i + 1
if x == MATCH.BOARD.to.x then
y = y + 1
x = MATCH.BOARD.from.x
else
x = x + 1
end
end
end
function isBoardEmpty()
for x = MATCH.BOARD.from.x, MATCH.BOARD.to.x do
for y = MATCH.BOARD.from.y, MATCH.BOARD.to.y do
local v = Tile(Position(x, y, MATCH.BOARD.from.z)):getTopVisibleThing(false)
if (v ~= nil) and isMatchRune(v:getId()) then
return false
end
end
end
return true
end
function onStepOut(creature, item, position, fromPosition)
if not position:isInRange(MATCH.BOARD.from, MATCH.BOARD.to) and (MATCH.SCORE[creature:getId()] ~= nil) then
creature:teleportTo(fromPosition)
end
return true
end
add@data/actions/actions.xml
XML:
<!-- Rune Match event -->
<action uniqueid="1337" script="match.lua" />
create@data/actions/scripts/match.lua
Lua:
function onUse(player, item, fromPosition, target, toPosition, isHotkey)
for _, pos in ipairs(MATCH.PLAYERS.to) do
local v = Tile(pos):getTopCreature()
if v ~= nil then
return player:say("There are still players on the game!", TALKTYPE_MONSTER_SAY, false, nil, fromPosition)
end
end
local players = {}
for _, pos in ipairs(MATCH.PLAYERS.from) do
local v = Tile(pos):getTopCreature()
if (v ~= nil) and v:isPlayer() then
table.insert(players, v)
end
end
if #players > 4 then
return player:say("Limit is 4 players!", TALKTYPE_MONSTER_SAY, false, nil, fromPosition)
end
if #players < 2 then
return player:say("At least 2 players are needed!", TALKTYPE_MONSTER_SAY, false, nil, fromPosition)
end
newBoard()
for i, eventPlayer in ipairs(players) do
local pid = eventPlayer:getId()
eventPlayer:teleportTo(MATCH.PLAYERS.to[i])
MATCH.POS[pid] = 0
MATCH.LAST[pid] = 0
MATCH.COUNT[pid] = 0
MATCH.SCORE[pid] = 0
MATCH.EXHAUST[pid] = 0
end
return item:transform(item:getId() == 1945 and 1946 or 1945)
end
enable player method@data/events/events.xml
XML:
<event class="Player" method="onMoveItem" enabled="1" />
edit player methods@data/events/scripts/player.lua
After
Lua:
function Player:onMoveItem(item, count, fromPosition, toPosition)
Lua:
if fromPosition:isInRange(MATCH.BOARD.from, MATCH.BOARD.to) then
self:sendCancelMessage('Sorry, not possible.')
return false
end
if toPosition:isInRange(MATCH.BOARD.from, MATCH.BOARD.to) then
self:sendCancelMessage('Sorry, not possible.')
return false
end
After
Lua:
function Player:onLook(thing, position, distance)
Lua:
if thing:isItem() and (thing.actionid == 31337) then
local pid = self:getId()
if MATCH.SCORE[pid] == nil then
return false
end
if thing:getId() ~= MATCH.HIDDEN then
return false
end
if getValue(MATCH.EXHAUST[pid]) >= os.time(t) then
self:say("Wait...", TALKTYPE_MONSTER_SAY, false, self)
return false
end
local a = MATCH.COUNT[pid]
if a == 2 then
return false
end
MATCH.EXHAUST[pid] = os.time(t) + 1
MATCH.COUNT[pid] = a + 1
self:getPosition():sendDistanceEffect(position, 31)
local v, p = showRune(pid, thing), MATCH.POS[pid]
enableSleep(function()
if v ~= nil then
pause(500)
MATCH.POS[pid] = 0
MATCH.LAST[pid] = 0
MATCH.COUNT[pid] = 0
if v then
for _, pos in ipairs({p, position}) do
local b = Tile(pos):getTopVisibleThing(false)
if isMatchRune(b:getId()) then
b:remove()
pos:sendMagicEffect(10)
end
end
self:say("MATCH!", TALKTYPE_MONSTER_SAY, false, nil, p)
self:say("MATCH!", TALKTYPE_MONSTER_SAY, false, nil, position)
p:sendDistanceEffect(self:getPosition(), 36)
position:sendDistanceEffect(self:getPosition(), 36)
local u = getValue(MATCH.SCORE[pid])
MATCH.SCORE[pid] = u + 2
local k = (getValue(MATCH.SCORE[pid])/(MATCH.SIZE[1]*MATCH.SIZE[2]))*100
self:say(math.floor(k) .. "%", TALKTYPE_MONSTER_SAY)
self:sendCancelMessage(getValue(MATCH.SCORE[pid])/2 .. "/".. (MATCH.SIZE[1]*MATCH.SIZE[2])/2 .." runes unlocked")
if isBoardEmpty() then
endMatch()
end
else
hideRune(p)
hideRune(position)
self:say("FAIL!", TALKTYPE_MONSTER_SAY, false, nil, p)
self:say("FAIL!", TALKTYPE_MONSTER_SAY, false, nil, position)
stopEvent(MATCH.EVENT[pid])
end
end
end)
return false
end
Lever used is 1945/1946
Set UniqueID 1337 to lever
Optional settings:
Add reward to player with top score, only if his score is higher than 2nd in rank:
Before
Lua:
Game.broadcastMessage(str, MESSAGE_EVENT_ADVANCE)
Lua:
if h[1][2] > h[2][2] then
local topPlayer = Player(h[1][1])
topPlayer:addItem(2152, 10)
topPlayer:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "You won cash!")
end
Show # of matches scored instead of %
Change
Lua:
local k = (getValue(MATCH.SCORE[pid])/(MATCH.SIZE[1]*MATCH.SIZE[2]))*100
self:say(math.floor(k) .. "%", TALKTYPE_MONSTER_SAY)
Lua:
self:say(getValue(MATCH.SCORE[pid])/2 .. " matches", TALKTYPE_MONSTER_SAY)
Last edited: