• 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.2] Pet system

XML:
    <event type="preparedeath" name="PetDeath" script="pet_creaturescript.lua" />
    <event type="kill" name="PetKill" script="pet_creaturescript.lua" />
    <event type="think" name="PetTeleport" script="pet_creaturescript.lua" />


    <event type="login" name="PetOwnerLogin" script="pet_owner_creaturescripts.lua" />
    <event type="logout" name="PetOwnerLogout" script="pet_owner_creaturescripts.lua" />
    <event type="preparedeath" name="PetOwnerDeath" script="pet_owner_creaturescripts.lua" />
pet_onwer_creaturescripts.lua
Lua:
-- <event type="login" name="PetOwnerLogin" script="pet_owner_creaturescripts.lua" />
-- <event type="logout" name="PetOwnerLogout" script="pet_owner_creaturescripts.lua" />
-- <event type="preparedeath" name="PetOwnerDeath" script="pet_owner_creaturescripts.lua" />

function onLogin(player)
    for _, event in pairs({"PetOwnerLogout", "PetOwnerDeath"}) do
        player:registerEvent(event)
    end
    player:openChannel(PETS.CHANNELID)
    return true
end

function onLogout(player)
    return player:doRemovePet()
end

function onPrepareDeath(creature, killer)
    local petUid = creature:getPetUid()
    local pet = Creature(petUid)
    if pet and pet:isCreature() then
        creature:doKillPet(true)
    end
    return true
end
Post automatically merged:

when i type !petheal
(while pet is offline but ALIVE(!?) I got error console


Lua Script Error: [TalkAction Interface]
data/talkactions/scripts/pet_heal.lua:eek:nSay
data/talkactions/scripts/pet_heal.lua:30: attempt to index local 'pet' (a nil value)
 
Last edited:
XML:
    <event type="preparedeath" name="PetDeath" script="pet_creaturescript.lua" />
    <event type="kill" name="PetKill" script="pet_creaturescript.lua" />
    <event type="think" name="PetTeleport" script="pet_creaturescript.lua" />


    <event type="login" name="PetOwnerLogin" script="pet_owner_creaturescripts.lua" />
    <event type="logout" name="PetOwnerLogout" script="pet_owner_creaturescripts.lua" />
    <event type="preparedeath" name="PetOwnerDeath" script="pet_owner_creaturescripts.lua" />
pet_onwer_creaturescripts.lua
Lua:
-- <event type="login" name="PetOwnerLogin" script="pet_owner_creaturescripts.lua" />
-- <event type="logout" name="PetOwnerLogout" script="pet_owner_creaturescripts.lua" />
-- <event type="preparedeath" name="PetOwnerDeath" script="pet_owner_creaturescripts.lua" />

function onLogin(player)
    for _, event in pairs({"PetOwnerLogout", "PetOwnerDeath"}) do
        player:registerEvent(event)
    end
    player:openChannel(PETS.CHANNELID)
    return true
end

function onLogout(player)
    return player:doRemovePet()
end

function onPrepareDeath(creature, killer)
    local petUid = creature:getPetUid()
    local pet = Creature(petUid)
    if pet and pet:isCreature() then
        creature:doKillPet(true)
    end
    return true
end
Post automatically merged:

when i type !petheal
(while pet is offline but ALIVE(!?) I got error console


Lua Script Error: [TalkAction Interface]
data/talkactions/scripts/pet_heal.lua:eek:nSay
data/talkactions/scripts/pet_heal.lua:30: attempt to index local 'pet' (a nil value)

I'll take a look at this deeply in saturday.
I don't have any idea now.

You can check if both scripts run. Change pet_owner_creaturescripts.lua
Lua:
-- <event type="login" name="PetOwnerLogin" script="pet_owner_creaturescripts.lua" />
-- <event type="logout" name="PetOwnerLogout" script="pet_owner_creaturescripts.lua" />
-- <event type="preparedeath" name="PetOwnerDeath" script="pet_owner_creaturescripts.lua" />

function onLogin(player)
    for _, event in pairs({"PetOwnerLogout", "PetOwnerDeath"}) do
        player:registerEvent(event)
        print('PET-LOGIN register event')
    end
    player:openChannel(PETS.CHANNELID)
    return true
end

function onLogout(player)
    print('PET-LOGOUT')
    return player:doRemovePet()
end

function onPrepareDeath(creature, killer)
    local petUid = creature:getPetUid()
    local pet = Creature(petUid)
    if pet and pet:isCreature() then
        creature:doKillPet(true)
         print('PET-PREPAREDEATH')
    end
    return true
end

Reaload scripts and show me console output, when You log in and log out.
 
I think its not for me..
i caught a Pet_wolf, and rejoin chat, and that wolf was on map for 0.1 second and dissapear, now i can't summon him back, 0 consol log (i did new character same bug)

Boniek Rashuje has logged in.
PET-LOGIN register event
PET-LOGIN register event
Klepie Starem has logged in.
PET-LOGIN register event
PET-LOGIN register event
PET-LOGOUT
Klepie Starem has logged out.
Klepie Starem has logged in.
PET-LOGIN register event
PET-LOGIN register event
 
I think its not for me..
i caught a Pet_wolf, and rejoin chat, and that wolf was on map for 0.1 second and dissapear, now i can't summon him back, 0 consol log (i did new character same bug)

Boniek Rashuje has logged in.
PET-LOGIN register event
PET-LOGIN register event
Klepie Starem has logged in.
PET-LOGIN register event
PET-LOGIN register event
PET-LOGOUT
Klepie Starem has logged out.
Klepie Starem has logged in.
PET-LOGIN register event
PET-LOGIN register event

Show me player storages:
from 10000 to 10005

Write Private Message.
 
  • TFS 1.2



  • data\lib\pets_lib.lua
Lua:
---- based on Jordanhenry pet system version 1.77 (changelog)
---- edited by hellboy

-- config
PETS = {
    PREFIX = "PET_",
    CHANNELID = 9,

    CONFIG = {
        introduction = "You may catch pets using command '!petcatch'. If your pet dies, you have to revive it in order to re-summon it. Some pets have a special requirement in order to catch them, some cannot be catched at all and can only be gotten by evolution. Type 'commands' for a list of available commands.",
        sameSpeed = true,

        healSoulCost = 0.1,
        healSoulBase = 10,

        healOnLevelUp = true,
        standardHpAdd = 5,
        expMultipler = 1,
        shareExpMultipler = 0.3,
        maxLevel = 30,

        reviveSoulBaseCost = 50,
        reviveSoulLevelCost = 0.2
    },

    SYSTEM = {
        EVOLUTION = true,
        MOUNTS = false,
        TELEPORT = true,
        PLAYER_SHARE_EXPERIENCE = false,
    DUELS_ONLY = false
    },

    IDENTIFICATION = {
        [1] = {
            name = "Cat",
            health = 100,
            evolve = {
                to = 3,
                at = 10
            },
            check = true
        },
        [2] = {
            name = "Dog",
            health = 100,
            evolve = {
                to = 4,
                at = 10
            },
            check = true
        },
        [3] = {
            name = "Tiger",
            health = 300,
            check = false,
            info = "Evolves from Cat."
        },
        [4] = {
            name = "Lion",
            health = 300,
            mountId = 40,
            check = false,
            info = "Evolves from Dog."
        },
        [5] = {
            name = "Husky",
            health = 150,
            check = function(player) return player:getPremiumDays() > 0 end,
            info = "Requires a premium account."
        },
        [6] = {
            name = "Wolf",
            health = 200,
            evolve = {
                to = 7,
                at = 4
            },
            check = function(player) return player:getLevel() >= 10 end,
            info = "Requires level 10."
        },
        [7] = {
            name = "War Wolf",
            health = 500,
            evolve = {
                to = 8,
                at = 55
            },
            check = false,
            info = "Evolves from Wolf."
        },
        [8] = {
            name = "Werewolf",
            health = 1000,
            check = false,
            info = "Evolves from War Wolf."
        },
        [9] = {
            name = "Bear",
            health = 300,
            mountId = 3,
            check = function(player) return player:isDruid() and player:getLevel() >= 10 end,
            info = "Only available to druids above level 10."
        },
        [10] = {
            name = "Panda",
            health = 300,
            mountId = 19,
            check = function(player) return player:isDruid() and player:getLevel() >= 10 end,
            info = "Only available to druids above level 10."
        },
        [11] = {
            name = "Chicken",
            health = 50,
            check = true
        },
        [12] = {
            name = "Sheep",
            health = 50,
            check = true
        },
        [13] = {
            name = "Seagull",
            health = 100,
            check = function(player) return player:getPremiumDays() > 0 end,
            info = "Requires a premium account."
        },
        [14] = {
            name = "Parrot",
            health = 100,
            check = function(player) return player:getPremiumDays() > 0 end,
            info = "Requires a premium account."
        },
        [15] = {
            name = "Penguin",
            health = 100,
            check = function(player) return player:getPremiumDays() > 0 end,
            info = "Requires a premium account."
        },
        [16] = {
            name = "Elephant",
            health = 300,
            check = function(player) return player:getPremiumDays() > 0 and player:getLevel() >= 10 end,
            contain = 5,
            info = "Only available to Premium accounts above level 10."
        },
        [17] = {
            name = "Dragon Hatchling",
            health = 300,
            evolve = {
                to = 18,
                at = 20
            },
            check = function(player) return player:getPremiumDays() > 0 and player:getLevel() >= 25 and player:isSorcerer() end,
            info = "Only available to Premium Sorcerers above level 25."
        },
        [18] = {
            name = "Dragon",
            health = 1000,
            check = false,
            info = "Evolves from Dragon Hatchling."
        }
    },

    STORAGE = {
        TYPE = 10000,
        UID = 10001,
        LOSTHEALTH = 10002,
        MAXHEALTH = 10003,
        EXPERIENCE = 10004,
        LEVEL = 10005
    },

    CONSTANS = {
        STATUS_OK = 0,
        STATUS_DOESNT_EXIST = -1,
        STATUS_DEAD = -2,
        STATUS_MOUNT = -3
    }
}

--/ config
function Player.petSystemMessage(self, txt, talkType)
    local playerId = self:getId()
    talkType = (talkType == nil) and TALKTYPE_CHANNEL_O or talkType

    local function eventMessage(playerId, text, talkType)
        local player = Player(playerId)
        if not player then
            return false
        end
        player:sendChannelMessage('[PET-SYSTEM]', txt, talkType, PETS.CHANNELID)
    end
    addEvent(eventMessage, 150, playerId, text, talkType)

    --self:sendChannelMessage('[PET-SYSTEM]', txt, ((talkType == nil) and TALKTYPE_CHANNEL_O or talkType), PETS.CHANNELID)
    --self:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, '[PET-SYSTEM] '..txt)
    return true
end

-- get
function Player.getPetExperience(self)
    return self:getStorageValue(PETS.STORAGE.EXPERIENCE)
end

function Player.getPetLevel(self)
    return self:getStorageValue(PETS.STORAGE.LEVEL)
end

function Player.getPetType(self)
    return self:getStorageValue(PETS.STORAGE.TYPE)
end

function Player.getPetUid(self)
    return self:getStorageValue(PETS.STORAGE.UID)
end

function Player.getPetMaxHealth(self)
    return self:getStorageValue(PETS.STORAGE.MAXHEALTH)
end

function Player.getPetLostHealth(self)
    return self:getStorageValue(PETS.STORAGE.LOSTHEALTH)
end

function Player.getPetMountId(self)
    local petType = self:getPetType()
    local mountId = PETS.IDENTIFICATION[petType].mountId
    return mountId
end

-- set
function Player.setPetExperience(self, experience)
    return self:setStorageValue(PETS.STORAGE.EXPERIENCE, experience)
end

function Player.setPetLevel(self, petLevel)
    return self:setStorageValue(PETS.STORAGE.LEVEL, petLevel)
end

function Player.setPetType(self, petType)
    return self:setStorageValue(PETS.STORAGE.TYPE, petType)
end

function Player.setPetUid(self, petUid)
    return self:setStorageValue(PETS.STORAGE.UID, petUid)
end

function Player.setPetMaxHealth(self, health)
    return self:setStorageValue(PETS.STORAGE.MAXHEALTH, health)
end

function Player.setPetLostHealth(self, health)
    return self:setStorageValue(PETS.STORAGE.LOSTHEALTH, health)
end

-- other
function Player.doAddPet(self, petType)
    local pet = Creature(self:getStorageValue(PETS.STORAGE.UID))
    if pet then
        return false
    end

    self:setPetUid(PETS.CONSTANS.STATUS_OK)
    self:setPetExperience(0)
    self:setPetLevel(1)
    self:setPetType(petType)

    self:setPetMaxHealth(PETS.IDENTIFICATION[petType].health)
    self:setPetLostHealth(0)

    if PETS.SYSTEM.MOUNTS then
        local mountId = self:getPetMountId()
        if mountId ~= nil and mountId ~= 0 then
            self:addMount(mountId)
        end
    end
    return true
end

function Player.doResetPet(self)
    for _, i in pairs(PETS.STORAGE) do
        self:setStorageValue(i, -1)
    end
    return true
end

function Player.doRemovePet(self)
    local petUid = self:getPetUid()
    local pet = Creature(petUid)

    if not pet or not pet:isCreature() then
        if petUid > 0 then
            self:setPetUid(PETS.CONSTANS.STATUS_OK)
        end
        return true
    end
    local maxHealth = pet:getMaxHealth()

    self:setPetMaxHealth(maxHealth)
    self:setPetLostHealth(maxHealth - pet:getHealth() )

    pet:remove()

    if PETS.SYSTEM.MOUNTS then
        local mountId = self:getPetMountId()
        if mountId ~= nil then
            self:addMount(mountId)
        end
    end
    return true
end

function Player.doKillPet(self, removeBody)
    if removeBody then
        self:doRemovePet()
    end
    self:setPetUid(PETS.CONSTANS.STATUS_DEAD)
    self:setPetLostHealth(0)

    if PETS.SYSTEM.MOUNTS then
        local mountId = self:getPetMountId()
        if mountId ~= nil then
            self:removeMount(mountId)
        end
    end
    return true
end

function Player.summonPet(self, position)
    local petUid = self:getPetUid()
    local pet = Creature(petUid)
    if pet and pet:isCreature() then
        return false
    end

    if (Tile(position)):hasFlag(TILESTATE_PROTECTIONZONE) then
      return false
    end

    if PETS.SYSTEM.MOUNTS then
        local mountId = self:getPetMountId()
        local currentMount = self:getOutfit()['lookMount']
        if mountId ~= nil and currentMount ~= nil and currentMount ~= 0 and currentMount == mountId then
            return false
        end
    end

    local pet = Game.createMonster(PETS.PREFIX .. (PETS.IDENTIFICATION[self:getPetType()].name), position)
    if pet then
        position:sendMagicEffect(CONST_ME_TELEPORT)
        pet:setMaster(self)
        local maxHealth = self:getPetMaxHealth()
        pet:setMaxHealth(maxHealth)
        pet:addHealth(maxHealth - pet:getHealth() - self:getPetLostHealth() )
        self:setPetUid( pet:getId() )
        pet:setSkull(SKULL_GREEN)
        pet:changeSpeed(PETS.CONFIG.sameSpeed and (self:getBaseSpeed() - pet:getBaseSpeed()) or 0)

        for _, eventName in pairs({"PetDeath", "PetKill"}) do
            pet:registerEvent(eventName)
        end

        if PETS.SYSTEM.TELEPORT then
            pet:registerEvent("PetTeleport")
        end

        if PETS.SYSTEM.DUELS_ONLY then
            pet:registerEvent("PetHealthChange")
        end

        if PETS.SYSTEM.MOUNTS then
            local mountId = self:getPetMountId()
            if mountId ~= nil then
                self:removeMount(mountId)
            end
        end

        return pet
    end

    local petMonsterType = MonsterType(PETS.PREFIX .. (PETS.IDENTIFICATION[self:getPetType()].name))
    if not petMonsterType then
        print('[PET-SYSTEM] Cant find monster type: ' .. PETS.PREFIX .. (PETS.IDENTIFICATION[self:getPetType()].name) )
    end
    return false
end

function getExpNeeded(level)
    return ( (50 *level^3) -(150 *level^2) +(400 *level) )/3 *PETS.CONFIG.expMultipler
end

function Player.addPetExp(self, amount)
    local pet = Creature(self:getPetUid())
    if not pet then
        return false
    end

    if self:getPetLevel() >= PETS.CONFIG.maxLevel then
        return false
    end

    local totalExp = self:getPetExperience() + amount
    self:setPetExperience(totalExp)
    local petLevel, petType, petUid = self:getPetLevel(), self:getPetType(), self:getPetUid()

    if totalExp >= getExpNeeded(petLevel + 1) then
        pet:setMaxHealth(pet:getMaxHealth() + (PETS.IDENTIFICATION[petType].hpAdd or PETS.CONFIG.standardHpAdd))
        self:setPetLevel(petLevel +1)
        self:petSystemMessage("Your pet "..PETS.IDENTIFICATION[petType].name.." has advanced to level "..(petLevel +1)..".")

        if PETS.CONFIG.healOnLevelUp then
            pet:addHealth( pet:getMaxHealth() )
        end

        if PETS.SYSTEM.EVOLUTION and (PETS.IDENTIFICATION[petType]).evolve and ((PETS.IDENTIFICATION[petType]).evolve.at <= (petLevel +1)) then
            local position = pet:getPosition()
            self:doRemovePet()
            self:setPetType( (PETS.IDENTIFICATION[petType]).evolve.to)
            self:setPetMaxHealth( (PETS.IDENTIFICATION[(PETS.IDENTIFICATION[petType]).evolve.to]).health )
            self:setPetLostHealth(0)
            self:petSystemMessage("Your pet "..(PETS.IDENTIFICATION[petType]).name.." has evolved to a "..((PETS.IDENTIFICATION[(PETS.IDENTIFICATION[petType]).evolve.to]).name)..".")
            self:summonPet(position)
        end

        -- save max hp fix
        self:setPetMaxHealth(pet:getMaxHealth())
    end

    return true
end

function Player.canGetPet(self, petId)
    if self:getGroup():getId() >= 3 then
        return true
    end

    if type(PETS.IDENTIFICATION[petId].check) == "function" then
        return PETS.IDENTIFICATION[petId].check(self)
    end
    return PETS.IDENTIFICATION[petId].check
end

-- is Pet
function Player.isPet(self)
    return false
end

function Npc.isPet(self)
    return false
end

function Monster.isPet(self)
    local owner = self:getMaster()
    if owner:isPlayer() and owner:getPetUid() == self:getId() then
        return true
    end
    return false
end

-- additional functions
function Position.getSurroundings(self)
  local return_array = {}
  local coordinates = {
    {x = self.x +1, y = self.y, z = self.z},
    {x = self.x -1, y = self.y, z = self.z},
    {x = self.x +1, y = self.y +1, z = self.z},
    {x = self.x, y = self.y +1, z = self.z},
    {x = self.x -1, y = self.y +1, z = self.z},
    {x = self.x +1, y = self.y -1, z = self.z},
    {x = self.x, y = self.y -1, z = self.z},
    {x = self.x -1, y = self.y -1, z = self.z}
  }

  for _, coordinate in pairs(coordinates) do
    table.insert(return_array, Position(coordinate))
  end
  return return_array
end

-- PET tools
function Monster.dig(self)
  if not self:isPet() then
    return false
  end

  local return_value = false

  local position = self:getPosition()
  local surroundings = position:getSurroundings()
  local HOLE_LIST = {468, 481, 483, 7932}

  local function _tmp_dig_in_tile(creature, tile, position)
    if not tile then
      return false
    end

    if tile:getCreatureCount() ~= 0 then
      return false
    end

    if tile:getItemCount() ~= 0 then
      return false
    end

    local ground = tile:getGround()

    if not ground then
      return false
    end

    local groundId = ground:getId()

    if not isInArray(HOLE_LIST, groundId) then
      return false
    end

    ground:transform(groundId + 1)
    ground:decay()
    position:sendMagicEffect(CONST_ME_POFF)
    return true
  end

  for _, next_position in pairs(surroundings) do
    local next_tile = Tile(next_position)

    if _tmp_dig_in_tile(self, next_tile, next_position) then
      return_value = true
    end
  end

  return return_value
end

  • data\chatchannels\chatchannels.lua
Lua:
<?xml version="1.0" encoding="UTF-8"?>
<channels>
    <channel id="2" name="Rule Violations" script="ruleviolations.lua" />
    <channel id="3" name="Tutor" script="tutor.lua" />
    <channel id="4" name="Game Chat" public="1" script="worldchat.lua" />
    <channel id="5" name="Real Chat" public="1" script="englishchat.lua" />
    <channel id="6" name="Trade" public="1" script="trade.lua" />
    <channel id="7" name="Help" public="1" script="help.lua" />
    <channel id="8" name="Gamemaster" script="gamemaster.lua" />
    <channel id="9" name="Pet" script="pet.lua" />   
</channels>



When I login to the char or try to open the PET channel, this error happens in the log:

Lua:
Lua Script Error: [Chat Interface]
data/chatchannels/scripts/pet.lua:onJoin
data/lib/pets_lib.lua:349: attempt to index a nil value
stack traceback:
        [C]: in function '__index'
        data/lib/pets_lib.lua:349: in function 'summonPet'
        data/chatchannels/scripts/pet.lua:6: in function <data/chatchannels/scripts/pet.lua:2>
        [C]: in function 'openChannel'
        data/creaturescripts/scripts/pet_owner_creaturescripts.lua:9: in function <data/creaturescripts/scripts/pet_owner_creaturescripts.lua:5>

Lua Script Error: [Chat Interface]
data/chatchannels/scripts/pet.lua:onJoin
data/lib/pets_lib.lua:349: attempt to index a nil value
stack traceback:
        [C]: in function '__index'
        data/lib/pets_lib.lua:349: in function 'summonPet'
        data/chatchannels/scripts/pet.lua:6: in function <data/chatchannels/scripts/pet.lua:2>

Lua Script Error: [Main Interface]
in a timer event called from:
(Unknown scriptfile)
data/lib/pets_lib.lua:192: attempt to call method 'sendChannelMessage' (a nil value)
stack traceback:
        [C]: in function 'sendChannelMessage'
        data/lib/pets_lib.lua:192: in function <data/lib/pets_lib.lua:187>

Lua Script Error: [Main Interface]
in a timer event called from:
(Unknown scriptfile)
data/lib/pets_lib.lua:192: attempt to call method 'sendChannelMessage' (a nil value)
stack traceback:
        [C]: in function 'sendChannelMessage'
        data/lib/pets_lib.lua:192: in function <data/lib/pets_lib.lua:187>

Can you help me?? Tenkiu!
 
  • TFS 1.2



  • data\lib\pets_lib.lua
Lua:
---- based on Jordanhenry pet system version 1.77 (changelog)
---- edited by hellboy

-- config
PETS = {
    PREFIX = "PET_",
    CHANNELID = 9,

    CONFIG = {
        introduction = "You may catch pets using command '!petcatch'. If your pet dies, you have to revive it in order to re-summon it. Some pets have a special requirement in order to catch them, some cannot be catched at all and can only be gotten by evolution. Type 'commands' for a list of available commands.",
        sameSpeed = true,

        healSoulCost = 0.1,
        healSoulBase = 10,

        healOnLevelUp = true,
        standardHpAdd = 5,
        expMultipler = 1,
        shareExpMultipler = 0.3,
        maxLevel = 30,

        reviveSoulBaseCost = 50,
        reviveSoulLevelCost = 0.2
    },

    SYSTEM = {
        EVOLUTION = true,
        MOUNTS = false,
        TELEPORT = true,
        PLAYER_SHARE_EXPERIENCE = false,
    DUELS_ONLY = false
    },

    IDENTIFICATION = {
        [1] = {
            name = "Cat",
            health = 100,
            evolve = {
                to = 3,
                at = 10
            },
            check = true
        },
        [2] = {
            name = "Dog",
            health = 100,
            evolve = {
                to = 4,
                at = 10
            },
            check = true
        },
        [3] = {
            name = "Tiger",
            health = 300,
            check = false,
            info = "Evolves from Cat."
        },
        [4] = {
            name = "Lion",
            health = 300,
            mountId = 40,
            check = false,
            info = "Evolves from Dog."
        },
        [5] = {
            name = "Husky",
            health = 150,
            check = function(player) return player:getPremiumDays() > 0 end,
            info = "Requires a premium account."
        },
        [6] = {
            name = "Wolf",
            health = 200,
            evolve = {
                to = 7,
                at = 4
            },
            check = function(player) return player:getLevel() >= 10 end,
            info = "Requires level 10."
        },
        [7] = {
            name = "War Wolf",
            health = 500,
            evolve = {
                to = 8,
                at = 55
            },
            check = false,
            info = "Evolves from Wolf."
        },
        [8] = {
            name = "Werewolf",
            health = 1000,
            check = false,
            info = "Evolves from War Wolf."
        },
        [9] = {
            name = "Bear",
            health = 300,
            mountId = 3,
            check = function(player) return player:isDruid() and player:getLevel() >= 10 end,
            info = "Only available to druids above level 10."
        },
        [10] = {
            name = "Panda",
            health = 300,
            mountId = 19,
            check = function(player) return player:isDruid() and player:getLevel() >= 10 end,
            info = "Only available to druids above level 10."
        },
        [11] = {
            name = "Chicken",
            health = 50,
            check = true
        },
        [12] = {
            name = "Sheep",
            health = 50,
            check = true
        },
        [13] = {
            name = "Seagull",
            health = 100,
            check = function(player) return player:getPremiumDays() > 0 end,
            info = "Requires a premium account."
        },
        [14] = {
            name = "Parrot",
            health = 100,
            check = function(player) return player:getPremiumDays() > 0 end,
            info = "Requires a premium account."
        },
        [15] = {
            name = "Penguin",
            health = 100,
            check = function(player) return player:getPremiumDays() > 0 end,
            info = "Requires a premium account."
        },
        [16] = {
            name = "Elephant",
            health = 300,
            check = function(player) return player:getPremiumDays() > 0 and player:getLevel() >= 10 end,
            contain = 5,
            info = "Only available to Premium accounts above level 10."
        },
        [17] = {
            name = "Dragon Hatchling",
            health = 300,
            evolve = {
                to = 18,
                at = 20
            },
            check = function(player) return player:getPremiumDays() > 0 and player:getLevel() >= 25 and player:isSorcerer() end,
            info = "Only available to Premium Sorcerers above level 25."
        },
        [18] = {
            name = "Dragon",
            health = 1000,
            check = false,
            info = "Evolves from Dragon Hatchling."
        }
    },

    STORAGE = {
        TYPE = 10000,
        UID = 10001,
        LOSTHEALTH = 10002,
        MAXHEALTH = 10003,
        EXPERIENCE = 10004,
        LEVEL = 10005
    },

    CONSTANS = {
        STATUS_OK = 0,
        STATUS_DOESNT_EXIST = -1,
        STATUS_DEAD = -2,
        STATUS_MOUNT = -3
    }
}

--/ config
function Player.petSystemMessage(self, txt, talkType)
    local playerId = self:getId()
    talkType = (talkType == nil) and TALKTYPE_CHANNEL_O or talkType

    local function eventMessage(playerId, text, talkType)
        local player = Player(playerId)
        if not player then
            return false
        end
        player:sendChannelMessage('[PET-SYSTEM]', txt, talkType, PETS.CHANNELID)
    end
    addEvent(eventMessage, 150, playerId, text, talkType)

    --self:sendChannelMessage('[PET-SYSTEM]', txt, ((talkType == nil) and TALKTYPE_CHANNEL_O or talkType), PETS.CHANNELID)
    --self:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, '[PET-SYSTEM] '..txt)
    return true
end

-- get
function Player.getPetExperience(self)
    return self:getStorageValue(PETS.STORAGE.EXPERIENCE)
end

function Player.getPetLevel(self)
    return self:getStorageValue(PETS.STORAGE.LEVEL)
end

function Player.getPetType(self)
    return self:getStorageValue(PETS.STORAGE.TYPE)
end

function Player.getPetUid(self)
    return self:getStorageValue(PETS.STORAGE.UID)
end

function Player.getPetMaxHealth(self)
    return self:getStorageValue(PETS.STORAGE.MAXHEALTH)
end

function Player.getPetLostHealth(self)
    return self:getStorageValue(PETS.STORAGE.LOSTHEALTH)
end

function Player.getPetMountId(self)
    local petType = self:getPetType()
    local mountId = PETS.IDENTIFICATION[petType].mountId
    return mountId
end

-- set
function Player.setPetExperience(self, experience)
    return self:setStorageValue(PETS.STORAGE.EXPERIENCE, experience)
end

function Player.setPetLevel(self, petLevel)
    return self:setStorageValue(PETS.STORAGE.LEVEL, petLevel)
end

function Player.setPetType(self, petType)
    return self:setStorageValue(PETS.STORAGE.TYPE, petType)
end

function Player.setPetUid(self, petUid)
    return self:setStorageValue(PETS.STORAGE.UID, petUid)
end

function Player.setPetMaxHealth(self, health)
    return self:setStorageValue(PETS.STORAGE.MAXHEALTH, health)
end

function Player.setPetLostHealth(self, health)
    return self:setStorageValue(PETS.STORAGE.LOSTHEALTH, health)
end

-- other
function Player.doAddPet(self, petType)
    local pet = Creature(self:getStorageValue(PETS.STORAGE.UID))
    if pet then
        return false
    end

    self:setPetUid(PETS.CONSTANS.STATUS_OK)
    self:setPetExperience(0)
    self:setPetLevel(1)
    self:setPetType(petType)

    self:setPetMaxHealth(PETS.IDENTIFICATION[petType].health)
    self:setPetLostHealth(0)

    if PETS.SYSTEM.MOUNTS then
        local mountId = self:getPetMountId()
        if mountId ~= nil and mountId ~= 0 then
            self:addMount(mountId)
        end
    end
    return true
end

function Player.doResetPet(self)
    for _, i in pairs(PETS.STORAGE) do
        self:setStorageValue(i, -1)
    end
    return true
end

function Player.doRemovePet(self)
    local petUid = self:getPetUid()
    local pet = Creature(petUid)

    if not pet or not pet:isCreature() then
        if petUid > 0 then
            self:setPetUid(PETS.CONSTANS.STATUS_OK)
        end
        return true
    end
    local maxHealth = pet:getMaxHealth()

    self:setPetMaxHealth(maxHealth)
    self:setPetLostHealth(maxHealth - pet:getHealth() )

    pet:remove()

    if PETS.SYSTEM.MOUNTS then
        local mountId = self:getPetMountId()
        if mountId ~= nil then
            self:addMount(mountId)
        end
    end
    return true
end

function Player.doKillPet(self, removeBody)
    if removeBody then
        self:doRemovePet()
    end
    self:setPetUid(PETS.CONSTANS.STATUS_DEAD)
    self:setPetLostHealth(0)

    if PETS.SYSTEM.MOUNTS then
        local mountId = self:getPetMountId()
        if mountId ~= nil then
            self:removeMount(mountId)
        end
    end
    return true
end

function Player.summonPet(self, position)
    local petUid = self:getPetUid()
    local pet = Creature(petUid)
    if pet and pet:isCreature() then
        return false
    end

    if (Tile(position)):hasFlag(TILESTATE_PROTECTIONZONE) then
      return false
    end

    if PETS.SYSTEM.MOUNTS then
        local mountId = self:getPetMountId()
        local currentMount = self:getOutfit()['lookMount']
        if mountId ~= nil and currentMount ~= nil and currentMount ~= 0 and currentMount == mountId then
            return false
        end
    end

    local pet = Game.createMonster(PETS.PREFIX .. (PETS.IDENTIFICATION[self:getPetType()].name), position)
    if pet then
        position:sendMagicEffect(CONST_ME_TELEPORT)
        pet:setMaster(self)
        local maxHealth = self:getPetMaxHealth()
        pet:setMaxHealth(maxHealth)
        pet:addHealth(maxHealth - pet:getHealth() - self:getPetLostHealth() )
        self:setPetUid( pet:getId() )
        pet:setSkull(SKULL_GREEN)
        pet:changeSpeed(PETS.CONFIG.sameSpeed and (self:getBaseSpeed() - pet:getBaseSpeed()) or 0)

        for _, eventName in pairs({"PetDeath", "PetKill"}) do
            pet:registerEvent(eventName)
        end

        if PETS.SYSTEM.TELEPORT then
            pet:registerEvent("PetTeleport")
        end

        if PETS.SYSTEM.DUELS_ONLY then
            pet:registerEvent("PetHealthChange")
        end

        if PETS.SYSTEM.MOUNTS then
            local mountId = self:getPetMountId()
            if mountId ~= nil then
                self:removeMount(mountId)
            end
        end

        return pet
    end

    local petMonsterType = MonsterType(PETS.PREFIX .. (PETS.IDENTIFICATION[self:getPetType()].name))
    if not petMonsterType then
        print('[PET-SYSTEM] Cant find monster type: ' .. PETS.PREFIX .. (PETS.IDENTIFICATION[self:getPetType()].name) )
    end
    return false
end

function getExpNeeded(level)
    return ( (50 *level^3) -(150 *level^2) +(400 *level) )/3 *PETS.CONFIG.expMultipler
end

function Player.addPetExp(self, amount)
    local pet = Creature(self:getPetUid())
    if not pet then
        return false
    end

    if self:getPetLevel() >= PETS.CONFIG.maxLevel then
        return false
    end

    local totalExp = self:getPetExperience() + amount
    self:setPetExperience(totalExp)
    local petLevel, petType, petUid = self:getPetLevel(), self:getPetType(), self:getPetUid()

    if totalExp >= getExpNeeded(petLevel + 1) then
        pet:setMaxHealth(pet:getMaxHealth() + (PETS.IDENTIFICATION[petType].hpAdd or PETS.CONFIG.standardHpAdd))
        self:setPetLevel(petLevel +1)
        self:petSystemMessage("Your pet "..PETS.IDENTIFICATION[petType].name.." has advanced to level "..(petLevel +1)..".")

        if PETS.CONFIG.healOnLevelUp then
            pet:addHealth( pet:getMaxHealth() )
        end

        if PETS.SYSTEM.EVOLUTION and (PETS.IDENTIFICATION[petType]).evolve and ((PETS.IDENTIFICATION[petType]).evolve.at <= (petLevel +1)) then
            local position = pet:getPosition()
            self:doRemovePet()
            self:setPetType( (PETS.IDENTIFICATION[petType]).evolve.to)
            self:setPetMaxHealth( (PETS.IDENTIFICATION[(PETS.IDENTIFICATION[petType]).evolve.to]).health )
            self:setPetLostHealth(0)
            self:petSystemMessage("Your pet "..(PETS.IDENTIFICATION[petType]).name.." has evolved to a "..((PETS.IDENTIFICATION[(PETS.IDENTIFICATION[petType]).evolve.to]).name)..".")
            self:summonPet(position)
        end

        -- save max hp fix
        self:setPetMaxHealth(pet:getMaxHealth())
    end

    return true
