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

Feature [TFS 1.3] Adding New Skills

Been using this guide to create a new skill using OTHire:
OTHire - Github

So far I've modified the source/
condition.cpp
C++:
case CONDITIONPARAM_SKILL_FISHING:
{
    skills[SKILL_FISH] = value;
    return true;
}

case CONDITIONPARAM_SKILL_FISHINGPERCENT:
{
    skillsPercent[SKILL_FISH] = value;
    return true;
}

case CONDITIONPARAM_SKILL_COOKING:
{
    skills[SKILL_COOK] = value;
    return true;
}

case CONDITIONPARAM_SKILL_COOKINGPERCENT:
{
    skillsPercent[SKILL_COOK] = value;
    return true;
}

enums.h
C++:
enum ConditionParam_t{
...
    CONDITIONPARAM_SKILL_MELEEPERCENT = 36,
    CONDITIONPARAM_SKILL_FISTPERCENT = 37,
    CONDITIONPARAM_SKILL_CLUBPERCENT = 38,
    CONDITIONPARAM_SKILL_SWORDPERCENT = 39,
    CONDITIONPARAM_SKILL_AXEPERCENT = 40,
    CONDITIONPARAM_SKILL_DISTANCEPERCENT = 41,
    CONDITIONPARAM_SKILL_SHIELDPERCENT = 42,
    CONDITIONPARAM_SKILL_FISHINGPERCENT = 43,
    CONDITIONPARAM_BUFF_SPELL = 44,
    CONDITIONPARAM_SUBID = 45,
    CONDITIONPARAM_SKILL_COOKING = 46,
    CONDITIONPARAM_SKILL_COOKINGPERCENT = 47
};


enum levelTypes_t {
    LEVEL_FIRST = 0,
    LEVEL_SKILL_FIST = LEVEL_FIRST,
    LEVEL_SKILL_CLUB = 1,
    LEVEL_SKILL_SWORD = 2,
    LEVEL_SKILL_AXE = 3,
    LEVEL_SKILL_DIST = 4,
    LEVEL_SKILL_SHIELD = 5,
    LEVEL_SKILL_FISH = 6,
    LEVEL_SKILL_COOK = 7,
    LEVEL_MAGIC = 8,
    LEVEL_EXPERIENCE = 9,
    LEVEL_LAST = LEVEL_EXPERIENCE
};

monsters.cpp
C++:
else if(readXMLInteger(node, "fishing", intValue) || readXMLInteger(node, "fish", intValue)){
    param = CONDITIONPARAM_SKILL_FISHING;
}
else if(readXMLInteger(node, "cooking", intValue) || readXMLInteger(node, "cook", intValue)){
    param = CONDITIONPARAM_SKILL_COOKING;
}

player.cpp
C++:
std::string Player::getSkillName(int skillid)
{
    std::string skillname;
    switch(skillid){
    case SKILL_FIST:
        skillname = "fist fighting";
        break;
    case SKILL_CLUB:
        skillname = "club fighting";
        break;
    case SKILL_SWORD:
        skillname = "sword fighting";
        break;
    case SKILL_AXE:
        skillname = "axe fighting";
        break;
    case SKILL_DIST:
        skillname = "distance fighting";
        break;
    case SKILL_SHIELD:
        skillname = "shielding";
        break;
    case SKILL_FISH:
        skillname = "fishing";
        break;
    case SKILL_COOK:
        skillname = "cooking";
        break;
    default:
        skillname = "unknown";
        break;
    }
    return skillname;
}

