Last active
September 4, 2016 18:34
-
-
Save dbjorkholm/ce69140315b779dde694 to your computer and use it in GitHub Desktop.
[TFS 1.x] Paintball Event
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function onThink(interval, lastExecution, thinkInterval) | |
if #Game.getPlayers() < PaintballEvent.minPlayers or PaintballEvent.startingUp or PaintballEvent.running then | |
return true | |
end | |
PaintballEvent:init() | |
return true | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
local config = { | |
minutesToEnd = 5, | |
minutesToStart = 10, | |
rewards = { | |
{itemId = 2160, count = 5}, | |
{itemId = 2160, count = 4}, | |
{itemId = 2160, count = 3}, | |
{itemId = 2160, count = 2}, | |
{itemId = 2160, count = 1}, | |
}, | |
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, | |
}, | |
ammunition = { | |
infinite = false, | |
refillOnDeath = true, | |
onStart = 100, | |
eachPoint = 2, | |
} | |
bullets = { | |
effect = CONST_ANI_SNOWBALL, | |
exhaustion = 5, -- Exhaustion in seconds | |
speed = 150, -- Speed in milliseconds | |
maxDistance = 6, -- Max distance a bullet can travel | |
}, | |
points = { | |
removeOnDeath = true, | |
removePoints = 1, | |
onKill = 3, | |
} | |
} | |
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 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 | |
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 blockProjectile(position) | |
local tile = Tile(position) | |
if not tile then | |
return false | |
end | |
local ground = tile:getGround() | |
if not ground or ground:hasProperty(CONST_PROP_BLOCKSOLID) then | |
return false | |
end | |
local items = tile:getItems() | |
for _, item in ipairs(items) do | |
local itemType = item:getType() | |
if itemType:getType() ~= ITEM_TYPE_MAGICFIELD and not itemType:isMovable() and item:hasProperty(CONST_PROP_BLOCKPROJECTILE) then | |
return true | |
end | |
end | |
return false | |
end | |
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 | |
if not PaintballEvent then | |
PaintballEvent = { | |
players = { }, | |
running = false, | |
startingUp = false, | |
minPlayers = 10, | |
eventStorage = 1234, | |
ammoStorage = 1235, | |
pointsStorage = 1236, | |
exhaustStorage = 1237, | |
endEvent = nil, | |
} | |
end | |
function PaintballEvent.addPlayer(self, player) | |
if not self.startingUp or self.running then | |
return | |
end | |
-- Send Message | |
player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You have joined the Paintball Event!") | |
-- Add Storages | |
player:setStorageValue(self.eventStorage, 1) | |
player:setStorageValue(self.ammoStorage, 100) | |
player:setStorageValue(self.pointsStorage, 0) | |
player:setStorageValue(self.exhaustStorage, 0) | |
-- 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:getId() | |
end | |
function PaintballEvent.addReward(self, playerId, scoreId) | |
local reward = config.rewards[scoreId] | |
if not reward then | |
return | |
end | |
local player = Player(playerId) | |
if not player or not player:addItem(reward.itemId, reward.count) then | |
return | |
end | |
player:sendTextMessage(MESSAGE_EVENT_ADVANCE, string.format('You received %s for coming in %d%s place.', ItemType(reward.itemId):getNameDescription(reward.count), scoreId, getNumberOrdinal(scoreId))) | |
end | |
function PaintballEvent.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 PaintballEvent.getTopScorers(self) | |
local scorers = { } | |
if #self.players ~= 0 then | |
for i = 1, #self.players do | |
local tmpPlayer = Player(self.players[i]) | |
if tmpPlayer then | |
scorers[#scorers + 1] = {id = tmpPlayer:getId(), name = tmpPlayer:getName(), points = math.max(0, tmpPlayer:getStorageValue(self.pointsStorage))} | |
end | |
end | |
end | |
table.sort(scorers, function(a, b) return a.points > b.points end) | |
for i = 1, #scorers do | |
scorers[#scorers] = nil | |
if #scorers == #config.rewards then | |
break | |
end | |
end | |
return scorers | |
end | |
function PaintballEvent.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 Paintball Event will start in %d minute%s. You can join the event by typing !paintball join.', config.minutesToStart - i, config.minutesToStart - i ~= 1 and "s" or ""), MESSAGE_EVENT_ADVANCE) | |
end | |
end | |
-- Create Teleport | |
if config.teleport.enabled then | |
local tmpTeleport = Game.createItem(1387, 1, config.teleport.position) | |
if tmpTeleport then | |
tmpTeleport:setAttribute(ITEM_ATTRIBUTE_ACTIONID, config.teleport.actionId) | |
end | |
end | |
self.startingUp = true | |
end | |
function PaintballEvent.isInRange(self, position) | |
return isInRange(position, config.spawnArea.fromPosition, config.spawnArea.toPosition) | |
end | |
function PaintballEvent.onPlayerCommands(self, player, keyword) | |
if keyword == 'join' then | |
if not self.startingUp then | |
player:sendCancelMessage("The event is not running at the moment.") | |
return false | |
end | |
if self.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(self.eventStorage) ~= -1 then | |
player:sendCancelMessage("You have already joined the event.") | |
return false | |
end | |
self:addPlayer(player) | |
elseif keyword == 'info' then | |
if not self.running or not self:isInRange(player:getPosition()) then | |
player:sendCancelMessage("This command is only available for players whom are inside the event.") | |
return false | |
end | |
local text = string.format("You have %d points.\nYou have %d ammo left.\n------------------\nThe current scores in the event is:\n", math.max(0, player:getStorageValue(self.pointsStorage)), player:getStorageValue(self.ammoStorage)) | |
local topScorers = self:getTopScorers() | |
if #topScorers ~= 0 then | |
text = string.format("%s\nTop Scorers:\n", text) | |
for scoreId, scorer in ipairs(topScorers) do | |
self:addReward(scorer.id, scoreId) | |
text = string.format("%s%d%s. %s - %d points.\n", text, scoreId, getNumberOrdinal(scoreId), scorer.name, scorer.points) | |
end | |
end | |
player:popupFYI(text) | |
elseif keyword == 'ammo' then | |
if not self.running or not self:isInRange(player:getPosition()) then | |
player:sendCancelMessage("This command is only available for players whom are inside the event.") | |
return false | |
end | |
if config.ammunition.infinite then | |
player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "The ammunition is infinite, there is no need to buy more.") | |
return false | |
end | |
local pointsStorage = math.max(0, player:getStorageValue(self.pointsStorage)) | |
if pointsStorage == 0 then | |
player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You do not have enough points to buy ammunition, you need " .. 1 - pointsStorage .. " more.") | |
return false | |
end | |
player:setStorageValue(self.pointsStorage, pointsStorage - 1) | |
player:setStorageValue(self.ammoStorage, player:getStorageValue(self.ammoStorage) + config.ammunition.eachPoint) | |
player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You have received " .. config.ammunition.eachPoint .. " bullets and you have lost 1 score point.") | |
elseif keyword == 'shoot' then | |
if not self.running or not self:isInRange(player:getPosition()) then | |
player:sendCancelMessage("This command is only available for players whom are inside the event.") | |
return false | |
end | |
local currentTime = os.time() | |
if player:getStorageValue(self.exhaustStorage) > currentTime then | |
player:sendCancelMessage("Gun is on cooldown") | |
return false | |
end | |
local ammoStorage = math.max(0, player:getStorageValue(self.ammoStorage)) | |
if ammoStorage == 0 then | |
player:sendCancelMessage("You are out of ammunition, you can exchange points for ammunition by typing !paintball ammo, or die for a recharge.") | |
return false | |
end | |
if not config.ammunition.infinite then | |
player:setStorageValue(self.ammoStorage, ammoStorage - 1) | |
end | |
player:setStorageValue(self.exhaustStorage, currentTime + config.bullets.exhaustion) | |
self:onPlayerShoot(player:getId(), player:getDirection(), player:getPosition(), 1) | |
else | |
player:sendCancelMessage("Insufficient parameters.") | |
end | |
end | |
function PaintballEvent.onPlayerDeath(self, playerId, killerId) | |
if not self.running then | |
return | |
end | |
local player = Player(playerId) | |
if not player then | |
return | |
end | |
local killer = Player(killerId) | |
if not killer then | |
return | |
end | |
local playerName, killerName = player:getName(), killer:getName() | |
-- Killer Actions | |
killer:setStorageValue(self.pointsStorage, math.max(0, killer:getStorageValue(self.pointsStorage)) + config.points.onKill) | |
killer:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You killed " .. playerName .. ".") | |
-- Player Actions | |
if config.points.removeOnDeath then | |
player:setStorageValue(self.pointsStorage, player:getStorageValue(self.pointsStorage) - config.points.removePoints) | |
end | |
if config.ammunition.refillOnDeath then | |
player:setStorageValue(self.ammoStorage, config.ammunition.onStart) | |
end | |
player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You were killed by " .. killerName .. ".") | |
local teleportPosition = getRandomSpawnPosition(config.spawnArea.fromPosition, config.spawnArea.toPosition) | |
player:teleportTo(teleportPosition) | |
teleportPosition:sendMagicEffect(CONST_ME_TELEPORT) | |
-- Broadcast "Death" | |
self:broadcastMessage(MESSAGE_EVENT_ADVANCE, string.format("[Paintball Event]: %s were killed by %s.", playerName, killerName)) | |
end | |
function PaintballEvent.onPlayerShoot(self, playerId, direction, fromPosition, distance) | |
if not self.running then | |
return | |
end | |
local targetPosition = fromPosition + Position.directionOffset[direction] | |
if blockProjectile(targetPosition) then | |
targetPosition:sendMagicEffect(CONST_ME_LOSEENERGY) | |
return | |
end | |
fromPosition:sendDistanceEffect(targetPosition, CONST_ANI_SNOWBALL) | |
local tile = Tile(targetPosition) | |
if tile then | |
local creature = tile:getTopCreature() | |
if creature and creature:isPlayer() then | |
local creatureId = creature:getId() | |
if creatureId ~= playerId then | |
self:onPlayerDeath(creatureId, playerId) | |
targetPosition:sendMagicEffect(CONST_ME_DRAWBLOOD) | |
return | |
end | |
end | |
end | |
distance = distance + 1 | |
if distance ~= config.bullets.maxDistance then | |
addEvent(function(self, playerId, direction, fromPosition, distance) self:onPlayerShoot(playerId, direction, fromPosition, distance) end, config.bullets.speed, self, playerId, direction, targetPosition, distance) | |
end | |
end | |
function PaintballEvent.releasePlayer(self, playerId, message) | |
local player = Player(playerId) | |
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 Storages | |
player:setStorageValue(self.eventStorage, -1) | |
player:setStorageValue(self.ammoStorage, -1) | |
player:setStorageValue(self.pointsStorage, -1) | |
player:setStorageValue(self.exhaustStorage, -1) | |
-- Teleport player to temple | |
local templePosition = player:getTown():getTemplePosition() | |
player:teleportTo(templePosition) | |
templePosition:sendMagicEffect(CONST_ME_TELEPORT, player) | |
end | |
function PaintballEvent.removePlayer(self, playerId, message) | |
if not self.running or #self.players == 0 then | |
return | |
end | |
for i = 1, #self.players do | |
if self.players[i] == playerId then | |
self:releasePlayer(playerId, message) | |
table.remove(self.players, i) | |
return | |
end | |
end | |
end | |
function PaintballEvent.start(self) | |
if not self.startingUp or self.running then | |
return | |
end | |
if #self.players < self.minPlayers then | |
self:stop("The Paintball Event did not start due to not enough participants. You got sent back to your hometown.") | |
Game.broadcastMessage("The Paintball 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:broadcastMessage(MESSAGE_EVENT_ADVANCE, "Welcome to paintball, the following commands are available:\n!paintball shoot --This will shot a bullet.\n!paintball ammo --This will give you " .. config.ammunition.eachPoint .. " bullets and take 1 point from your current score (you need at least 1 point to use this command).\n!paintball info --This will show you your current score and ammo, it'll also show the current high score of the event.\nIt is strongly recommended that you bind these commands to your hotkeys.") | |
self.running = true | |
self.startingUp = false | |
self.endEvent = addEvent(function(self) | |
local text = "The Paintball Event has ended." | |
local topScorers = self:getTopScorers() | |
if #topScorers ~= 0 then | |
text = string.format("%s\nTop Scorers:\n", text) | |
for scoreId, scorer in ipairs(topScorers) do | |
self:addReward(scorer.id, scoreId) | |
text = string.format("%s%d%s. %s - %d points.\n", text, scoreId, getNumberOrdinal(scoreId), scorer.name, scorer.points) | |
end | |
end | |
self:stop(text) | |
end, config.minutesToEnd * 60000, self) | |
Game.broadcastMessage("The Paintball Event has started. Good luck to all participants!", MESSAGE_EVENT_ADVANCE) | |
end | |
function PaintballEvent.stop(self, message) | |
if #self.players ~= 0 then | |
for i = 1, #self.players do | |
self:releasePlayer(self.players[i], message) | |
end | |
end | |
if self.endEvent then | |
stopEvent(self.endEvent) | |
end | |
-- Remove Teleport | |
if config.teleport.enabled then | |
local tmpTeleport = Tile(config.teleport.position):getItemById(1387) | |
if tmpTeleport then | |
tmpTeleport:remove() | |
end | |
end | |
self.players = { } | |
self.running = false | |
self.startingUp = false | |
self.endEvent = nil | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 PaintballEvent.startingUp then | |
pushPlayer(player, "The event is not running at the moment.", position, fromPosition) | |
return true | |
end | |
if PaintballEvent.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(PaintballEvent.eventStorage) ~= -1 then | |
pushPlayer(player, "You have already joined the event.", position, fromPosition) | |
return true | |
end | |
PaintballEvent:addPlayer(player) | |
return true | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function onSay(player, words, param) | |
if param == "" then | |
player:sendCancelMessage("Insufficient parameters.") | |
return false | |
end | |
PaintballEvent:onPlayerCommands(player, param:lower()) | |
return false | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment