• There is NO official Otland's Discord server and NO official Otland's server list. The Otland's Staff does not manage any Discord server or server list. Moderators or administrator of any Discord server or server lists have NO connection to the Otland's Staff. Do not get scammed!

Tower Defense Event TFS 1.0

Vanderlay

Back on track
Premium User
Joined
Sep 20, 2007
Messages
893
Reaction score
109
Location
Sweden
This event was created by @Printer, @Limos and @Ninja but was not working correctly for TFS 1.0 so I decided to release a new version which works 100% for TFS 1.0.
Original version can be seen here: Tower Defense Event

I also rewrote the entire functionality for making the monsters move.
If you are changing the position of the map or creating your own one, all you need to do is to go to /lib/TWD/towerDefenseLib.lua at line 160 and edit the starting walk direction:
Code:
local function summonMonster(name)
    local monster = Game.createMonster(name .."_TWD", summonMonsterPosition, false, true)
    if monster then
        monster:setDirection(EAST)
        monsterWalkTo(monster, 'EAST')
        summonMonsterPosition:sendMagicEffect(CONST_ME_TELEPORT)
        monster:changeSpeed(-monster:getSpeed() + 130)

        local extraHealth = monsters[name].extraHealth
        if extraHealth then
            monster:setMaxHealth(monster:getMaxHealth() + extraHealth)
            monster:addHealth(monster:getMaxHealth())
        end
    end
end

And also in /lib/TWD/towerDefenseConfig.lua at line 21 you need to edit the starting position.
Code:
summonMonsterPosition = Position(31530, 32782, 7) -- Where should the monster be created?

The path system does not handle diagonal directions, just up, down, left and right.

More additions might come later on.

You can clone the repository from github: https://github.com/Vanderlay/tfs-TDEvent
 
Cheers mate :) That is what i like, take original code and learn from it, improve it and even adding more features.
 
you can update it to 1.4.2, I will be very grateful.
 
C++:
Lua Script Error: [Action Interface]
data/actions/scripts/TDEvent/twdHammer.lua:onUse
data/actions/scripts/TDEvent/twdHammer.lua:9: attempt to call method 'getTile' (a nil value)
stack traceback:
        [C]: in function 'getTile'
        data/actions/scripts/TDEvent/twdHammer.lua:9: in function <data/actions/scripts/TDEvent/twdHammer.lua:3>
[Warning - Monsters::loadMonster] targetdistance less than 1. data/monster/TDEvent/rat.xml
C++:
Lua Script Error: [Main Interface]
in a timer event called from:
(Unknown scriptfile)
data/lib/TWD/towerDefenseLib.lua:90: attempt to call method 'getTile' (a nil value)
stack traceback:
        [C]: in function 'getTile'
        data/lib/TWD/towerDefenseLib.lua:90: in function 'checkTile'
        data/lib/TWD/towerDefenseLib.lua:51: in function 'monsterPathFinding'
        data/lib/TWD/towerDefenseLib.lua:102: in function 'monsterWalkTo'
        data/lib/TWD/towerDefenseLib.lua:160: in function <data/lib/TWD/towerDefenseLib.lua:156>

twdHammer.lua
Lua:
dofile('data/lib/TWD/towerDefenseLib.lua')

function onUse(cid, item, fromPosition, target, toPosition, isHotkey)
    local player = Player(cid)
    if player:getStorageValue(playingGameStorage) ~= 1 then
        return false
    end

    local tile = toPosition:getTile()
    if tile then
        if not tile:hasFlag(TILESTATE_PROTECTIONZONE) or tile:hasProperty(CONST_PROP_IMMOVABLEBLOCKSOLID) then
            player:sendCancelMessage("You cannot place the turret here.")
            return true
        end
        local npc = Creature(tile:getTopCreature())
        if not npc then
            local modalWindow = ModalWindow(100, "Build Turret", "Here you can select variations of turrets to build.")
            local turret, cfgTable = turrets.allTurretsId
            for i = 1, #turret do
                turret = turrets.allTurretsId[i]
                cfgTable = turrets[turret].cfg
                modalWindow:addChoice(turret, string.format("%s [%s coins]", cfgTable.turretName, cfgTable[1].buildPrice))
            end

            modalWindow:addButton(0, "Build")
            modalWindow:setDefaultEnterButton(0)
            modalWindow:addButton(1, "Cancel")
            modalWindow:setDefaultEscapeButton(1)
            modalWindow:sendToPlayer(player)
            turretPosition = toPosition
        elseif npc:isNpc() and npc:getName() == "Turret" then
            local table = turrets[npc:getOutfit().lookType]
            local lvl = npc:getTurretLevel()
            local cfg, cfgCombat = table.cfg[lvl], table.combat[lvl]

            local turrentInfo = string.format("Turret Information\n----------------------------\nTurret Level: %s\nAttack Type: %s\nRange SQM: %sx%s\nTurret Damage: [%s - %s]\nAttack Speed: %s\nSell/Upgrade Price: [%s / %s]", lvl, string.upper(cfgCombat.attackType), cfg.rangeX, cfg.rangeY, cfgCombat.dmgValues[1], cfgCombat.dmgValues[2], cfg.attackSpeed, cfg.sellPrice, cfg.upgradePrice)
            local playerInfo = string.format("Player Information\n----------------------------\nWave Level: %s\nYour Coins: %s", getWaveLevel(), player:getCoins())
            local modalWindow = ModalWindow(101, "Information", string.format("%s\n\n%s", turrentInfo, playerInfo))

            if lvl < 3 then
                modalWindow:addChoice(0, "Upgrade")
            end
            modalWindow:addChoice(1, "Sell")

            modalWindow:addButton(0, "Yes")
            modalWindow:setDefaultEnterButton(0)
            modalWindow:addButton(0x01, "Cancel")
            modalWindow:setDefaultEscapeButton(1)
            modalWindow:sendToPlayer(player)
            targetTurret = npc
        end
    end
    return true
end
towerDefenseLib.lua
Lua:
dofile('data/lib/TWD/towerDefenseSpellsArea.lua')
dofile('data/lib/TWD/towerDefenseConfig.lua')
 
targetTurret = nil
turretPosition = nil
 
local twdEvents = {
    "TWDOnLose",
    "TWDBuildWindow",
    "TWDOtherWindow",
    "TWDHealthChange"
}
 
function Player.resetValues(self)
    self:removeItem(2557, 1)
    self:setStorageValue(coinStorage, 0)
    self:addHealth(self:getMaxHealth())
    self:setStorageValue(playingGameStorage, 0)
    self:teleportTo(self:getTown():getTemplePosition())
    for i = 1, #twdEvents do
        self:unregisterEvent(twdEvents[i])
    end
end
 
function sendReward(cid)
    local player = Player(cid)
    player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You have won the Tower Of Defense Event.")
    player:addItem(2160, 10)
    player:resetValues()
end
 
function resetEvent()
    turretPosition = nil
    targetTurret = nil
    setWaveLevel(0)
    Game.setStorageValue(totalMonsterKillCountGlobalStorage, 0)
    Game.setStorageValue(totalMonsterCountGlobalStorage, 0)
 
    local specs, turrets = Game.getSpectators(eventCenterPosition, false, false, 40, 40, 40, 40)
    for i = 1, #specs do
        turrets = specs[i]
        if turrets:isNpc() and turrets:getName() == "Turret" then
            turrets:remove()
        end
    end
end

function monsterPathFinding(currentDirection, pos)
    local newPos = getNextTile(currentDirection, pos)
    if newPos then
        if checkTile(newPos) then
            return newPos, currentDirection
        elseif currentDirection == 'EAST' or currentDirection == 'WEST' then
            northPos = Position(pos.x, pos.y - 1, pos.z)
            southPos = Position(pos.x, pos.y + 1, pos.z)
            if checkTile(northPos) then
                return northPos, 'NORTH'
            elseif checkTile(southPos) then
                return southPos, 'SOUTH'
            end
        elseif currentDirection == 'SOUTH' or currentDirection == 'NORTH' then
            westPos = Position(pos.x - 1, pos.y, pos.z)
            eastPos = Position(pos.x + 1, pos.y, pos.z)
            if checkTile(westPos) then
                return westPos, 'WEST'
            elseif checkTile(eastPos) then
                return eastPos, 'EAST'
            end
        end
    end
    return false, currentDirection
end

function getNextTile(currentDirection, pos)
    local newPos
    if currentDirection == 'EAST' then
        newPos = Position(pos.x + 1, pos.y, pos.z)
    elseif currentDirection == 'WEST' then
        newPos = Position(pos.x - 1, pos.y, pos.z)
    elseif currentDirection == 'SOUTH' then
        newPos = Position(pos.x, pos.y + 1, pos.z)
    elseif currentDirection == 'NORTH' then
        newPos = Position(pos.x, pos.y - 1, pos.z)
    end

    return newPos