protocolgame.cpp
C++:
void ProtocolGame::AddPlayerSkills(NetworkMessage_ptr msg)
{
    msg->AddByte(0xA1);
    msg->AddByte(player->getSkill(SKILL_FIST,   SKILL_LEVEL));
    msg->AddByte(player->getSkill(SKILL_FIST,   SKILL_PERCENT));
    msg->AddByte(player->getSkill(SKILL_CLUB,   SKILL_LEVEL));
    msg->AddByte(player->getSkill(SKILL_CLUB,   SKILL_PERCENT));
    msg->AddByte(player->getSkill(SKILL_SWORD,  SKILL_LEVEL));
    msg->AddByte(player->getSkill(SKILL_SWORD,  SKILL_PERCENT));
    msg->AddByte(player->getSkill(SKILL_AXE,    SKILL_LEVEL));
    msg->AddByte(player->getSkill(SKILL_AXE,    SKILL_PERCENT));
    msg->AddByte(player->getSkill(SKILL_DIST,   SKILL_LEVEL));
    msg->AddByte(player->getSkill(SKILL_DIST,   SKILL_PERCENT));
    msg->AddByte(player->getSkill(SKILL_SHIELD, SKILL_LEVEL));
    msg->AddByte(player->getSkill(SKILL_SHIELD, SKILL_PERCENT));
    msg->AddByte(player->getSkill(SKILL_FISH,   SKILL_LEVEL));
    msg->AddByte(player->getSkill(SKILL_FISH,   SKILL_PERCENT));
    msg->AddByte(player->getSkill(SKILL_COOK,   SKILL_LEVEL));
    msg->AddByte(player->getSkill(SKILL_COOK,   SKILL_PERCENT));
}

items.cpp
C++:
else if(asLowerCaseString(strValue) == "skillfish"){
    if(readXMLInteger(itemAttributesNode, "value", intValue)){
        it.abilities.skill.upgrades[SKILL_FISH] = intValue;
    }
}
else if(asLowerCaseString(strValue) == "skillcook"){
    if(readXMLInteger(itemAttributesNode, "value", intValue)){
        it.abilities.skill.upgrades[SKILL_COOK] = intValue;
    }
}

vocation.cpp
C++:
skillMultipliers[6] = 1.1f;
skillMultipliers[7] = 1.1f;


skillBases[6] = 20;
skillBases[7] = 20;

Now on the server data/

global.lua
Lua:
LEVEL_SKILL_FIST = 0
LEVEL_SKILL_CLUB = 1
LEVEL_SKILL_SWORD = 2
LEVEL_SKILL_AXE = 3
LEVEL_SKILL_DISTANCE = 4
LEVEL_SKILL_SHIELDING = 5
LEVEL_SKILL_FISHING = 6
LEVEL_SKILL_COOKING = 7
LEVEL_MAGIC = 8
LEVEL_EXPERIENCE = 9

CONST_SKILL_FIST = 0
CONST_SKILL_CLUB = 1
CONST_SKILL_SWORD = 2
CONST_SKILL_AXE = 3
CONST_SKILL_DISTANCE = 4
CONST_SKILL_SHIELDING = 5
CONST_SKILL_FISHING = 6
CONST_SKILL_COOKING = 7

vocations.xml
XML:
<skill id="7" base="20" multiplier="1.1"/>

Created an action script for cooking raw fish into cooked fish:

cooking.lua
Lua:
local ITEM_RAW_FISH = 2667
local ITEM_COOKED FISH = 5135


function onUse(cid, item, fromPosition, itemEx, toPosition)

local formula = (getPlayerSkill(cid, CONST_SKILL_COOKING) / 200) + (0.85 * math.random())
    if(item.itemid == 2667 and isInArray(OVEN_ON, itemEx.itemid) == true) then
        doPlayerAddItem(cid, 5135)
        doRemoveItem(item.uid, 1)
        doPlayerAddSkillTry(cid, CONST_SKILL_COOKING, 1)
        return true
    end
 
    return false
end

Also, I've changed the sql schema to allow all new characters to have this new skill
schema.sql
PHP:
-- Triggers `players`
--
DROP TRIGGER IF EXISTS `oncreate_players`;
DELIMITER //
CREATE TRIGGER `oncreate_players` AFTER INSERT ON `players`
 FOR EACH ROW BEGIN
    INSERT INTO `player_skills` (`player_id`, `skillid`, `value`) VALUES (NEW.`id`, 0, 10);
    INSERT INTO `player_skills` (`player_id`, `skillid`, `value`) VALUES (NEW.`id`, 1, 10);
    INSERT INTO `player_skills` (`player_id`, `skillid`, `value`) VALUES (NEW.`id`, 2, 10);
    INSERT INTO `player_skills` (`player_id`, `skillid`, `value`) VALUES (NEW.`id`, 3, 10);
    INSERT INTO `player_skills` (`player_id`, `skillid`, `value`) VALUES (NEW.`id`, 4, 10);
    INSERT INTO `player_skills` (`player_id`, `skillid`, `value`) VALUES (NEW.`id`, 5, 10);
    INSERT INTO `player_skills` (`player_id`, `skillid`, `value`) VALUES (NEW.`id`, 6, 10);
    INSERT INTO `player_skills` (`player_id`, `skillid`, `value`) VALUES (NEW.`id`, 7, 10);
END

So far so good, everything works as intended:
tibia_cooking.png

But when I logout, seems the skill advance and the count tries are not being registered at the database player_skills:
1688150597865.png
And I'm back at cooking level 10, even if I modify the D.B. manually with some counts (1) it will load them at the front-end.
1688150788340.png
1688150757103.png
Any guess on what's going on, why is it not sending the tries?

Greatly appreciated if anyone could take a look, thanks.
 
Last edited:
Been using this guide to create a new skill using OTHire:
OTHire - Github

So far I've modified the source/
condition.cpp
C++:
case CONDITIONPARAM_SKILL_FISHING:
{
    skills[SKILL_FISH] = value;
    return true;
}

case CONDITIONPARAM_SKILL_FISHINGPERCENT:
{
    skillsPercent[SKILL_FISH] = value;
    return true;
}

case CONDITIONPARAM_SKILL_COOKING:
{
    skills[SKILL_COOK] = value;
    return true;
}

case CONDITIONPARAM_SKILL_COOKINGPERCENT:
{
    skillsPercent[SKILL_COOK] = value;
    return true;
}

enums.h
C++:
enum ConditionParam_t{
...
    CONDITIONPARAM_SKILL_MELEEPERCENT = 36,
    CONDITIONPARAM_SKILL_FISTPERCENT = 37,
    CONDITIONPARAM_SKILL_CLUBPERCENT = 38,
    CONDITIONPARAM_SKILL_SWORDPERCENT = 39,
    CONDITIONPARAM_SKILL_AXEPERCENT = 40,
    CONDITIONPARAM_SKILL_DISTANCEPERCENT = 41,
    CONDITIONPARAM_SKILL_SHIELDPERCENT = 42,
    CONDITIONPARAM_SKILL_FISHINGPERCENT = 43,
    CONDITIONPARAM_BUFF_SPELL = 44,
    CONDITIONPARAM_SUBID = 45,
    CONDITIONPARAM_SKILL_COOKING = 46,
    CONDITIONPARAM_SKILL_COOKINGPERCENT = 47
};


enum levelTypes_t {
    LEVEL_FIRST = 0,
    LEVEL_SKILL_FIST = LEVEL_FIRST,
    LEVEL_SKILL_CLUB = 1,
    LEVEL_SKILL_SWORD = 2,
    LEVEL_SKILL_AXE = 3,
    LEVEL_SKILL_DIST = 4,
    LEVEL_SKILL_SHIELD = 5,
    LEVEL_SKILL_FISH = 6,
    LEVEL_SKILL_COOK = 7,
    LEVEL_MAGIC = 8,
    LEVEL_EXPERIENCE = 9,
    LEVEL_LAST = LEVEL_EXPERIENCE
};

