• 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!

TFS 1.X+ Creatureevent; Find killed creature in an area.

Klank

Althea ¤ A New World Developer
Joined
Feb 21, 2008
Messages
1,077
Reaction score
631
Location
Norway
Hello!
Im struggling on getting this code to work.. So, the start of this function is 4x different creatures(not random) summoned when performing an action. What i want this script to do, is to search an area defined in the code, to confirm that all 4 creatures are killed. That is, 1 ghoul, 1 ghost , 1 demon skeleton and 1 skeleton has to be killed to create a teleport. Can't get my head around this.

Lua:
local area = {
    centerTilePos = Position(558, 744, 7),
    tilesToLeft = 5,
    tilesToRight = 5,
    tilesToTop = 2,
    tilesToBottom = 2,
    teleportPos =  Position(554,745,7)
}
local monsterNames = {"ghoul","ghost","demon skeleton","skeleton",
}
function onKill(cid, target, lastHit)
    local targetName = string.lower(getCreatureName(target))

    if isMonster(target) and table.contains(monsterNames, targetName)  then -- checks if killed thing is monster and monster with right name
        local spectators = Game.getSpectators(area.centerTilePos, false, false, 5,5,2,2) -- find every creature in room

        for key, creature in pairs(spectators) do
            if isMonster(creature) then -- finish script
                return true

            else
                doCreateTeleport(1387, getThingPos(target), area.teleportPos)-- creating teleport at position
            end
        end
    end


    return true
end
 
I prepared script for multiple rooms :p it is working on TFS 1.4.2
Just create file myRoomQuest.lua, put code below inside and move file to /data/scripts
Then register creatureEvent scripts in player login (in config "creatureEventName")

Lua:
l
local config = {
    [1] = {
        center = Position(444, 444, 7),
        effectAnimationAmount = 25,
        rangeX = 2,
        rangeY = 2,
        storageKey = Storage.YourQuestStorage,
        storageValue = 3,
        animationEffect = CONST_ME_HITBYFIRE,
        playerEffect = CONST_ME_HOLYAREA,
        monsters = {"Ghoul","Ghost","Demon Skeleton","Skeleton",},
       
        creatureEventName = "saveFeldenRoom1" -- add this to login.lua in creaturescripts
    },
    [2] = {
        center = Position(555, 555, 7),
        effectAnimationAmount = 25,
        rangeX = 2,
        rangeY = 2,
        storageKey = Storage.YourQuestStorage,
        storageValue = 3,
        animationEffect = CONST_ME_HITBYFIRE,
        playerEffect = CONST_ME_HOLYAREA,
        monsters = {"Ghoul","Ghost","Demon Skeleton","Skeleton",},
       
        creatureEventName = "saveFeldenRoom2" -- add this to login.lua in creaturescripts
    },
}
local function isAnyMonsterInAreaAlive(center, rangeX, rangeY, monsters, currentMonster)
    local area = Game.getSpectators(center, false, false, rangeX, rangeX, rangeY, rangeY)
    for i, creature in ipairs(area) do
        if creature:isMonster() and table.contains(monsters, creature:getName()) and creature ~= currentMonster and not creature:getMaster() then
            return true
        end
    end
    return false
end
local function spawnEffectOnPos(position, effect)
    position:sendMagicEffect(effect)
end
local function fireAnimation(centerRoomPos, leftUpperCornerPos, rightBottomCornerPos, effect, effectAnimationAmount)
    for i = 1, effectAnimationAmount do
        local effectPos = Position(
            math.random(leftUpperCornerPos.x, rightBottomCornerPos.x),
            math.random(leftUpperCornerPos.y, rightBottomCornerPos.y),
            leftUpperCornerPos.z
        )
        addEvent(spawnEffectOnPos, 25 * i, effectPos, effect)
    end
end
-- loop to register many rooms that you have in config
for roomNumber = 1, #config do
--- THIS IS YOUR MAIN SCRIPT
local saveFeldenRoom = CreatureEvent(config[roomNumber].creatureEventName) -- this is new way of registring scripts without using XML - it is called revScripts.
function saveFeldenRoom.onKill(creature, target, lastHit) -- Here you define what type of script it is - here its onKill
    -- is killed target is monster and if it has master(is summon) then finish script
    if not target:isMonster() or target:getMaster() then
        return true
    end
    -- if monster you are killing has wrong name (not in config)
    if not table.contains(config[roomNumber].monsters, target:getName()) then
        return true
    end
    local targetPos = target:getPosition()
    local leftUpperCornerPos = Position(config[roomNumber].center.x - config[roomNumber].rangeX, config[roomNumber].center.y - config[roomNumber].rangeY, config[roomNumber].center.z)
    local rightBottomCornerPos = Position(config[roomNumber].center.x + config[roomNumber].rangeX, config[roomNumber].center.y + config[roomNumber].rangeY, config[roomNumber].center.z)
    -- if killed monster is not in area position then finish script
    if not isInRange(targetPos, leftUpperCornerPos, rightBottomCornerPos) then
        return true
    end
    -- function that checks if any monster in your area is alive
    if isAnyMonsterInAreaAlive(config[roomNumber].center, config[roomNumber].rangeX, config[roomNumber].rangeY, config[roomNumber].monsters, target) then
        return true
    end
   
    -- here starts script that do what you want when everything is how it should be, and in every case that script shouldn't be triggered is checked above
    fireAnimation(config[roomNumber].centerRoomPos, leftUpperCornerPos, rightBottomCornerPos, config[roomNumber].animationEffect, config[roomNumber].effectAnimationAmount)
    local area = Game.getSpectators(config[roomNumber].center, false, true, config[roomNumber].rangeX, config[roomNumber].rangeX, config[roomNumber].rangeY, config[roomNumber].rangeY)
    for i, player in ipairs(area) do
        player:getPosition():sendMagicEffect(config[roomNumber].playerEffect)
        if player:setStorageValue(config[roomNumber].storageKey) < config[roomNumber].storageValue then
            player:setStorageValue(config[roomNumber].storageKey, config[roomNumber].storageValue)
        end
    end
    return true
end
saveFeldenRoom:register() -- finally you register here your scripts
-- if you want reload this type of script you need to use "/reload scripts" talkaction
end
It also let you choose different effects and monsters for different room
I hope it is not overkill
 
Last edited:
Lua:
local config = {
    center = Position(558, 744, 7),
    rangeX = 5,
    rangeY = 2,
    teleport = {
        id = 1387,
        destination = Position(554,745,7),
    }
    monsters = {"ghoul","ghost","demon skeleton","skeleton",},
}

local function monstersAlive(center, rangeX, rangeY, monsters)
    local area = Game.getSpectators(center, false, false, rangeX, rangeX, rangeY, rangeY)
    for i, k in ipairs(area) do
        if isMonster(k) and table.find(monsters, getCreatureName(k)) then
            return true
        end
    end
    return false
end

function onKill(cid, target, lastHit)
    if monstersAlive(config.center, config.rangeX, config.rangeY, config.monsters) then
        return false
    end

    doCreateTeleport(config.teleport.id, getThingPos(target), config.teleport.destination)
    return true
end
btw. I don't know why you using compact.lua for reverse compatibility while using TFS 1.X+ (I guess you using one from newest TFS due to recognizing "Position" notation).
Try to get used to object oriented coding instead of using old functions from compact.lua. Peace.
 
Last edited:
you'd always want to return true in the onKill method, or the monster won't die.
Probably want to check if the monsters are summons as well, in case a player has some out, for w/e reason.

Might have to delay the check in area.. because the monster being killed might still be 'alive' when the check is being performed.

--
I'd probably change this to be a globalstorage and onDeath for the monsters.
Instead of counting remaining monsters.. you'd just increase the globalstorage by 1..
and when you hit threshold, spawn portal + reset global storage at some point.
 
Lua:
local config = {
    center = Position(558, 744, 7),
    rangeX = 5,
    rangeY = 2,
    teleport = {
        id = 1387,
        destination = Position(554,745,7),
    }
    monsters = {"ghoul","ghost","demon skeleton","skeleton",},
}

local function monstersAlive(center, rangeX, rangeY, monsters)
    local area = Game.getSpectators(center, false, false, rangeX, rangeX, rangeY, rangeY)
    for i, k in ipairs(area) do
        if isMonster(k) and table.find(monsters, getCreatureName(k)) then
            return true
        end
    end
    return false
end

function onKill(cid, target, lastHit)
    if monstersAlive(config.center, config.rangeX, config.rangeY, config.monsters) then
        return false
    end

    doCreateTeleport(config.teleport.id, getThingPos(target), config.teleport.destination)
    return true
end
btw. I don't know why you using compact.lua for reverse compatibility while using TFS 1.X+ (I guess you using one from newest TFS due to recognizing "Position" notation).
Try to get used to object oriented coding instead of using old functions from compact.lua. Peace.

Thanks for the reply.
I am new to scripting, and trying to learn. Im using TFS 1.3 and well, i just started the scripting from there.
However, ur code does not work. I did some minor changes due to errors;
Lua:
local config = {
    center = Position(558, 744, 7),
    rangeX = 5,
    rangeY = 2,
    teleportPos = Position(554,745,7),
    monsters = {"ghoul","ghost","demon skeleton","skeleton",},
}

function monstersAlive(center, rangeX, rangeY, monsters)
    local area = Game.getSpectators(config.center, false, false, config.rangeX, config.rangeX, config.rangeY, config.rangeY)
    for i, k in ipairs(area) do
        if isMonster(k) and table.contains(monsters, getCreatureName(k)) then
            return true
        end
    end
    return false
end

function onKill(cid, target, lastHit)
    if monstersAlive(config.center, config.rangeX, config.rangeY, config.monsters) then
        return false
    end

    doCreateTeleport(1387, getThingPos(target), area.teleportPos)

    return true
end

The "table.find" did not work, changed it to table.contains. Also some correct calling of table was fixed. However, i'm getting this; Im guessing it has something to do with the local function monstersAlive?
1674762583532.png
 
Thanks for the reply.
I am new to scripting, and trying to learn. Im using TFS 1.3 and well, i just started there scripting from there.
However, ur code does not work. I did some minor changes due to errors;
Lua:
local config = {
    center = Position(558, 744, 7),
    rangeX = 5,
    rangeY = 2,
    teleportPos = Position(554,745,7),
    monsters = {"ghoul","ghost","demon skeleton","skeleton",},
}

function monstersAlive(center, rangeX, rangeY, monsters)
    local area = Game.getSpectators(config.center, false, false, config.rangeX, config.rangeX, config.rangeY, config.rangeY)
    for i, k in ipairs(area) do
        if isMonster(k) and table.contains(monsters, getCreatureName(k)) then
            return true
        end
    end
    return false
end

function onKill(cid, target, lastHit)
    if monstersAlive(config.center, config.rangeX, config.rangeY, config.monsters) then
        return false
    end

    doCreateTeleport(1387, getThingPos(target), area.teleportPos)

    return true
end

The "table.find" did not work, changed it to table.contains. Also some correct calling of table was fixed. However, i'm getting this; Im guessing it has something to do with the local function monstersAlive?
View attachment 73010
table name changed from area to config.
so edit for config.teleportPos
 
You'll also need to change monsters to config.monsters on line 12
You don't need because it's reading passed "config.monsters" in function parameter in usage in main function. Edited main post.

you'd always want to return true in the onKill method, or the monster won't die.
Probably want to check if the monsters are summons as well, in case a player has some out, for w/e reason.

Might have to delay the check in area.. because the monster being killed might still be 'alive' when the check is being performed.

--
I'd probably change this to be a globalstorage and onDeath for the monsters.
Instead of counting remaining monsters.. you'd just increase the globalstorage by 1..
and when you hit threshold, spawn portal + reset global storage at some point.

Another option is to start addEvent in loop when entering/executing script/whatever in area to check if monsters are alive, there are many options to handle that.
onKill is not a good way for this idea, but he wanted it like that :rolleyes:
 
you'd always want to return true in the onKill method, or the monster won't die.
Probably want to check if the monsters are summons as well, in case a player has some out, for w/e reason.

Might have to delay the check in area.. because the monster being killed might still be 'alive' when the check is being performed.

--
I'd probably change this to be a globalstorage and onDeath for the monsters.
Instead of counting remaining monsters.. you'd just increase the globalstorage by 1..
and when you hit threshold, spawn portal + reset global storage at some point.
Note taken, thanks.
Thats a very good point, ill have to check summons aswell.
table name changed from area to config.
so edit for config.teleportPos
Lol, thanks..
You don't need because it's reading passed "config.monsters" in function parameter in usage in main function. Edited main post.



Another option is to start addEvent in loop when entering/executing script/whatever in area to check if monsters are alive, there are many options to handle that.
onKill is not a good way for this idea, but he wanted it like that :rolleyes:

Script now goes through, but is spawning teleport upon first creature killed. Just like my own script xd
 
You don't need because it's reading passed "config.monsters" in function parameter in usage in main function. Edited main post.



Another option is to start addEvent in loop when entering/executing script/whatever in area to check if monsters are alive, there are many options to handle that.
onKill is not a good way for this idea, but he wanted it like that :rolleyes:

Sorry yes my bad, didn't see the param :p
 
You don't need because it's reading passed "config.monsters" in function parameter in usage in main function. Edited main post.



Another option is to start addEvent in loop when entering/executing script/whatever in area to check if monsters are alive, there are many options to handle that.
onKill is not a good way for this idea, but he wanted it like that :rolleyes:

It's not that i want that, but im trying to understand functions etc, and i have not worked with onDeath yet. So i guess that would change the whole structure of the code again. Just to make u understand, this is my first creaturescript ever :p
 
If the creatures are being summoned, just register an event to them when they are spawned, and just run an onDeath script. No need to call getSpectators for this. Each onDeath decrements an integer count stored in memory as a global, and then execute whatever function when its 0 (all creatures killed).

Would be better if you used revscript, as you could store all of this in one file.

However, if it's just your first script, keep with the one you've got. But keep learning :)
 
If the creatures are being summoned, just register an event to them when they are spawned, and just run an onDeath script. No need to call getSpectators for this. Each onDeath decrements an integer count stored in memory as a global, and then execute whatever function when its 0 (all creatures killed).

Would be better if you used revscript, as you could store all of this in one file.

However, if it's just your first script, keep with the one you've got. But keep learning :)

To be honest, i dont know what RevScripts are yet. Much to learn.
I would be fine by changing it, but spent so much time on this, that without proper follow-up im guessing i would use just as much time on the onDeath function xd.
 
To be honest, i dont know what RevScripts are yet. Much to learn.
I would be fine by changing it, but spent so much time on this, that without proper follow-up im guessing i would use just as much time on the onDeath function xd.
Can you post the current script that is not working?
 
getSpectators already passes the class object through so you can just do this:
Lua:
function monstersAlive(center, rangeX, rangeY, monsters)
    local spectators = Game.getSpectators(config.center, false, false, config.rangeX, config.rangeX, config.rangeY, config.rangeY)
    for _, spectator in ipairs(spectators) do
        if spectator:isMonster() and table.contains(monsters, spectator:getName()) then
            return true
        end
    end
    return false
end
 
Can you post the current script that is not working?
This is the one that is "working", but create the teleports on first (1/4) kills
Lua:
local config = {
    center = Position(558, 744, 7),
    rangeX = 5,
    rangeY = 2,
    teleportPos = Position(554,745,7),
    monsters = {"ghoul","ghost","demon skeleton","skeleton",},
}

local function monstersAlive(center, rangeX, rangeY, monsters)
    local area = Game.getSpectators(config.center, false, false, config.rangeX, config.rangeX, config.rangeY, config.rangeY)
    for i, k in ipairs(area) do
        if isMonster(k) and table.contains(monsters, getCreatureName(k)) then
            return true
        end
    end
    return false
end


function onKill(cid, target, lastHit)
    if monstersAlive(config.center, config.rangeX, config.rangeY, config.monsters) then
        return false
    end
     doCreateTeleport(1387, Position(556,745,7), Position(558,744,7))

    return true
end

getSpectators already passes the class object through so you can just do this:
Lua:
function monstersAlive(center, rangeX, rangeY, monsters)
    local spectators = Game.getSpectators(config.center, false, false, config.rangeX, config.rangeX, config.rangeY, config.rangeY)
    for _, spectator in ipairs(spectators) do
        if spectator:isMonster() and table.contains(monsters, spectator:getName()) then
            return true
        end
    end
    return false
end
(tested this seperatly, not in code above)
spectator:isMonster() generates error:
Lua Script Error: [CreatureScript Interface]
data/creaturescripts/scripts/custom/SaveFeldenRoom1.lua:eek:nKill
data/creaturescripts/scripts/custom/SaveFeldenRoom1.lua:14: attempt to call method 'isMonster' (a nil value)
stack traceback:
[C]: in function 'isMonster'
data/creaturescripts/scripts/custom/SaveFeldenRoom1.lua:14: in function 'monstersAlive'
data/creaturescripts/scripts/custom/SaveFeldenRoom1.lua:23: in function <data/creaturescripts/scripts/custom/SaveFeldenRoom1.lua:22>
 
