• 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] How make critical work with distance and magiclevel?

thalmonn

New Member
Joined
Aug 18, 2019
Messages
9
Reaction score
0
So, with help of users from OtLand, we make this code and it is works very well for melee combat. I try use with sword, axe and club. Works very fine.

But now, the question is, how make this work with distance for ranged atk and work with magic level, for druids and sorc? The need is to work when a palladin atk with a bow, and his basic atk with arrow have the chance to do the critical, like the swords, axes and clubs. For sorc and druid, its the same, when use a wand or staff, the atk have a chance to do critical hit too.

Anyone can help me?

The code:

Lua:
-- Create the normal attack combatObject
local normalAttack = createCombatObject()
setCombatParam(normalAttack, COMBAT_PARAM_TYPE, COMBAT_PHYSICALDAMAGE)

-- Define the formula for a normal attack
function normalAttackFormula(player, level, maglevel)
    skill = player:getSkillLevel(SKILL_SWORD)
    level = player:getLevel()
    min = -((skill*1)+level*1)
    max = -((skill*7)+level*1)
    return min, max
end

-- Attach the normal attack formula to the normal attack combat object
setCombatCallback(normalAttack, CALLBACK_PARAM_LEVELMAGICVALUE, "normalAttackFormula")

-- Create the critical strike combatObject
local criticalStrike = createCombatObject()
setCombatParam(criticalStrike, COMBAT_PARAM_EFFECT, 32)
setCombatParam(criticalStrike, COMBAT_PARAM_TYPE, COMBAT_PHYSICALDAMAGE)

-- Define the formula for a critical strike
function criticalStrikeFormula(player, level, maglevel)
    skill = player:getSkillLevel(SKILL_SWORD)
    level = player:getLevel()
    min = -((skill*9)+level*2)
    max = -((skill*12)+level*3)
    return min, max
end

-- Attach a delayed condition to the critical strike combatObject
local condition = createConditionObject(CONDITION_EMO)
setConditionParam(condition, CONDITION_PARAM_DELAYED, 1)
addDamageCondition(condition, 100, 3100, -math.random(67,112))
setCombatCondition(criticalStrike, condition)

-- Attach the critical strike formula to the critical strike combat object
setCombatCallback(criticalStrike, CALLBACK_PARAM_LEVELMAGICVALUE, "criticalStrikeFormula")

-- When assigned weapon is used
function onUseWeapon(player, variant)
    -- 50 skill / 645 = aprox 7.7% chance of a critical strike
    if math.random(1,645) <= player:getSkillLevel(SKILL_SWORD) then
        -- If you do a critical strike, there is an additional
        -- 1 / 29 = 3.4% chance of saying "For honor!"
        if math.random(1,29) == 1 then
            player:say("For honor!",TALKTYPE_SAY)
        end
        -- Heal player 20% of his max health
        player:addHealth(player:getMaxHealth()/5)
        -- AnimatedText might be deprecated in clients > 8.6
        --doSendAnimatedText(getPlayerPosition(cid),"Critical!",129)
        player:say("Critical!", TALKTYPE_MONSTER_SAY)
        -- Do the critical strike
        doCombat(player, criticalStrike, variant)
    else
        -- Do a regular attack
        doCombat(player, normalAttack, variant)
    end
end
 
How are you assigning the weapons?

For distance, you might need to assign it to the ammo itself, not the bow. And in the ammo script, determine equipped weapon and do the logic accordingly.

For wands, could it be the default wand behavior overrides this somehow?

There is a sample of weapons handled through revscriptsys (including ammo and wand):
 
@Znote you asked me how i assigning the weapons. So, i do like this. I create new weapons on game (new id, sprites, etc), then i setup this new weapon on items.xml. After that, i create a line on weapons.xml calling the script to the new custom weapon. I'll show on the screens bellow.

itemsxml.PNG

weaponsxml.PNG

So, there some way to distance and magic (wand and bows), works like sword, club and axe?
 
@Delusion the code of critical system its on the start of this thread. And the aplication is explained on nexts posts of this thread.
Forgive me, I misunderstood what you were trying to do here, my apologies.
You could copy the same script to another one and instead of using skillLevel of SKILL_SWORD, change it to player:getMagicLevel() or player:getSkillLevel(SKILL_DISTANCE). Or, you could check for the weapon type that the player is using for all 3 types (melee, distance, magic) and handle it in the same way I just described, but all within the same script. Try it yourself, if you can't figure it out post here again and I can help tomorrow (it's already 1am for me).
 
I havent played with this in a while, so I'm not quite sure how to do it.

Forgive me, I misunderstood what you were trying to do here, my apologies.
You could copy the same script to another one and instead of using skillLevel of SKILL_SWORD, change it to player:getMagicLevel() or player:getSkillLevel(SKILL_DISTANCE). Or, you could check for the weapon type that the player is using for all 3 types (melee, distance, magic) and handle it in the same way I just described, but all within the same script. Try it yourself, if you can't figure it out post here again and I can help tomorrow (it's already 1am for me).

I think he has script variations that account for multiple skill types, but he is struggling to get the scripts properly registered (or get onUseWeapon called) for wands, rods and distance weapons.

The need is to work when a palladin atk with a bow, and his basic atk with arrow have the chance to do the critical, like the swords, axes and clubs. For sorc and druid, its the same, when use a wand or staff, the atk have a chance to do critical hit too.

I interpret this as "I need this script to work for paladins attacking with a distance weapon, and mages using wands or rods. I have currently only gotten it to work with melee weapons.".

(The script gives you a chance to deal a critical strike).

I believe this is easy to do with Revscriptsys, but that is not available in TFS 1.2, only latest 1.3.
 
Last edited:
Implement on sources on c++, critical system isn't hard to understand. It just takes a "chance" value and verifies with another "random" value, if one is bigger then other, it adds a CONST_ME_CRITICAL_DAMAGE (a constante magic effect of critical damage) and increases the damage. I implemented it on sources without using items.xml, on my project every sword, club, axe or distance weapon can do a critical damage. Take a look at Combat class:


Code:
void Combat::CombatHealthFunc(Creature* caster, Creature* target, const CombatParams& params, CombatDamage* data)
{
    assert(data);
    CombatDamage damage = *data;
    if (g_game.combatBlockHit(damage, caster, target, params.blockedByShield, params.blockedByArmor, params.itemId != 0)) {
        return;
    }

    Player* attackerPlayer = caster ? caster->getPlayer() : nullptr;
    Player* targetPlayer = target ? target->getPlayer() : nullptr;

    if ((attackerPlayer && damage.primary.value < 0) || damage.secondary.value < 0) {
        uint16_t chance = attackerPlayer->getSpecialSkill(SPECIALSKILL_HITPOINTSLEECHCHANCE);
        uint16_t skill = attackerPlayer->getSpecialSkill(SPECIALSKILL_HITPOINTSLEECHAMOUNT);
        if (chance != 0 && uniform_random(1, 100) <= chance) {
            CombatDamage lifeLeech;
            lifeLeech.primary.value = std::round(damage.primary.value * (skill / 100.));
            lifeLeech.primary.value += std::round(damage.secondary.value * (skill / 100.));
            g_game.combatChangeHealth(nullptr, attackerPlayer, lifeLeech);
        }

        chance = attackerPlayer->getSpecialSkill(SPECIALSKILL_MANAPOINTSLEECHCHANCE);
        skill = attackerPlayer->getSpecialSkill(SPECIALSKILL_MANAPOINTSLEECHAMOUNT);
        if (chance != 0 && uniform_random(1, 100) <= chance) {
            CombatDamage manaLeech;
            manaLeech.primary.value = std::round(damage.primary.value * (skill / 100.));
            manaLeech.primary.value += std::round(damage.secondary.value * (skill / 100.));
            g_game.combatChangeMana(nullptr, attackerPlayer, manaLeech);
        }

        chance = attackerPlayer->getSpecialSkill(SPECIALSKILL_CRITICALHITCHANCE);
        skill = attackerPlayer->getSpecialSkill(SPECIALSKILL_CRITICALHITAMOUNT);

        if (g_config.getBoolean(ConfigManager::CRITICAL_ON_ALL_WEAPONS)) {
            WeaponType_t weaponType = attackerPlayer->getWeapon(true)->getWeaponType();
            if (weaponType == WEAPON_SWORD || weaponType == WEAPON_AXE ||
                weaponType == WEAPON_CLUB || weaponType == WEAPON_DISTANCE) {
                chance = g_config.getNumber(ConfigManager::CRITICAL_CHANCE);
                if (chance != 0 && uniform_random(1, 100) <= chance) {
                    damage.primary.value += std::round(damage.primary.value * (g_config.getNumber(ConfigManager::CRITICAL_AMOUNT) / 100.));
                    damage.secondary.value += std::round(damage.secondary.value * (g_config.getNumber(ConfigManager::CRITICAL_AMOUNT) / 100.));
                    g_game.addMagicEffect(target->getPosition(), CONST_ME_CRITICAL_DAMAGE);
                }
            }
        }

        else {
            if (chance != 0 && uniform_random(1, 100) <= chance) {
                damage.primary.value += std::round(damage.primary.value * (skill / 100.));
                damage.secondary.value += std::round(damage.secondary.value * (skill / 100.));
                g_game.addMagicEffect(target->getPosition(), CONST_ME_CRITICAL_DAMAGE);
            }
        }

        if (targetPlayer && caster->getPlayer() && targetPlayer->getSkull() != SKULL_BLACK) {
            damage.primary.value /= 2;
            damage.secondary.value /= 2;
        }
    }

    if (g_game.combatChangeHealth(caster, target, damage)) {
        CombatConditionFunc(caster, target, params, &damage);
        CombatDispelFunc(caster, target, params, nullptr);
    }
}

The if statement is what a I did, the else is default:
Code:
if (g_config.getBoolean(ConfigManager::CRITICAL_ON_ALL_WEAPONS)) {
            WeaponType_t weaponType = attackerPlayer->getWeapon(true)->getWeaponType();
            if (weaponType == WEAPON_SWORD || weaponType == WEAPON_AXE ||
                weaponType == WEAPON_CLUB || weaponType == WEAPON_DISTANCE) {
                chance = g_config.getNumber(ConfigManager::CRITICAL_CHANCE);
                if (chance != 0 && uniform_random(1, 100) <= chance) {
                    damage.primary.value += std::round(damage.primary.value * (g_config.getNumber(ConfigManager::CRITICAL_AMOUNT) / 100.));
                    damage.secondary.value += std::round(damage.secondary.value * (g_config.getNumber(ConfigManager::CRITICAL_AMOUNT) / 100.));
                    g_game.addMagicEffect(target->getPosition(), CONST_ME_CRITICAL_DAMAGE);
                }
            }
        }

        else {
            if (chance != 0 && uniform_random(1, 100) <= chance) {
                damage.primary.value += std::round(damage.primary.value * (skill / 100.));
                damage.secondary.value += std::round(damage.secondary.value * (skill / 100.));
                g_game.addMagicEffect(target->getPosition(), CONST_ME_CRITICAL_DAMAGE);
            }
        }

You can find where the rod and wand damage is being calculated and make a random chance to double it and add g_game.addMagicEffect(target->getPosition(), CONST_ME_CRITICAL_DAMAGE);
 
Last edited:
Back
Top