monsters.cpp
C++:
else if(readXMLInteger(node, "fishing", intValue) || readXMLInteger(node, "fish", intValue)){
    param = CONDITIONPARAM_SKILL_FISHING;
}
else if(readXMLInteger(node, "cooking", intValue) || readXMLInteger(node, "cook", intValue)){
    param = CONDITIONPARAM_SKILL_COOKING;
}

player.cpp
C++:
std::string Player::getSkillName(int skillid)
{
    std::string skillname;
    switch(skillid){
    case SKILL_FIST:
        skillname = "fist fighting";
        break;
    case SKILL_CLUB:
        skillname = "club fighting";
        break;
    case SKILL_SWORD:
        skillname = "sword fighting";
        break;
    case SKILL_AXE:
        skillname = "axe fighting";
        break;
    case SKILL_DIST:
        skillname = "distance fighting";
        break;
    case SKILL_SHIELD:
        skillname = "shielding";
        break;
    case SKILL_FISH:
        skillname = "fishing";
        break;
    case SKILL_COOK:
        skillname = "cooking";
        break;
    default:
        skillname = "unknown";
        break;
    }
    return skillname;
}

protocolgame.cpp
C++:
void ProtocolGame::AddPlayerSkills(NetworkMessage_ptr msg)
{
    msg->AddByte(0xA1);
    msg->AddByte(player->getSkill(SKILL_FIST,   SKILL_LEVEL));
    msg->AddByte(player->getSkill(SKILL_FIST,   SKILL_PERCENT));
    msg->AddByte(player->getSkill(SKILL_CLUB,   SKILL_LEVEL));
    msg->AddByte(player->getSkill(SKILL_CLUB,   SKILL_PERCENT));
    msg->AddByte(player->getSkill(SKILL_SWORD,  SKILL_LEVEL));
    msg->AddByte(player->getSkill(SKILL_SWORD,  SKILL_PERCENT));
    msg->AddByte(player->getSkill(SKILL_AXE,    SKILL_LEVEL));
    msg->AddByte(player->getSkill(SKILL_AXE,    SKILL_PERCENT));
    msg->AddByte(player->getSkill(SKILL_DIST,   SKILL_LEVEL));
    msg->AddByte(player->getSkill(SKILL_DIST,   SKILL_PERCENT));
    msg->AddByte(player->getSkill(SKILL_SHIELD, SKILL_LEVEL));
    msg->AddByte(player->getSkill(SKILL_SHIELD, SKILL_PERCENT));
    msg->AddByte(player->getSkill(SKILL_FISH,   SKILL_LEVEL));
    msg->AddByte(player->getSkill(SKILL_FISH,   SKILL_PERCENT));
    msg->AddByte(player->getSkill(SKILL_COOK,   SKILL_LEVEL));
    msg->AddByte(player->getSkill(SKILL_COOK,   SKILL_PERCENT));
}

items.cpp
C++:
else if(asLowerCaseString(strValue) == "skillfish"){
    if(readXMLInteger(itemAttributesNode, "value", intValue)){
        it.abilities.skill.upgrades[SKILL_FISH] = intValue;
    }
}
else if(asLowerCaseString(strValue) == "skillcook"){
    if(readXMLInteger(itemAttributesNode, "value", intValue)){
        it.abilities.skill.upgrades[SKILL_COOK] = intValue;
    }
}

vocation.cpp
C++:
skillMultipliers[6] = 1.1f;
skillMultipliers[7] = 1.1f;


skillBases[6] = 20;
skillBases[7] = 20;

Now on the server data/

global.lua
Lua:
LEVEL_SKILL_FIST = 0
LEVEL_SKILL_CLUB = 1
LEVEL_SKILL_SWORD = 2
LEVEL_SKILL_AXE = 3
LEVEL_SKILL_DISTANCE = 4
LEVEL_SKILL_SHIELDING = 5
LEVEL_SKILL_FISHING = 6
LEVEL_SKILL_COOKING = 7
LEVEL_MAGIC = 8
LEVEL_EXPERIENCE = 9

CONST_SKILL_FIST = 0
CONST_SKILL_CLUB = 1
CONST_SKILL_SWORD = 2
CONST_SKILL_AXE = 3
CONST_SKILL_DISTANCE = 4
CONST_SKILL_SHIELDING = 5
CONST_SKILL_FISHING = 6
CONST_SKILL_COOKING = 7

vocations.xml
XML:
<skill id="7" base="20" multiplier="1.1"/>

Created an action script for cooking raw fish into cooked fish:

cooking.lua
Lua:
local ITEM_RAW_FISH = 2667
local ITEM_COOKED FISH = 5135


function onUse(cid, item, fromPosition, itemEx, toPosition)

local formula = (getPlayerSkill(cid, CONST_SKILL_COOKING) / 200) + (0.85 * math.random())
    if(item.itemid == 2667 and isInArray(OVEN_ON, itemEx.itemid) == true) then
        doPlayerAddItem(cid, 5135)
        doRemoveItem(item.uid, 1)
        doPlayerAddSkillTry(cid, CONST_SKILL_COOKING, 1)
        return true
    end
 
    return false
end

Also, I've changed the sql schema to allow all new characters to have this new skill
schema.sql
PHP:
-- Triggers `players`
--
DROP TRIGGER IF EXISTS `oncreate_players`;
DELIMITER //
CREATE TRIGGER `oncreate_players` AFTER INSERT ON `players`
 FOR EACH ROW BEGIN
    INSERT INTO `player_skills` (`player_id`, `skillid`, `value`) VALUES (NEW.`id`, 0, 10);
    INSERT INTO `player_skills` (`player_id`, `skillid`, `value`) VALUES (NEW.`id`, 1, 10);
    INSERT INTO `player_skills` (`player_id`, `skillid`, `value`) VALUES (NEW.`id`, 2, 10);
    INSERT INTO `player_skills` (`player_id`, `skillid`, `value`) VALUES (NEW.`id`, 3, 10);
    INSERT INTO `player_skills` (`player_id`, `skillid`, `value`) VALUES (NEW.`id`, 4, 10);
    INSERT INTO `player_skills` (`player_id`, `skillid`, `value`) VALUES (NEW.`id`, 5, 10);
    INSERT INTO `player_skills` (`player_id`, `skillid`, `value`) VALUES (NEW.`id`, 6, 10);
    INSERT INTO `player_skills` (`player_id`, `skillid`, `value`) VALUES (NEW.`id`, 7, 10);
END

So far so good, everything works as intended:
View attachment 76493

But when I logout, seems the skill advance and the count tries are not being registered at the database player_skills:
View attachment 76494
And I'm back at cooking level 10, even if I modify the D.B. manually with some counts (1) it will load them at the front-end.
View attachment 76496
View attachment 76495
Any guess on what's going on, why is it not sending the tries?

Greatly appreciated if anyone could take a look, thanks.
Let us know if it worked out after checking what Evil said above :)
 
You guys were right about the enums.h moreover:

Thanks to @Tofame ioplayer.cpp
C++:
//skills
for(int32_t i = 0; i <= 7; ++i){
    query << "UPDATE `player_skills` SET `value` = " << player->skills[i][SKILL_LEVEL] << ", `count` = " << player->skills[i][SKILL_TRIES] << " WHERE `player_id` = " << player->getGUID() << " AND `skillid` = " << i;

    if(!db->executeQuery(query.str())){
        return false;
    }
    query.str("");
}

Database

1688771591918.png
 
here are the changes in format of github changes in case anyone needs:



based on tfs 1.5 master, but should work on downgrades as well

credits to @Ramirow
 
Last edited by a moderator:
For nothing I can not train skills I do not know what the problem I tried different code and the client skill does not grow everything compiled without error
 
