• 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

Gubailovo

Well-Known Member
Joined
Dec 19, 2013
Messages
407
Solutions
2
Reaction score
62
I asked a question in the main thread, but no one answered me.
I'll ask in support.
C++:
Lua Script Error: [Action Interface]
data/actions/scripts/twdHammer.lua:onUse
data/actions/scripts/twdHammer.lua:8: attempt to call method 'Tile' (a nil value)
stack traceback:
        [C]: in function 'Tile'
        data/actions/scripts/twdHammer.lua:8: in function <data/actions/scripts/twdHammer.lua:3>
twdHammer.lua
Lua:
dofile('data/libs/TWD/towerDefenseLib.lua')

function onUse(player, item, fromPosition, target, toPosition, isHotkey)
    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
    end

    if target:isItem() 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 target:isNpc() and target:getName() == "Turret" then
        local table = turrets[target:getOutfit().lookType]
        local lvl = target: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 = target
    end
    return true
end
10.98 TFS1.3
a more detailed description of the error
 
try change line 8 to

Lua:
local tile = Tile(toPosition)
C++:
Lua Script Error: [Main Interface]
in a timer event called from:
(Unknown scriptfile)
data/lib/compat/compat.lua:315: attempt to call global 'doAreaCombat' (a nil value)
stack traceback:
        [C]: in function 'doAreaCombat'
        data/lib/compat/compat.lua:315: in function 'doAreaCombatHealth'
        data/libs/TWD/towerDefenseLib.lua:114: in function 'shootSpell'
        data/npc/scripts/Turret.lua:47: in function <data/npc/scripts/Turret.lua:29>
Turret.lua
Lua:
local keywordHandler = KeywordHandler:new()
local npcHandler = NpcHandler:new(keywordHandler)
NpcSystem.parseParameters(npcHandler)

function onCreatureDisappear(cid) npcHandler:onCreatureDisappear(cid) end
function onCreatureSay(cid, type, msg) npcHandler:onCreatureSay(cid, type, msg) end
function onThink() npcHandler:onThink() end

dofile('data/libs/TWD/towerDefenseLib.lua')

local function searchForTarget(cid)
    local npc = Npc(cid)
    if not npc then
        return false
    end

    local lvl = npc:getTurretLevel()
    local table = turrets[npc:getOutfit().lookType]
    if not table then
        print("[ERROR]: This turret does not exsist in the turrets table.")
        return false
    end

    local cfg = table.cfg[lvl]
    npc:searchTarget(cfg.rangeX, cfg.rangeY)
    addEvent(searchForTarget, 100, cid)
end

local function onAttack(cid)
    local npc = Npc(cid)
    if not npc then
        return false
    end

    local lvl = npc:getTurretLevel()
    local table = turrets[npc:getOutfit().lookType]
    local cfg = table.cfg[lvl]

    if not table then
        print("[ERROR]: This turret does not exsist in the turrets table.")
        return false
    end

    local target = npc:getTarget()
    if target then
        local cfgCombat = table.combat[lvl]
        npc:shootSpell(cfgCombat.attackType, target, cfgCombat.combatType, cfgCombat.combatArea, cfgCombat.dmgValues[1], cfgCombat.dmgValues[2], cfgCombat.magicEffect, cfgCombat.shootEffect)
        npc:setFocus(target)
    end

    addEvent(onAttack, cfg.attackSpeed, cid)
end

function onCreatureAppear(creature)
    local cid = creature:getId()
    onAttack(cid)
    searchForTarget(cid)
    npcHandler:onCreatureAppear(creature)
end
 
Code:
towerDefenseLib.lua
show this file, here is old function "doAreaCombat" that must be changed to 1.x code
 
Code:
towerDefenseLib.lua
show this file, here is old function "doAreaCombat" that must be changed to 1.x code
Lua:
dofile('data/libs/TWD/towerDefenseSpellsArea.lua')
dofile('data/libs/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

-- Everytime the monster need to turn, you have to write the position of the turning point and where it should walk after it reached the point.
local walkPaths = {
    Position(1037, 1221, 7),
    Position(1037, 1230, 7),
    Position(1028, 1230, 7),
    Position(1028, 1236, 7),
    Position(1039, 1236, 7),
    Position(1039, 1241, 7),
    Position(1044, 1241, 7),
    Position(1044, 1236, 7),
    Position(1051, 1236, 7),
    Position(1051, 1224, 7),
    Position(1048, 1224, 7),
    Position(1048, 1217, 7),
    Position(1047, 1217, 7)
}

local function monsterWalkTo(cid, fromPos, toPos, state) -- Limos
    local toPosState = toPos[state]
    if not toPosState then
        return false
    end

    if fromPos.y == toPosState.y then
        fromPos.x = fromPos.x > toPosState.x and fromPos.x - 1 or (fromPos.x < toPosState.x and fromPos.x + 1 or fromPos.x)
         else
        fromPos.y = fromPos.y > toPosState.y and fromPos.y - 1 or (fromPos.y < toPosState.y and fromPos.y + 1 or fromPos.y)
         end

    local monster = Monster(cid)
    if not monster then
        return false
    end

         monster:teleportTo(fromPos, true)
         if fromPos.x == toPosState.x and fromPos.y == toPosState.y then
             state = state + 1
         end

    local speed = monsters[monster:getName()].speed
    if not speed then
        speed = 0
    end

         addEvent(monsterWalkTo, 1000 - speed, cid, fromPos, toPos, state)
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:getId(), monster:getPosition(), walkPaths, 1)
        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
 

Attachments

Check if ur engine have this function, here is from 1.4 (luascript.cpp)

C++:
int LuaScriptInterface::luaDoAreaCombat(lua_State* L)
{
    //doAreaCombat(cid, type, pos, area, min, max, effect[, origin = ORIGIN_SPELL[, blockArmor = false[, blockShield = false[, ignoreResistances = false]]]])
    Creature* creature = getCreature(L, 1);
    if (!creature && (!isNumber(L, 1) || getNumber<uint32_t>(L, 1) != 0)) {
        reportErrorFunc(L, getErrorDesc(LUA_ERROR_CREATURE_NOT_FOUND));
        pushBoolean(L, false);
        return 1;
    }

    uint32_t areaId = getNumber<uint32_t>(L, 4);
    const AreaCombat* area = g_luaEnvironment.getAreaObject(areaId);
    if (area || areaId == 0) {
        CombatType_t combatType = getNumber<CombatType_t>(L, 2);

        CombatParams params;
        params.combatType = combatType;
        params.impactEffect = getNumber<uint8_t>(L, 7);
        params.blockedByArmor = getBoolean(L, 8, false);
        params.blockedByShield = getBoolean(L, 9, false);
        params.ignoreResistances = getBoolean(L, 10, false);

        CombatDamage damage;
        damage.origin = getNumber<CombatOrigin>(L, 8, ORIGIN_SPELL);
        damage.primary.type = combatType;
        damage.primary.value = normal_random(getNumber<int32_t>(L, 6), getNumber<int32_t>(L, 5));

        Combat::doAreaCombat(creature, getPosition(L, 3), area, damage, params);
        pushBoolean(L, true);
    } else {
        reportErrorFunc(L, getErrorDesc(LUA_ERROR_AREA_NOT_FOUND));
        pushBoolean(L, false);
    }
    return 1;
}
 
Check if ur engine have this function, here is from 1.4 (luascript.cpp)

C++:
int LuaScriptInterface::luaDoAreaCombat(lua_State* L)
{
    //doAreaCombat(cid, type, pos, area, min, max, effect[, origin = ORIGIN_SPELL[, blockArmor = false[, blockShield = false[, ignoreResistances = false]]]])
    Creature* creature = getCreature(L, 1);
    if (!creature && (!isNumber(L, 1) || getNumber<uint32_t>(L, 1) != 0)) {
        reportErrorFunc(L, getErrorDesc(LUA_ERROR_CREATURE_NOT_FOUND));
        pushBoolean(L, false);
        return 1;
    }

    uint32_t areaId = getNumber<uint32_t>(L, 4);
    const AreaCombat* area = g_luaEnvironment.getAreaObject(areaId);
    if (area || areaId == 0) {
        CombatType_t combatType = getNumber<CombatType_t>(L, 2);

        CombatParams params;
        params.combatType = combatType;
        params.impactEffect = getNumber<uint8_t>(L, 7);
        params.blockedByArmor = getBoolean(L, 8, false);
        params.blockedByShield = getBoolean(L, 9, false);
        params.ignoreResistances = getBoolean(L, 10, false);

        CombatDamage damage;
        damage.origin = getNumber<CombatOrigin>(L, 8, ORIGIN_SPELL);
        damage.primary.type = combatType;
        damage.primary.value = normal_random(getNumber<int32_t>(L, 6), getNumber<int32_t>(L, 5));

        Combat::doAreaCombat(creature, getPosition(L, 3), area, damage, params);
        pushBoolean(L, true);
    } else {
        reportErrorFunc(L, getErrorDesc(LUA_ERROR_AREA_NOT_FOUND));
        pushBoolean(L, false);
    }
    return 1;
}
TFS 1.3
Скриншот 26-08-2022 204030.jpg
I didn't find this function LuaScriptInterface::luaDoAreaCombat
C++:
//getWorldUpTime()
    lua_register(luaState, "getWorldUpTime", LuaScriptInterface::luaGetWorldUpTime);

    //createCombatArea( {area}, <optional> {extArea} )
    lua_register(luaState, "createCombatArea", LuaScriptInterface::luaCreateCombatArea);

    //doAreaCombatHealth(cid, type, pos, area, min, max, effect)
    lua_register(luaState, "doAreaCombatHealth", LuaScriptInterface::luaDoAreaCombatHealth);

    //doTargetCombatHealth(cid, target, type, min, max, effect)
    lua_register(luaState, "doTargetCombatHealth", LuaScriptInterface::luaDoTargetCombatHealth);

    //doAreaCombatMana(cid, pos, area, min, max, effect)
    lua_register(luaState, "doAreaCombatMana", LuaScriptInterface::luaDoAreaCombatMana);

    //doTargetCombatMana(cid, target, min, max, effect)
    lua_register(luaState, "doTargetCombatMana", LuaScriptInterface::luaDoTargetCombatMana);

    //doAreaCombatCondition(cid, pos, area, condition, effect)
    lua_register(luaState, "doAreaCombatCondition", LuaScriptInterface::luaDoAreaCombatCondition);

    //doTargetCombatCondition(cid, target, condition, effect)
    lua_register(luaState, "doTargetCombatCondition", LuaScriptInterface::luaDoTargetCombatCondition);

    //doAreaCombatDispel(cid, pos, area, type, effect)
    lua_register(luaState, "doAreaCombatDispel", LuaScriptInterface::luaDoAreaCombatDispel);

    //doTargetCombatDispel(cid, target, type, effect)
    lua_register(luaState, "doTargetCombatDispel", LuaScriptInterface::luaDoTargetCombatDispel);

    //doChallengeCreature(cid, target)
    lua_register(luaState, "doChallengeCreature", LuaScriptInterface::luaDoChallengeCreature);

    //addEvent(callback, delay, ...)
    lua_register(luaState, "addEvent", LuaScriptInterface::luaAddEvent);

    //stopEvent(eventid)
    lua_register(luaState, "stopEvent", LuaScriptInterface::luaStopEvent);
how do I add it?
 
Back
Top