Skip to content

Instantly share code, notes, and snippets.

@dbjorkholm
Last active March 11, 2016 15:52
Show Gist options
  • Save dbjorkholm/4232630711b0f4e7c41e to your computer and use it in GitHub Desktop.
Save dbjorkholm/4232630711b0f4e7c41e to your computer and use it in GitHub Desktop.
[TFS 1.1+] Zombie Event
function onPrepareDeath(player, killer)
if not ZombieEvent.running or not ZombieEvent:isInRange(player:getPosition()) or player:getStorageValue(ZombieEvent.eventStorage) ~= 1 then
return true
end
local killerName = "a field item"
if killer then
local master = killer:getMaster()
if master and master ~= killer and master:isPlayer() then
killer = master
end
killerName = killer:isMonster() and killer:getType():getNameDescription() or killer:getName()
end
ZombieEvent:onPlayerDeath(player:getName(), killerName)
return false
end
function onThink(interval, lastExecution, thinkInterval)
if #Game.getPlayers() < ZombieEvent.minPlayers
or ZombieEvent.nextExecution > os.time()
or ZombieEvent.startingUp
or ZombieEvent.running then
return true
end
ZombieEvent:init()
return true
end
local config = {
minutesToStart = 10,
minutesToNextExecution = 15,
firstSpawnInterval = 10 * 1000,
spawnInterval = 15 * 1000,
monsterName = "ZombieEvent",
rewards = {
{itemId = 2160, count = 10},
{itemId = 2160, count = 5},
{itemId = 2160, count = 3},
},
spawnArea = {fromPosition = Position(1000, 1000, 7), toPosition = Position(1000, 1000, 7)},
waitingRoomArea = {fromPosition = Position(1000, 1000, 7), toPosition = Position(1000, 1000, 7)},
teleport = {
enabled = false,
position = Position(1000, 1000, 7),
actionId = 1000,
},
}
local function getRandomSpawnPosition(fromPosition, toPosition)
local random = math.random
return Position(random(fromPosition.x, toPosition.x), random(fromPosition.y, toPosition.y), random(fromPosition.z, toPosition.z))
end
local function getNumberOrdinal(number)
if number >= 11 and number <= 19 then
return "th"
end
local number = number % 10
local ordinals = {"st", "nd", "rd"}
return ordinals[number] or "th"
end
local function isInRange(pos, fromPos, toPos)
return pos.x >= fromPos.x and pos.y >= fromPos.y and pos.z >= fromPos.z and pos.x <= toPos.x and pos.y <= toPos.y and pos.z <= toPos.z
end
if not ZombieEvent then
ZombieEvent = {
players = { },
zombies = { },
running = false,
startingUp = false,
spawnEvent = nil,
minPlayers = 10,
nextExecution = 0,
round = 1,
eventStorage = 1234,
eventName = "ZombieEvent",
}
end
-- Functions A-Z
function ItemType.getNameDescription(self, count)
local count = count or 1
local description = ""
if self:isRune() then
description = string.format("%s %s (%d charges)", self:getArticle(), self:getName(), count)
elseif self:isStackable() and count > 1 then
description = string.format("%d %s", count, self:getPluralName())
elseif self:getArticle() ~= '' then
description = string.format("%s %s", self:getArticle(), self:getName())
else
description = self:getName()
end
return description
end
function ZombieEvent.addPlayer(self, player)
if not self.startingUp or self.running then
return
end
-- Send Message
player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You have joined the Zombie Event!")
-- Add Storage
player:setStorageValue(self.eventStorage, 1)
-- Register Event
player:registerEvent(self.eventName)
-- Teleport Player
local waitingRoomPosition = getRandomSpawnPosition(config.waitingRoomArea.fromPosition, config.waitingRoomArea.toPosition)
player:getPosition():sendMagicEffect(CONST_ME_POFF)
player:teleportTo(waitingRoomPosition)
waitingRoomPosition:sendMagicEffect(CONST_ME_TELEPORT)
self.players[#self.players + 1] = player:getName()
end
function ZombieEvent.addReward(self, playerName)
local reward = config.rewards[#self.players]
if not reward then
return
end
local player = Player(playerName)
if not player then
return
end
local rewardItem = Game.createItem(reward.itemId, reward.count)
if not rewardItem then
return
end
local ret = player:addItemEx(rewardItem)
if ret ~= RETURNVALUE_NOERROR then
local inbox = player:getInbox()
if inbox then
inbox:addItemEx(rewardItem, INDEX_WHEREEVER, FLAG_NOLIMIT)
end
end
player:sendTextMessage(MESSAGE_EVENT_ADVANCE, string.format('You received %s for coming in %d%s place. %s', ItemType(reward.itemId):getNameDescription(reward.count), #self.players, getNumberOrdinal(#self.players), ret ~= 0 and "The reward was sent to your inbox due not enough capacity or room in your backpack." or ""))
end
function ZombieEvent.addTeleport(self)
if not self.startingUp or not config.teleport.enabled then
return
end
local tile = Tile(config.teleport.position)
if not tile or tile:getItemById(1387) then
return
end
local teleport = Game.createItem(1387, 1, config.teleport.position)
if not teleport then
return
end
teleport:setActionId(config.teleport.actionId)
end
function ZombieEvent.broadcastMessage(self, type, message)
if not self.running or #self.players == 0 then
return
end
for i = 1, #self.players do
local tmpPlayer = Player(self.players[i])
if tmpPlayer then
tmpPlayer:sendTextMessage(type, message)
end
end
end
function ZombieEvent.init(self)
if self.startingUp or self.running then
return
end
for i = 0, config.minutesToStart do
if i == config.minutesToStart then
addEvent(function(self) self:start() end, i * 60000, self)
else
addEvent(Game.broadcastMessage, i * 60000, string.format('The Zombie Event will start in %d minute%s. You can join the event by typing !joinzombie.', config.minutesToStart - i, config.minutesToStart - i ~= 1 and "s" or ""), MESSAGE_EVENT_ADVANCE)
end
end
self.startingUp = true
self:addTeleport()
end
function ZombieEvent.isInRange(self, position)
return isInRange(position, config.spawnArea.fromPosition, config.spawnArea.toPosition)
end
function ZombieEvent.onPlayerDeath(self, playerName, killerName)
if not self.running or #self.players <= 1 then
return
end
self:addReward(playerName)
self:removePlayer(playerName, string.format("You were killed by %s", killerName))
if #self.players ~= 1 then
return
end
self:onWinner(self.players[1])
end
function ZombieEvent.onWinner(self, playerName)
if not self.running or #self.players ~= 1 then
return
end
local winner = Player(playerName)
local round = self.round ~= 1 and self.round - 1 or self.round
if winner then
self:addReward(playerName)
self:releasePlayer(playerName, string.format("You won the Zombie Event in the %d%s round. Congratulations!", round, getNumberOrdinal(round)))
Game.broadcastMessage(string.format("%s is the winner of the Zombie Event in the %d%s round. Congratulations!", winner:getName(), round, getNumberOrdinal(round)), MESSAGE_EVENT_ADVANCE)
end
self:stop("The Zombie Event is over.")
end
function ZombieEvent.releasePlayer(self, playerName, message)
local player = Player(playerName)
if not player then
return
end
-- Send Text Message
if message then
player:sendTextMessage(MESSAGE_INFO_DESCR, message)
end
-- Add Health/Mana
player:addHealth(player:getMaxHealth())
player:addMana(player:getMaxMana())
-- Remove Storage
player:setStorageValue(self.eventStorage, -1)
-- Unregister Event
player:unregisterEvent(self.eventName)
-- Teleport player to temple
local templePosition = player:getTown():getTemplePosition()
player:teleportTo(templePosition)
templePosition:sendMagicEffect(CONST_ME_TELEPORT, player)
end
function ZombieEvent.removePlayer(self, playerName, message)
if not self.running or #self.players == 0 then
return
end
for i = 1, #self.players do
if self.players[i] == playerName then
self:releasePlayer(playerName, message)
table.remove(self.players, i)
return
end
end
end
function ZombieEvent.removeTeleport(self)
if not config.teleport.enabled then
return
end
local tile = Tile(config.teleport.position)
if not tile then
return
end
local teleport = tile:getItemById(1387)
if not teleport then
return
end
teleport:remove()
end
function ZombieEvent.spawnZombies(self)
if not self.running then
return
end
local amount = self.round * 2 - 1
for i = 1, amount do
local tmpMonster = Game.createMonster(config.monsterName, getRandomSpawnPosition(config.spawnArea.fromPosition, config.spawnArea.toPosition), true)
if tmpMonster then
self.zombies[#self.zombies + 1] = tmpMonster:getId()
end
end
self:broadcastMessage(MESSAGE_EVENT_ADVANCE, string.format("The %d%s wave of zombies will spawn shortly. Prepare yourself!", self.round, getNumberOrdinal(self.round)))
self.round = self.round + 1
self.spawnEvent = addEvent(function(self) self:spawnZombies() end, config.spawnInterval, self)
end
function ZombieEvent.start(self)
if not self.startingUp or self.running then
return
end
if #self.players < self.minPlayers then
self:stop("The Zombie Event did not start due to not enough participants. You were sent back to your hometown.")
Game.broadcastMessage("The Zombie Event did not start due to not enough participants.", MESSAGE_EVENT_ADVANCE)
return
end
for i = 1, #self.players do
local tmpPlayer = Player(self.players[i])
if tmpPlayer then
tmpPlayer:teleportTo(getRandomSpawnPosition(config.spawnArea.fromPosition, config.spawnArea.toPosition))
end
end
self.running = true
self.startingUp = false
self.round = 1
self.spawnEvent = addEvent(function(self) self:spawnZombies() end, config.firstSpawnInterval, self)
self:removeTeleport()
self:broadcastMessage(MESSAGE_EVENT_ADVANCE, "The first wave of zombies will spawn shortly. Prepare yourself!")
Game.broadcastMessage("The Zombie Event has started. Good luck to all participants!", MESSAGE_EVENT_ADVANCE)
end
function ZombieEvent.stop(self, message)
-- Remove Players
if #self.players ~= 0 then
for i = 1, #self.players do
self:releasePlayer(self.players[i], message)
end
end
-- Remove Zombies
if #self.zombies ~= 0 then
for i = 1, #self.zombies do
local tmpMonster = Monster(self.zombies[i])
if tmpMonster then
tmpMonster:remove()
end
end
end
-- Stop spawn event
if self.spawnEvent then
stopEvent(self.spawnEvent)
end
-- Remove Teleport
self:removeTeleport()
self.players = { }
self.zombies = { }
self.running = false
self.startingUp = false
self.round = 1
self.nextExecution = os.time() + config.minutesToNextExecution
self.spawnEvent = nil
end
<?xml version="1.0" encoding="UTF-8"?>
<monster name="Zombie" nameDescription="a zombie" race="undead" experience="0" speed="100" manacost="0">
<health now="500" max="500"/>
<look type="311" corpse="0"/>
<targetchange interval="4000" chance="10"/>
<strategy attack="100" defense="0"/>
<flags>
<flag summonable="0"/>
<flag attackable="1"/>
<flag hostile="1"/>
<flag illusionable="0"/>
<flag convinceable="0"/>
<flag pushable="0"/>
<flag canpushitems="1"/>
<flag canpushcreatures="1"/>
<flag targetdistance="1"/>
<flag staticattack="90"/>
<flag runonhealth="0"/>
</flags>
<attacks>
<attack name="melee" interval="2000" min="999999" max="999999" />
</attacks>
<defenses armor="15" defense="15"/>
<immunities>
<immunity death="1"/>
<immunity fire="1"/>
<immunity holy="1"/>
<immunity physical="1"/>
<immunity energy="1"/>
<immunity ice="1"/>
<immunity earth="1"/>
<immunity drown="1"/>
<immunity drunk="1"/>
<immunity lifedrain="1"/>
<immunity paralyze="1"/>
</immunities>
<voices interval="5000" chance="10">
<voice sentence="Mst.... klll...."/>
<voice sentence="Whrrrr... ssss.... mmm.... grrrrl"/>
<voice sentence="Dnnnt... cmmm... clsrrr...."/>
<voice sentence="Httt.... hmnnsss..."/>
</voices>
</monster>
local function pushPlayer(player, text, position, fromPosition)
if not player then
return
end
player:sendCancelMessage(text)
player:teleportTo(fromPosition)
position:sendMagicEffect(CONST_ME_TELEPORT)
fromPosition:sendMagicEffect(CONST_ME_TELEPORT)
end
function onStepIn(creature, item, position, fromPosition)
local player = creature:getPlayer()
if not player then
return true
end
if not ZombieEvent.startingUp then
pushPlayer(player, "The event is not running at the moment.", position, fromPosition)
return true
end
if ZombieEvent.running then
pushPlayer(player, "The event has already started.", position, fromPosition)
return true
end
if player:getGroup():getAccess() then
pushPlayer(player, "Staff members are not allowed to join the event.", position, fromPosition)
return true
end
if player:isPzLocked() then
pushPlayer(player, "You cannot join the event while you are in a fight.", position, fromPosition)
return true
end
if player:getStorageValue(ZombieEvent.eventStorage) ~= -1 then
pushPlayer(player, "You have already joined the event.", position, fromPosition)
return true
end
ZombieEvent:addPlayer(player)
return true
end
function onSay(player, words, param)
if not ZombieEvent.startingUp then
player:sendCancelMessage("The event is not running at the moment.")
return false
end
if ZombieEvent.running then
player:sendCancelMessage("The event has already started.")
return false
end
if player:getGroup():getAccess() then
player:sendCancelMessage("Staff members are not allowed to join the event.")
return false
end
if player:isPzLocked() then
player:sendCancelMessage("You cannot join the event while you are in a fight.")
return false
end
if player:getStorageValue(ZombieEvent.eventStorage) ~= -1 then
player:sendCancelMessage("You have already joined the event.")
return false
end
ZombieEvent:addPlayer(player)
return false
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment