• 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
 
Okey guys, small update. Got it to work with help from "Trollheim", so all credits to Trollheim. However, i duplicated and changed the creatures in config lua for my second script.
Also, moved from TFS 1.3 to TFS 1.4.2 (not relevant maybe)
Problem is: When executing the first script, i set the storage value in the second script, and if i add a similar third script, the storage value in that is set on first script as well. Events are registered on login and in creatureevent.xml.
I appreciate the help I've already got earlier, but seems I'm in need of more :<

Question: When using same name of functions in different scripts, does it matter if its local function or not?

First script:
Lua:
local config = {
    center = Position(558, 744, 7),
    rangeX = 5,
    rangeY = 2,
    effectY = 1,
    effectX = 4,
    --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(config.monsters, getCreatureName(k)) and k ~= currentMonster and not isSummon(k) then
            return true
        end
    end
    return false
end

local function spawnEffectOnPos(position)
    doSendMagicEffect(position, CONST_ME_HITBYFIRE)
end

local function fireAnimation()
    local howManyEffects = 25
    for i = 1, howManyEffects do
        local effectPos = Position(
            math.random(config.center.x - config.effectX, config.center.x + config.effectX),
            math.random(config.center.y - config.effectY, config.center.y + config.effectY),
            config.center.z
        )
        addEvent(spawnEffectOnPos, 25 * i, effectPos)
    end

end
function onKill(cid, target, lastHit)
    if isSummon(target) then
        return true
    end
    if not monstersAlive(config.center, config.rangeX, config.rangeY, config.monsters, target) then
        local area = Game.getSpectators(config.center, false, false, config.rangeX, config.rangeX, config.rangeY, config.rangeY)
        cid:setStorageValue(Storage.SaveFrelden, 3)
        fireAnimation()
        for i, k in ipairs(area) do
            if isPlayer(k) then
                doSendMagicEffect(getCreaturePos(k), CONST_ME_HOLYAREA)
            end
        end
        return true
    end

    return true
end

Second script ( Second Room)

Lua:
local config = {
    center = Position(558, 750, 6),
    rangeX = 5,
    rangeY = 2,
    effectY = 1,
    effectX = 4,
    monster = {"Crypt Shambler","Ghoul","Bonelord","Mummy","Skeleton Warrior",},
}

local function monstersAlive(center, rangeX, rangeY, monster, 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(config.monster, getCreatureName(k)) and k ~= currentMonster and not isSummon(k) then
            return true
        end
    end
    return false
end

local function spawnEffectOnPos(position)
    doSendMagicEffect(position, CONST_ME_HITBYFIRE)
end

local function fireAnimation()
    local howManyEffects = 25
    for i = 1, howManyEffects do
        local effectPos = Position(
            math.random(config.center.x - config.effectX, config.center.x + config.effectX),
            math.random(config.center.y - config.effectY, config.center.y + config.effectY),
            config.center.z
        )
        addEvent(spawnEffectOnPos, 25 * i, effectPos)
    end

end

function onKill(cid, target, lastHit)
    if isSummon(target) then
        return true
    end
    if not monstersAlive(config.center, config.rangeX, config.rangeY, config.monsters, target) then
        local area = Game.getSpectators(config.center, false, false, config.rangeX, config.rangeX, config.rangeY, config.rangeY)
        cid:setStorageValue(Storage.SaveFrelden, 4)
        fireAnimation()
        for i, k in ipairs(area) do
            if isPlayer(k) then
                doSendMagicEffect(getCreaturePos(k), CONST_ME_HOLYAREA)
            end
        end
        return true
    end

    return true
end
We need to know how players are accessing these area's and what the end goal of giving them storage values are.
 
We need to know how players are accessing these area's and what the end goal of giving them storage values are.
They are accessing these areas by entering quest doors. Each cleared room gives a storage value accessing the next room (next door)..
 
They are accessing these areas by entering quest doors. Each cleared room gives a storage value accessing the next room (next door)..
change the lines like this
Lua:
cid:setStorageValue(Storage.SaveFrelden, 3)
Lua:
if cid:getStorageValue(Storage.SaveFrelden) < 3 then
    cid:setStorageValue(Storage.SaveFrelden, 3)
end

This will make sure they still have to do the area's in order.. but will make sure their progress does not go backwards, if redoing an earlier part of the quest
 
I can't stand it, 94590 scripts for each room 🍺 😶‍🌫️
Go Go Go Fml GIF
 
change the lines like this
Lua:
cid:setStorageValue(Storage.SaveFrelden, 3)
Lua:
if cid:getStorageValue(Storage.SaveFrelden) < 3 then
    cid:setStorageValue(Storage.SaveFrelden, 3)
end

This will make sure they still have to do the area's in order.. but will make sure their progress does not go backwards, if redoing an earlier part of the quest
It's a good Idea, but it seems it will still trigger. I also tried to set the exact storage value ( == ), but it will still run through it all and set the last available storage value :< Maybe i neeed to add an if statement to the whole script?
I can't stand it, 94590 scripts for each room 🍺 😶‍🌫️
Go Go Go Fml GIF
I appreciate the support you have given previously in the thread. I have mentioned that i am new to scripting and trying to learn, so critic like that i really dont appreciate without proposing a better solution for me to improve. I know that using multiple scripts for this is not a good idea, i tried to add everything to the same script, but fail as im loosing overview. I need to find a better solution to set-up for this script, i know :)
 
It's a good Idea, but it seems it will still trigger. I also tried to set the exact storage value ( == ), but it will still run through it all and set the last available storage value :< Maybe i neeed to add an if statement to the whole script?

Alright new plan.

Explain you want it to work, if ideally you could snap your fingers and a script was born..
How would you want it to work, from the players perspective?

Cuz otherwise, this thread is going to get to like 4 pages with no resolution.
 
Alright new plan.

Explain you want it to work, if ideally you could snap your fingers and a script was born..
How would you want it to work, from the players perspective?

Cuz otherwise, this thread is going to get to like 4 pages with no resolution.
1. Entering the door requires a different storage value from a quest line. Entering this door gives a new storage value == 1 (Iused only in this "tower"), to be able to re-do quest if died.
2.. Entering the room by clicking a quest door and 4x minions spawns (ghoul, ghost, demon skeleton and skeleton) gives storage == 2. (This works fine) After killing all 4 mobs within that room, storage == 3.
3. Only access with storage == 3.
4. Only access with storage == 3. almost same as point 2. summon 5 x new mobs that gives storage == 4 upon kill.
5. Same feature as 2&4.
So the creaturescripts should not trigger eachother, which it is currently doing. ( I will remove pictures when a solution is found ;<)
1675072578978.png1675072775100.png


EDIT** I also noticed that when killing monsters outside this area, for instant in an troll spawn far away it will try to trigger the creaturescripts but fail. Trolls is not a part of the config table, but it shouldnt check other places in the defined area?
 
Last edited:
Your current script is a bit excessive. You are calling getSpectators to check for monsters, and then calling it again to do things if the area is clear.

I highly suggest when spawning the creatures, you register an event to them, and then check for them onDeath. This way you eliminate any need for getSpectators whatsoever. You can also register an event to the player who spawned them, and despawn the creatures if the player dies.
 
Your current script is a bit excessive. You are calling getSpectators to check for monsters, and then calling it again to do things if the area is clear.

I highly suggest when spawning the creatures, you register an event to them, and then check for them onDeath. This way you eliminate any need for getSpectators whatsoever. You can also register an event to the player who spawned them, and despawn the creatures if the player dies.

Could you give me an example? Spawning the creature, register the even and check when it dies?
I started look into it and ended up on this function;
Lua:
function Monster:onSpawn(position, startup, artificial)
(?) Am i on the right path here? :p
 
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:
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
Thanks man, this one works.
I had to change
isInRange(targetPos, leftUpperCornerPos, rightBottomCornerPos)
to
Lua:
targetPos:isInRange(leftUpperCornerPos, rightBottomCornerPos)
.

Also in line 87
player:setStorageValue(config[roomNumber].storageKey

to
Code:
player:getStorageValue(config[roomNumber].storageKey)
 
Back
Top