end

function Player.canGetPet(self, petId)
    if self:getGroup():getId() >= 3 then
        return true
    end

    if type(PETS.IDENTIFICATION[petId].check) == "function" then
        return PETS.IDENTIFICATION[petId].check(self)
    end
    return PETS.IDENTIFICATION[petId].check
end

-- is Pet
function Player.isPet(self)
    return false
end

function Npc.isPet(self)
    return false
end

function Monster.isPet(self)
    local owner = self:getMaster()
    if owner:isPlayer() and owner:getPetUid() == self:getId() then
        return true
    end
    return false
end

-- additional functions
function Position.getSurroundings(self)
  local return_array = {}
  local coordinates = {
    {x = self.x +1, y = self.y, z = self.z},
    {x = self.x -1, y = self.y, z = self.z},
    {x = self.x +1, y = self.y +1, z = self.z},
    {x = self.x, y = self.y +1, z = self.z},
    {x = self.x -1, y = self.y +1, z = self.z},
    {x = self.x +1, y = self.y -1, z = self.z},
    {x = self.x, y = self.y -1, z = self.z},
    {x = self.x -1, y = self.y -1, z = self.z}
  }

  for _, coordinate in pairs(coordinates) do
    table.insert(return_array, Position(coordinate))
  end
  return return_array
end

-- PET tools
function Monster.dig(self)
  if not self:isPet() then
    return false
  end

  local return_value = false

  local position = self:getPosition()
  local surroundings = position:getSurroundings()
  local HOLE_LIST = {468, 481, 483, 7932}

  local function _tmp_dig_in_tile(creature, tile, position)
    if not tile then
      return false
    end

    if tile:getCreatureCount() ~= 0 then
      return false
    end

    if tile:getItemCount() ~= 0 then
      return false
    end

    local ground = tile:getGround()

    if not ground then
      return false
    end

    local groundId = ground:getId()

    if not isInArray(HOLE_LIST, groundId) then
      return false
    end

    ground:transform(groundId + 1)
    ground:decay()
    position:sendMagicEffect(CONST_ME_POFF)
    return true
  end

  for _, next_position in pairs(surroundings) do
    local next_tile = Tile(next_position)

    if _tmp_dig_in_tile(self, next_tile, next_position) then
      return_value = true
    end
  end

  return return_value
end

  • data\chatchannels\chatchannels.lua
Lua:
<?xml version="1.0" encoding="UTF-8"?>
<channels>
    <channel id="2" name="Rule Violations" script="ruleviolations.lua" />
    <channel id="3" name="Tutor" script="tutor.lua" />
    <channel id="4" name="Game Chat" public="1" script="worldchat.lua" />
    <channel id="5" name="Real Chat" public="1" script="englishchat.lua" />
    <channel id="6" name="Trade" public="1" script="trade.lua" />
    <channel id="7" name="Help" public="1" script="help.lua" />
    <channel id="8" name="Gamemaster" script="gamemaster.lua" />
    <channel id="9" name="Pet" script="pet.lua" />  
</channels>



When I login to the char or try to open the PET channel, this error happens in the log:

Lua:
Lua Script Error: [Chat Interface]
data/chatchannels/scripts/pet.lua:onJoin
data/lib/pets_lib.lua:349: attempt to index a nil value
stack traceback:
        [C]: in function '__index'
        data/lib/pets_lib.lua:349: in function 'summonPet'
        data/chatchannels/scripts/pet.lua:6: in function <data/chatchannels/scripts/pet.lua:2>
        [C]: in function 'openChannel'
        data/creaturescripts/scripts/pet_owner_creaturescripts.lua:9: in function <data/creaturescripts/scripts/pet_owner_creaturescripts.lua:5>

Lua Script Error: [Chat Interface]
data/chatchannels/scripts/pet.lua:onJoin
data/lib/pets_lib.lua:349: attempt to index a nil value
stack traceback:
        [C]: in function '__index'
        data/lib/pets_lib.lua:349: in function 'summonPet'
        data/chatchannels/scripts/pet.lua:6: in function <data/chatchannels/scripts/pet.lua:2>

Lua Script Error: [Main Interface]
in a timer event called from:
(Unknown scriptfile)
data/lib/pets_lib.lua:192: attempt to call method 'sendChannelMessage' (a nil value)
stack traceback:
        [C]: in function 'sendChannelMessage'
        data/lib/pets_lib.lua:192: in function <data/lib/pets_lib.lua:187>

Lua Script Error: [Main Interface]
in a timer event called from:
(Unknown scriptfile)
data/lib/pets_lib.lua:192: attempt to call method 'sendChannelMessage' (a nil value)
stack traceback:
        [C]: in function 'sendChannelMessage'
        data/lib/pets_lib.lua:192: in function <data/lib/pets_lib.lua:187>

Can you help me?? Tenkiu!
It's clean TFS 1.2?

To debug issue:

Inside pets_lib.lua change:
Code:
local pet = Game.createMonster(PETS.PREFIX .. (PETS.IDENTIFICATION[self:getPetType()].name), position)

To
Code:
local petType = self:getPetType()
print('- PET-TYPE = ' .. string(petType) )
local petTypeConfig = PETS.IDENTIFICATION[petType]
print('- PET-TYPE-CONFIG = ' .. type(petTypeConfig) )
local petTypeName = petTypeConfig.name
print('- PET-TYPE-CONFIG-NAME = ' .. type(petTypeName) )
local pet = Game.createMonster(PETS.PREFIX .. petTypeName, position)

And show new console log.
 
It's clean TFS 1.2?

To debug issue:

Inside pets_lib.lua change:
Code:
local pet = Game.createMonster(PETS.PREFIX .. (PETS.IDENTIFICATION[self:getPetType()].name), position)