Change onKill to this and check what is going on
Lua:
function onKill(cid, target, lastHit)
    if not monstersAlive(config.center, config.rangeX, config.rangeY, config.monsters) then
        doCreateTeleport(1387, Position(556,745,7), Position(558,744,7))
        return true
    end

    return true
end
 
Hello!
Im struggling on getting this code to work.. So, the start of this function is 4x different creatures(not random) summoned when performing an action. What i want this script to do, is to search an area defined in the code, to confirm that all 4 creatures are killed. That is, 1 ghoul, 1 ghost , 1 demon skeleton and 1 skeleton has to be killed to create a teleport. Can't get my head around this.

Lua:
local area = {
    centerTilePos = Position(558, 744, 7),
    tilesToLeft = 5,
    tilesToRight = 5,
    tilesToTop = 2,
    tilesToBottom = 2,
    teleportPos =  Position(554,745,7)
}
local monsterNames = {"ghoul","ghost","demon skeleton","skeleton",
}
function onKill(cid, target, lastHit)
    local targetName = string.lower(getCreatureName(target))

    if isMonster(target) and table.contains(monsterNames, targetName)  then -- checks if killed thing is monster and monster with right name
        local spectators = Game.getSpectators(area.centerTilePos, false, false, 5,5,2,2) -- find every creature in room

        for key, creature in pairs(spectators) do
            if isMonster(creature) then -- finish script
                return true

            else
                doCreateTeleport(1387, getThingPos(target), area.teleportPos)-- creating teleport at position
            end
        end
    end


    return true
end
Lua:
local config = {
    topLeftCorner = {x = 994, y = 1007, z = 7},
    bottomRightCorner = {x = 997, y = 1009, z = 7},
    teleportPos =  {x = 996, y = 1010, z = 7}
}
local monsterNames = {"ghoul","ghost","demon skeleton","skeleton"}
local killedCounter = {ghoul = 0, ghost = 0, ["demon skeleton"] = 0, skeleton = 0}

local topLeft = Position(config.topLeftCorner.x, config.topLeftCorner.y, config.topLeftCorner.z)
local bottomRight = Position(config.bottomRightCorner.x, config.bottomRightCorner.y, config.bottomRightCorner.z)
local teleport = Position(config.teleportPos.x, config.teleportPos.y, config.teleportPos.z)

function onKill(cid, target, lastHit)
    local targetName = string.lower(getCreatureName(target))
    local targetPos = getThingPos(target)
    if isMonster(target) and table.contains(monsterNames, targetName) and targetPos:isInRange(topLeft, bottomRight) then
        killedCounter[targetName] = killedCounter[targetName] + 1
            if killedCounter.ghoul > 0 and killedCounter.ghost > 0 and killedCounter["demon skeleton"] > 0 and killedCounter.skeleton > 0 then
            doCreateTeleport(1387, teleport, getThingPos(cid))
        end
    end
    return true
end
 
This is script that you want. You have to check if the monster you are killing is the last monster in the area.
onKill event triggers before creature is dead, that is why you never had your area cleared
Also getCreatureName function returns creature name with capital letters, so monsters in your config also needs capital letters

Lua:
local config = {
    center = Position(558, 744, 7),
    rangeX = 5,
    rangeY = 2,
    teleportPos = Position(554,745,7),
    monsters = {"Ghoul","Ghost","Demon Skeleton","Skeleton",},
}

local function monstersAlive(center, rangeX, rangeY, monsters, currentMonsterCid)
    local currentMonster = Monster(currentMonsterCid)
    local area = Game.getSpectators(config.center, false, false, config.rangeX, config.rangeX, config.rangeY, config.rangeY)
    for i, k in ipairs(area) do
        if isMonster(k) and table.contains(monsters, getCreatureName(k)) and k ~= currentMonster  then
            return true
        end
    end
    return false
end


function onKill(cid, target, lastHit)
    if not monstersAlive(config.center, config.rangeX, config.rangeY, config.monsters, target) then
        doCreateTeleport(1387, Position(556,745,7), Position(558,744,7))
        return true
    end


    return true
end
 
Back
Top