For nothing I can not train skills I do not know what the problem I tried different code and the client skill does not grow everything compiled without error
how are you adding the skill tries? show us the code
 
It gives me fist instead of energy. xD somewhere I probably have an error
Lua:
local config = {
    healthPercent = 0.25, -- Procent zdrowia do regeneracji
    manaPercent = 0.25,  -- Procent many do regeneracji
 
}

function onUse(player, item, fromPosition, target, toPosition, isHotkey)
    player:addSkillTries(SKILL_ENERGY, 10)
   
    local maxHealth = player:getMaxHealth()
    local maxMana = player:getMaxMana()
   
  
    local healthToRegen = maxHealth * config.healthPercent
    local manaToRegen = maxMana * config.manaPercent
   
   
    player:addHealth(healthToRegen)
    player:addMana(manaToRegen)
    item:remove(1)
   
    return true
end
Post automatically merged:

How do I use SKILL_ENERGY fist gives me but how to use Skillid or 7 it adds energy why so?
 
Last edited:
C++:
 CONDITION_PARAM_AGGRESSIVE = 54,
    CONDITION_PARAM_DRUNKENNESS = 55,
    CONDITION_PARAM_SKILL_ENERGY = 56,
    CONDITION_PARAM_SKILL_ENERGYPERCENT = 57,
};

BlockType_t wyliczenia : uint8_t

    SKILL_DISTANCE = 4,
    SKILL_SHIELD = 5,
    SKILL_FISHING = 6,
    SKILL_ENERGY = 7,

 
    SKILL_MAGLEVEL = 8,
    SKILL_LEVEL = 9,

    SKILL_FIRST = SKILL_FIST,
    SKILL_LAST = SKILL_FISHING
    SKILL_LAST = SKILL_ENERGY
};
 
@Michcol94 there should be only one SKILL_LAST, not two

the github changes should be added if they are green, and removed if they are red
 
I didn't have this error before, but now it shows up.

Lua Script Error: [Main Interface]
data/global.lua
data/lib/compat/compat.lua:1458: table index is nil
stack traceback:
[C]: in function '__newindex'
data/lib/compat/compat.lua:1458: in main chunk
[C]: in function 'dofile'
data/lib/lib.lua:5: in main chunk
[C]: in function 'dofile'
data/global.lua:2: in main chunk
[Warning - ScriptingManager::loadScriptSystems] Can not load data/global.lua

Lua Script Error: [Scripts Interface]
C:\Users\Majke\Desktop\Source-Dragon-Ball-master (2)\Source-Dragon-Ball-master\data_moja\data\scripts/lib\create_functions.lua
data/lib/compat/compat.lua:1387: attempt to call field 'contains' (a nil value)
stack traceback:
[C]: in function 'contains'
data/lib/compat/compat.lua:1387: in function 'createFunctions'
...l-master\data_moja\data\scripts/lib\create_functions.lua:1: in main chunk
create_functions.lua [error]
^[Warning - Actions::registerLuaEvent] There is no id / aid / uid set for this event
This happens as soon as I make changes to lua. Even if I do them on a backup, strange, it was working normally before, I tried a new database to do the same.
 
Last edited:
But these changes are the ones you wrote on github without this skill will probably not work and the changes cause me this error, I don't know why? engine tfs 1.5 downgrade 8.6 by Nekiro
 
But these changes are the ones you wrote on github without this skill will probably not work and the changes cause me this error, I don't know why? engine tfs 1.5 downgrade 8.6 by Nekiro
no, they shouldn't error, have you tried?
 
It adds only in global.lua`p`.skill_energy,["skill_energy"] = result.getNumber(query, "skill_energy"),and in compat.lua[SKILL_ENERGY] = 'energy',and crashes errors in the console as removed I have no skill errors as if it works as removed these elements I will add that it worked before with changes, only something broke and it no longer works with changes
 
Back
Top