end

function checkTile(position)
    local tile = position:getTile()
    if tile then
        if not tile:hasProperty(CONST_PROP_IMMOVABLEBLOCKSOLID) then
            return true
        end
    end

    return false
end
 
local function monsterWalkTo(monster, currentDirection) -- Limos - Rewritted entirely by Vanderlay
    if monster then
        local newPos, direction = monsterPathFinding(currentDirection, monster:getPosition())
        if newPos then
            monster:teleportTo(newPos, true)
            local speed = monsters[monster:getName()].speed
            if not speed then
                speed = 0
            end
            addEvent(monsterWalkTo, 1000 - speed, monster, direction)
        end
    end
end
 
function Npc.searchTarget(self, xRange, yRange)
    local target = self:getTarget()
    local specs, creatures = Game.getSpectators(self:getPosition(), false, false, xRange, xRange, yRange, yRange)
    for i = 1, #specs do
        if target then -- We already have a target, which is in range. Let's break the loop then
            break
        end
 
        creatures = specs[i]
        if creatures:isMonster() then -- Let's pick a target, which is a monster
            return self:setTarget(creatures)
        end
    end
end
 
function Npc.shootSpell(self, attackType, target, combat, area, min, max, magicEffect, distEffect)
    if attackType == "aoe" then
        doAreaCombatHealth(self, combat, self:getPosition(), area, -min, -max, magicEffect)
    elseif attackType == "targetAoe" then
        doAreaCombatHealth(self, combat, target:getPosition(), area, -min, -max, magicEffect)
        self:getPosition():sendDistanceEffect(target:getPosition(), distEffect)
    else
        doTargetCombatHealth(self, target, combat, -min, -max, magicEffect)
        self:getPosition():sendDistanceEffect(target:getPosition(), distEffect)
    end
end
 
function getPlayerInEvent(xRange, yRange)
    local player
    if player then
        return player
    end
 
    local specs = Game.getSpectators(eventCenterPosition, false, true, xRange, xRange, yRange, yRange)
    for i = 1, #specs do
        if specs[i]:getStorageValue(playingGameStorage) == 1 then
            player = specs[i]
            return player
        end
    end
end
 
local function summonMonster(name)
    local monster = Game.createMonster(name .."_TWD", summonMonsterPosition, false, true)
    if monster then
        monster:setDirection(EAST)
        monsterWalkTo(monster, 'EAST')
        summonMonsterPosition:sendMagicEffect(CONST_ME_TELEPORT)
        monster:changeSpeed(-monster:getSpeed() + 130)
 
        local extraHealth = monsters[name].extraHealth
        if extraHealth then
            monster:setMaxHealth(monster:getMaxHealth() + extraHealth)
            monster:addHealth(monster:getMaxHealth())
        end
    end
end
 
function startWaveLevel(level) -- Ninja
    local table, total = waves, 0
    for a = 1, #waves do
        table = waves[level]
        for b = 1, #table.monsters do
            for c = 1, table.monsters[b].count do
                addEvent(function()
                    addEvent(summonMonster, b * table.monsters[b].interval, table.monsters[b].name)
                end, c * table.interval)
            end
 
            total = total + table.monsters[b].count
        end
        break
    end
 
    Game.setStorageValue(totalMonsterCountGlobalStorage, total)
end     
 
function startNextWave(level, interval)
    addEvent(startWaveLevel, interval * 1000, level)
end
 
function Npc.setTurretLevel(self, level)
    if level > 3 then
        level = 3
    end
 
    local lookId = self:getOutfit().lookType
    local setColor = turrets[lookId].cfg[level].colorId
    self:setOutfit({lookType = lookId, lookHead = setColor , lookBody = setColor, lookLegs = setColor, lookFeet = setColor, lookAddons = level})
end
 
function Npc.getTurretLevel(self)
    local addon = self:getOutfit().lookAddons
    if addon == 0 then
        return 1
    end
 
    return addon
end
 
function getWaveLevel()
    return Game.getStorageValue(waveLevelGlobalStorage) or 0
end
 
function setWaveLevel(lvl)
    Game.setStorageValue(waveLevelGlobalStorage, lvl)
end
 
function Player.getCoins(self)
    return self:getStorageValue(coinStorage)
end
 
function Player.addCoins(self, amount)
    self:setStorageValue(coinStorage, math.max(0, self:getStorageValue(coinStorage)) + amount)
end
please help TFS 1.4.2
 
Back
Top