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

Lua Different Critical effect on vocation id

Tbol

Well-Known Member
Joined
Apr 7, 2019
Messages
592
Reaction score
64
So in player.cpp im using a
LUA:
void Player::sendCritical(const Creature* target) const
{
    if (target) {
        g_game.addMagicEffect(target->getPosition(), 10);
    }

    g_game.addAnimatedText("Critical", getPosition(), TEXTCOLOR_RED);
}
would it be possible to achieve different critical effects depending on vacation id, without hurting performance a lot because considering it might hit performance quite a lot beause every if on attack will be have another if statement which in my logic is quite a truck hit in performance.
 
C++:
void Player::sendCritical(const Creature* target) const
{
    if (target) {
        g_game.addMagicEffect(target->getPosition(), 10);
    }

    g_game.addAnimatedText("Critical", getPosition(), TEXTCOLOR_RED);
}
This code is wrong. You added g_game.addMagicEffect and g_game.sendAnimatedText to Player::sendCritical, which executes for every player on screen that sees critical damage (or at least it should, idk your code).
With that code, if there are 10 players on same screen, it will send 10x text Critical and 10x animation critical to each player (10x same network packet, Tibia Client/OTC may hide it and show as just 1 message on screen, but server will send it 10x to each player).
You should go to function that executes Player::sendCritical and add your effects there.

After you fix this, you can work on modifying what effects and what messages you send based on vocation in Player::sendCritical... On vocation of player that sees effect, if you want to make it base on vocation of player that does attack, then go to place where it executes sendCritical and modify it.
 
This code is wrong. You added g_game.addMagicEffect and g_game.sendAnimatedText to Player::sendCritical, which executes for every player on screen that sees critical damage (or at least it should, idk your code).
With that code, if there are 10 players on same screen, it will send 10x text Critical and 10x animation critical to each player (10x same network packet, Tibia Client/OTC may hide it and show as just 1 message on screen, but server will send it 10x to each player).
You should go to function that executes Player::sendCritical and add your effects there.

After you fix this, you can work on modifying what effects and what messages you send based on vocation in Player::sendCritical... On vocation of player that sees effect, if you want to make it base on vocation of player that does attack, then go to place where it executes sendCritical and modify it.
The sendcritical is being sent like this, and it suppose to send it for everyone because alll of them have to see it, so it suppose to be like this
C++:
bool Weapon::useFist(Player* player, Creature* target)
{
    if (!Position::areInRange<1, 1>(player->getPosition(), target->getPosition())) {
        return false;
    }
 
    float attackFactor = player->getAttackFactor();
    int32_t attackSkill = player->getSkillLevel(SKILL_FIST);
    int32_t attackValue = 7;
 
    int32_t maxDamage = Weapons::getMaxWeaponDamage(player->getLevel(), attackSkill, attackValue, attackFactor);
    uint32_t skill = player->getSkillLevel(SKILL_SWORD);
    int32_t criticChance = std::floor(skill  / 4);
    if(criticChance > 100)
        criticChance = 100;
 
    bool isCritic = false;
    if(uniform_random(1, 100) <= criticChance)
    {
        maxDamage = std::pow(maxDamage, g_config.getFloat(ConfigManager::CRITIC_DAMAGE_MULTIPLIER));
        player->sendCritical(target);

        isCritic = true;
    }
 
    CombatParams params;
    params.combatType = COMBAT_PHYSICALDAMAGE;
    params.blockedByArmor = true;
    params.blockedByShield = true;
 
    CombatDamage damage;
    damage.origin = ORIGIN_MELEE;
    damage.primary.type = params.combatType;

    if(isCritic)
        damage.primary.value = -normal_random(maxDamage * 0.90, maxDamage);
    else
        damage.primary.value = -normal_random(maxDamage * 0.65, maxDamage);

    Combat::doCombatHealth(player, target, damage, params);
    if (!player->hasFlag(PlayerFlag_NotGainSkill) && player->getAddAttackSkill()) {
        player->addSkillAdvance(SKILL_AXE, 1);
        player->addSkillAdvance(SKILL_SWORD, 1);
    }
 
    return true;
}
same goes for those functions its beeing executed exactly the same like in sent example of code
C++:
int32_t WeaponMelee::getElementDamage(const Player* player, const Creature* target, const Item* item) const
int32_t WeaponMelee::getWeaponDamage(const Player* player, const Creature* target, const Item* item, bool maxDamage /*= false*/) const  
int32_t WeaponDistance::getElementDamage(const Player* player, const Creature* target, const Item* item) const
int32_t WeaponDistance::getWeaponDamage(const Player* player, const Creature* target, const Item* item, bool maxDamage /*= false*/) const
 
Last edited:
i think what gesior means is that if you cast it from player class like you do other player->sendsomethingsomething it would mean you send it to 1 player, in your code you just send it to 1 position, so you could just move this function to game class or wherever you want because you're not using player in this case anyway.

But tfs src have a lot of dogshit code anyway, so you can just throw switch by player vocation id in there and do not care, unless you have 7324832 players doing this thing in 1 time its not like you have 1gb ram on your server anyway.
You shoudnt care about performance at all untill you're sure it will heavly impact your server or until players start reporting bugs, then you fix that. Waste of time to perfect everthing
 
i think what gesior means is that if you cast it from player class like you do other player->sendsomethingsomething it would mean you send it to 1 player, in your code you just send it to 1 position, so you could just move this function to game class or wherever you want because you're not using player in this case anyway.

But tfs src have a lot of dogshit code anyway, so you can just throw switch by player vocation id in there and do not care, unless you have 7324832 players doing this thing in 1 time its not like you have 1gb ram on your server anyway.
You shoudnt care about performance at all untill you're sure it will heavly impact your server or until players start reporting bugs, then you fix that. Waste of time to perfect everthing
I tried using 'case' like you said but for some reason it didnt work. And another annoying factor doing it trough source is hella annoying to adjust it later on if i want to change the effect or something it would require entire src recompile again
 
player.h:
C++:
uint_32t criticalEffect = 0;


void setCriticalEffect(uint32_t value) {
    criticalEffect = value;
}

uint32_t getCriticalEffect() {
    return criticalEffect;
}

then just register it in lua, make a script where player first time login that will use above function for example
(based on xx vocation)

like

LUA:
criticalEffects = {
   ["Sorcerer"] = 12,
   ["Druid"] = 13
}

LUA:
player:setCriticalEffect(criticalEffects[getVocationFunction]);

to prevent repeating this process you can use database and do something like
SQL:
ALTER TABLE  `players` ADD  `criticalEffect` INT( 11 ) NOT NULL DEFAULT  '0'

then in file iologindata edit the lines that will read / load our "criticalEffect" as player->setCriticalEffect(xx id)


then simply in function that critical is sended
C++:
uint32_t effectId = player->getCriticalEffect();

ps. thats a turbo basic, i would do this in vocation class using vectors or just vocations.xml
 
Last edited:
player.h:
C++:
uint_32t criticalEffect = 0;


void setCriticalEffect(uint32_t value) {
    criticalEffect = value;
}

uint32_t getCriticalEffect() {
    return criticalEffect;
}

then just register it in lua, make a script where player first time login that will use above function for example
(based on xx vocation)

like

LUA:
criticalEffects = {
   ["Sorcerer"] = 12,
   ["Druid"] = 13
}

LUA:
player:setCriticalEffect(criticalEffects[getVocationFunction]);

to prevent repeating this process you can use database and do something like
SQL:
ALTER TABLE  `players` ADD  `criticalEffect` INT( 11 ) NOT NULL DEFAULT  '0'

then in file iologindata edit the lines that will read / load our "criticalEffect" as player->setCriticalEffect(xx id)


then simply in function that critical is sended
C++:
uint32_t effectId = player->getCriticalEffect();

ps. thats a turbo basic, i would do this in vocation class using vectors or just vocations.xml
Something is not adding up with the code you sent. Doesnt it need to be registered on hit like im doing with my code on functions \/
C++:
int32_t WeaponMelee::getElementDamage(const Player* player, const Creature* target, const Item* item) const
int32_t WeaponMelee::getWeaponDamage(const Player* player, const Creature* target, const Item* item, bool maxDamage /*= false*/) const 
int32_t WeaponDistance::getElementDamage(const Player* player, const Creature* target, const Item* item) const
int32_t WeaponDistance::getWeaponDamage(const Player* player, const Creature* target, const Item* item, bool maxDamage /*= false*/) const
 
its not a full working solution code, its more like hint how it should look +/-
I think i better gonna use vocation check and sending effect trough source, but with my code for some reason its not sending any effect only the text
C++:
void Player::sendCritical(const Creature* target) const
{
    uint32_t effectId = 10;  // Default magic effect

    switch (getVocationId()) {
        case 1: // Knight
            effectId = 11;
            break;
        case 2: // Sorcerer
            effectId = 12;
            break;
        case 3: // Paladin
            effectId = 13;
            break;
        case 4: // Druid
            effectId = 14;
            break;
        default:
            break;
    }

    if (target) {
        g_game.addMagicEffect(target->getPosition(), effectId);
    }

    g_game.addAnimatedText("Critical!", getPosition(), TEXTCOLOR_LIGHTGREEN);
}
Edit: it seems like sometimes it sends and sometimes it doesnt. wtf
 
Why not use the onHealthChange and onManaChange functions? Simply check the vocation and apply different effects for each. For example, Druids should have effect ID 14.

example:

LUA:
local vocationEffects = {
        [1] = CONST_ME_BLOCKHIT,  -- Knight (effect ID 11)
        [2] = CONST_ME_MAGIC_RED, -- Sorcerer (effect ID 12)
        [3] = CONST_ME_MAGIC_BLUE, -- Paladin (effect ID 13)
        [4] = CONST_ME_MAGIC_GREEN -- Druid (effect ID 14)
    }

    local player = Player(creature)
    if not player then
        return primaryDamage, primaryType, secondaryDamage, secondaryType
    end

    local vocationId = player:getVocation():getId()
    local effect = vocationEffects[vocationId] or CONST_ME_MAGIC_YELLOW -- Default effect

    -- Apply the visual effect at the player's position
    creature:getPosition():sendMagicEffect(effect)

If it were you, you would make the script in LUA... Just take Ralke's script and adapt it as Xinki said. Then, you can adapt a vocation table, each with a different critical ID, as I mentioned above. It should work.
 
Why not use the onHealthChange and onManaChange functions? Simply check the vocation and apply different effects for each. For example, Druids should have effect ID 14.

example:

LUA:
local vocationEffects = {
        [1] = CONST_ME_BLOCKHIT,  -- Knight (effect ID 11)
        [2] = CONST_ME_MAGIC_RED, -- Sorcerer (effect ID 12)
        [3] = CONST_ME_MAGIC_BLUE, -- Paladin (effect ID 13)
        [4] = CONST_ME_MAGIC_GREEN -- Druid (effect ID 14)
    }

    local player = Player(creature)
    if not player then
        return primaryDamage, primaryType, secondaryDamage, secondaryType
    end

    local vocationId = player:getVocation():getId()
    local effect = vocationEffects[vocationId] or CONST_ME_MAGIC_YELLOW -- Default effect

    -- Apply the visual effect at the player's position
    creature:getPosition():sendMagicEffect(effect)

If it were you, you would make the script in LUA... Just take Ralke's script and adapt it as Xinki said. Then, you can adapt a vocation table, each with a different critical ID, as I mentioned above. It should work.
you would need to move whole function about critical hit to lua, its already in game.cpp or combat.cpp, using onHealthChange event from lua its the last option that you want to use when you wanna increase / decrease damage value.
 
Back
Top