To
Code:
local petType = self:getPetType()
print('- PET-TYPE = ' .. string(petType) )
local petTypeConfig = PETS.IDENTIFICATION[petType]
print('- PET-TYPE-CONFIG = ' .. type(petTypeConfig) )
local petTypeName = petTypeConfig.name
print('- PET-TYPE-CONFIG-NAME = ' .. type(petTypeName) )
local pet = Game.createMonster(PETS.PREFIX .. petTypeName, position)

And show new console log.
return
Lua:
Lua Script Error: [Chat Interface]
data/chatchannels/scripts/pet.lua:onJoin
data/lib/pets_lib.lua:350: attempt to call global 'string' (a table value)
stack traceback:
        [C]: in function 'string'
        data/lib/pets_lib.lua:350: in function 'summonPet'
        data/chatchannels/scripts/pet.lua:6: in function <data/chatchannels/scripts/pet.lua:2>
        [C]: in function 'openChannel'
        data/creaturescripts/scripts/pet_owner_creaturescripts.lua:9: in function <data/creaturescripts/scripts/pet_owner_creaturescripts.lua:5>
 
return
Lua:
Lua Script Error: [Chat Interface]
data/chatchannels/scripts/pet.lua:onJoin
data/lib/pets_lib.lua:350: attempt to call global 'string' (a table value)
stack traceback:
        [C]: in function 'string'
        data/lib/pets_lib.lua:350: in function 'summonPet'
        data/chatchannels/scripts/pet.lua:6: in function <data/chatchannels/scripts/pet.lua:2>
        [C]: in function 'openChannel'
        data/creaturescripts/scripts/pet_owner_creaturescripts.lua:9: in function <data/creaturescripts/scripts/pet_owner_creaturescripts.lua:5>

1. It's clean TFS 1.2?
2. Show me line 350 of data/lib/pets_lib.lua
 
[Warning - Monsters::loadMonster] manaCost missing or zero on monster with summonable and/or convinceable flags: data/monster/pets/cat.xml

???
 
1.4
But i change mana to 5 and dont have errors. I have question. This is normal when I go to Protected Zone my pet disapear? And when I exit from PZ dont respawn. I must close and open pet channel again. Its normal?
This system is very cool. We love this with my childrens :)
 
1.4
But i change mana to 5 and dont have errors. I have question. This is normal when I go to Protected Zone my pet disapear? And when I exit from PZ dont respawn. I must close and open pet channel again. Its normal?
[...]

In TFS 1.2 PET stop moving in that case. Only if You go too far away from pet in Protection Zone or change floor inside PZ, then pet disappear.
I guess it's same with regular summons.

[...]
This system is very cool. We love this with my childrens :)

Nice to hear :)
 
In TFS 1.2 PET stop moving in that case. Only if You go too far away from pet in Protection Zone or change floor inside PZ, then pet disappear.
I guess it's same with regular summons.



Nice to hear :)
Then I'll try to fix it somehow. But I'm not good at LUA and XML so I probably dont fix it hahaha :(

And I find this:
<event type="healthchange" name="PetHealthChange" script="pet_creaturescript.lua" />
You don't write this in tutorial but write this in pet_creaturescript.lua.
What is this?

And where I find this code regarding this waiting?
 
Last edited:
I have 2 more questions.
1. How to do that when logging in, the PET chat does not pop up immediately, but only when I turn it on?
2. Is there a simple way for my pet to enter the Protection zone?
Thank you for any help!
 
Then I'll try to fix it somehow. But I'm not good at LUA and XML so I probably dont fix it hahaha :(

And I find this:
<event type="healthchange" name="PetHealthChange" script="pet_creaturescript.lua" />
You don't write this in tutorial but write this in pet_creaturescript.lua.
What is this?

And where I find this code regarding this waiting?

Propably it was "work in progress" part of code.
It can disable damage caused to pets from players.
I don't touch code for 2 years. So I don't remember now.

I have 2 more questions.
1. How to do that when logging in, the PET chat does not pop up immediately, but only when I turn it on?
2. Is there a simple way for my pet to enter the Protection zone?
Thank you for any help!

1. It should open after login.
Here is code:
lua-scripts/pet_owner_creaturescripts.lua at 968ed389fa80e0e0236dc44fe0fbc9e0010f0d80 · yrpen/lua-scripts (https://github.com/yrpen/lua-scripts/blob/968ed389fa80e0e0236dc44fe0fbc9e0010f0d80/pets_system/pet_owner_creaturescripts.lua#L5)
Maybe it's some client side think? Try use "addEvent" with delay 500ms (0,5s).

2. In TFS 1.2 as far as I know, it was possible only with source edit. I don't know about TFS 1.4
 
I was looking into having evolving summons that would evolve like the summons they have in the game Fable.
(Start with base summon, upon killing another summon it would transform into that creature)

Now not very immersive, but this script does allow for that, and with some imagination, you can also just enhance the strength of your existent summon.
Loving it so far, I only have found two minor issues so far which were solved with a re-log.

- Upon catching a pet, it will not allow you to summon it, because pet channel is open by default but can disable that and doesn't do that automatically, although if you re-log, the pet is well alive and standing next to you :)
- The moment the summon has to teleport, the green party skull disappears, this for sure happens when changing floors, and when moving to fast (only a nuisance when testing on GM, probably won't happen as much as a Player), this skull appears back again with a re-log.

Skull Fix: Fix/Patch - [TFS 1.x] Monster Skulls (https://otland.net/threads/tfs-1-x-monster-skulls.281787/)

Thank you for this!!
 
Last edited:
I'm starting to tweak this system into a fully-fledged Companion system and have some tweaks for people using this system.

- Skulls disappearing from Pet Fix: Fix/Patch - [TFS 1.x] Monster Skulls (https://otland.net/threads/tfs-1-x-monster-skulls.281787/)

- Pets not directly summoned upon receiving; just change self:summonPet(position) to player:summonPet(player:getPosition())

- Want to let an NPC give you a Pet?
Just add:
Lua:
dofile('data/lib/pets_lib.lua')

player:doAddPet(1) -- Pet ID
player:summonPet(player:getPosition())
To the NPC in their dialogue function.
 
Back
Top