• 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 0.X Difference between hits

Natsume

Member
Joined
Aug 22, 2017
Messages
44
Reaction score
7
Hey guys, how can i change difference between hits?
difference is very high. it's even bigger when i change meleedamage (vocations.xml) to higher value.

for example:
1 hit - 20.000
2 hit - 15.000
3 hit - 40.000
4 hit - 1000
5 hit - 5000
 
sorry bro but its very hard to help you if you don't give any info, specially server version:
 
sorry bro but its very hard to help you if you don't give any info, specially server version:
Oh, sorry tfs 0.3.6 8.54 and also im pretty sure i saw post like that but that was 2 years ago and i cant find it. I remember i should do something in source i hope that kinda help.
 
Weapons.cpp

C++:
#include "otpch.h"

#include "definitions.h"
#include "weapons.h"
#include "combat.h"
#include "configmanager.h"
#include <sstream>

#include <libxml/xmlmemory.h>
#include <libxml/parser.h>

extern Game g_game;
extern Vocations g_vocations;
extern ConfigManager g_config;

Weapons::Weapons():
m_scriptInterface("Weapon Interface")
{
    m_scriptInterface.initState();
}

Weapons::~Weapons()
{
    clear();
}

const Weapon* Weapons::getWeapon(const Item* item) const
{
    if(!item){
        return NULL;
    }

    WeaponMap::const_iterator it = weapons.find(item->getID());
    
    if(it != weapons.end()){
        return it->second;
    }

    return NULL;
}

void Weapons::clear()
{
    WeaponMap::iterator it;
    for(it = weapons.begin(); it != weapons.end(); ++it){
        delete it->second;
    }

    weapons.clear();
}   

LuaScriptInterface& Weapons::getScriptInterface()
{
    return m_scriptInterface;   
}

std::string Weapons::getScriptBaseName()
{
    return "weapons";   
}

bool Weapons::loadDefaults()
{
    for(uint32_t i = 0; i < Item::items.size(); ++i){
        const ItemType& it = Item::items[i];

        if(it.id == 0 || weapons.find(it.id) != weapons.end()){
            continue;
        }

        if(it.weaponType != WEAPON_NONE){
            switch(it.weaponType){
                case WEAPON_AXE:
                case WEAPON_SWORD:
                case WEAPON_CLUB:
                {
                    WeaponMelee* weapon = new WeaponMelee(&m_scriptInterface);
                    weapon->configureWeapon(it);
                    weapons[it.id] = weapon;
                    break;
                }

                case WEAPON_AMMO:
                case WEAPON_DIST:
                {
                    if(it.weaponType == WEAPON_DIST && it.amuType != AMMO_NONE){
                        //distance weapons with ammunitions are configured seperatly
                        continue;
                    }

                    WeaponDistance* weapon = new WeaponDistance(&m_scriptInterface);
                    weapon->configureWeapon(it);
                    weapons[it.id] = weapon;
                    break;
                }
                default:
                {
                    break;
                }
            }
        }
    }

    return true;
}

Event* Weapons::getEvent(const std::string& nodeName)
{
    if(nodeName == "melee"){
        return new WeaponMelee(&m_scriptInterface);
    }
    else if(nodeName == "distance"){
        return new WeaponDistance(&m_scriptInterface);
    }
    else if(nodeName == "wand"){
        return new WeaponWand(&m_scriptInterface);
    }
    else{
        return NULL;
    }
}

bool Weapons::registerEvent(Event* event, xmlNodePtr p)
{
    Weapon* weapon = dynamic_cast<Weapon*>(event);
    //weapon->init();
    
    if(weapon){
        weapons[weapon->getID()] = weapon;
    }
    else{
        return false;
    }
    
    return true;
}

int32_t Weapons::getMaxWeaponDamage(int32_t attackSkill, int32_t attackValue)
{
    return ((attackSkill * attackValue)/20 + attackValue);
}

Weapon::Weapon(LuaScriptInterface* _interface) :
    Event(_interface)
{
    m_scripted = true;
    id = 0;
    level = 0;
    magLevel = 0;
    mana = 0;
    manaPercent = 0;
    soul = 0;
    exhaustion = false;
    premium = false;
    enabled = true;
    exhaustion = 0;
    range = 1;
    ammoAction = AMMOACTION_NONE;
}

Weapon::~Weapon()
{
    //
}

void Weapon::setCombatParam(const CombatParams& _params)
{
    m_scripted = false;
    params = _params;
}

bool Weapon::configureEvent(xmlNodePtr p)
{
    int intValue;
    std::string strValue;

    if(readXMLInteger(p, "id", intValue)){
         id = intValue;
    }
    else{
        return false;
    }

    if(readXMLInteger(p, "lvl", intValue) || readXMLInteger(p, "level", intValue)){
         level = intValue;
    }

    if(readXMLInteger(p, "maglv", intValue) || readXMLInteger(p, "maglevel", intValue)){
         magLevel = intValue;
    }

    if(readXMLInteger(p, "mana", intValue)){
         mana = intValue;
    }

    if(readXMLInteger(p, "manapercent", intValue)){
         manaPercent = intValue;
    }

    if(readXMLInteger(p, "soul", intValue)){
         soul = intValue;
    }

    if(readXMLInteger(p, "exhaustion", intValue)){
        exhaustion = (intValue == 1);
    }

    if(readXMLInteger(p, "prem", intValue)){
        premium = (intValue == 1);
    }

    if(readXMLInteger(p, "enabled", intValue)){
        enabled = (intValue == 1);
    }

    if(readXMLInteger(p, "range", intValue)){
        range = intValue;
    }

    if(readXMLString(p, "ammo", strValue)){
        if(xmlStrcmp((const xmlChar*)strValue.c_str(), (const xmlChar*)"move") == 0){
            ammoAction = AMMOACTION_MOVE;
        }
        else if(xmlStrcmp((const xmlChar*)strValue.c_str(), (const xmlChar*)"removecharge") == 0){
            ammoAction = AMMOACTION_REMOVECHARGE;
        }
        else if(xmlStrcmp((const xmlChar*)strValue.c_str(), (const xmlChar*)"removecount") == 0){
            ammoAction = AMMOACTION_REMOVECOUNT;
        }
    }

    xmlNodePtr vocationNode = p->children;
    while(vocationNode){
        if(xmlStrcmp(vocationNode->name,(const xmlChar*)"vocation") == 0){
            if(readXMLInteger(vocationNode, "id", intValue)){
                if(intValue != -1){
                    vocWeaponMap[intValue] = true;
                }
            }
        }
        
        vocationNode = vocationNode->next;
    }

    return true;
}

bool Weapon::configureWeapon(const ItemType& it)
{
    return true;
}

std::string Weapon::getScriptEventName()
{
    return "onUseWeapon";
}

bool Weapon::playerWeaponCheck(Player* player, Creature* target) const
{
    const Position& playerPos = player->getPosition();
    const Position& targetPos = target->getPosition();

    if(playerPos.z != targetPos.z){
        return false;
    }

    if( std::max(std::abs(playerPos.x - targetPos.x), std::abs(playerPos.y - targetPos.y)) > range){
        return false;
    }

    if(player->getAccessLevel() >= ACCESS_PROTECT){
        return true;
    }
    
    if(!enabled){
        return false;
    }

    if(player->getLevel() < level){
        return false;
    }

    if(player->getMagicLevel() < magLevel){
        return false;
    }

    if(player->getMana() < getManaCost(player)){
        return false;
    }

    if(player->getPlayerInfo(PLAYERINFO_SOUL) < soul){
        return false;
    }

    if(!vocWeaponMap.empty()){
        if(vocWeaponMap.find(player->getVocationId()) == vocWeaponMap.end()){
            return false;
        }
    }

    return true;
}

bool Weapon::useWeapon(Player* player, Item* item, Creature* target) const
{
    if(!playerWeaponCheck(player, target)){
        return false;
    }

    return internalUseWeapon(player, item, target);
}

bool Weapon::useFist(Player* player, Creature* target)
{
    const Position& playerPos = player->getPosition();
    const Position& targetPos = target->getPosition();

    if(Position::areInRange<1,1>(playerPos, targetPos)){
        int32_t attackStrength = player->getAttackStrength();
        int32_t attackSkill = player->getSkill(SKILL_FIST, SKILL_LEVEL);
        int32_t attackValue = 7;

        int32_t maxDamage = Weapons::getMaxWeaponDamage(attackSkill, attackValue);
        int32_t damage = -(random_range(0, maxDamage) * attackStrength) / 100;

        CombatParams params;
        params.combatType = COMBAT_PHYSICALDAMAGE;
        params.blockedByArmor = true;
        params.blockedByShield = true;
        Combat::doCombatHealth(player, target, damage, damage, params);

        if(player->getAccessLevel() < ACCESS_PROTECT){
            player->addSkillAdvance(SKILL_FIST, player->getSkillPoint());
        }

        return true;
    }

    return false;
}

bool Weapon::internalUseWeapon(Player* player, Item* item, Creature* target) const
{
    if(m_scripted){
        LuaVariant var;
        var.type = VARIANT_NUMBER;
        var.number = target->getID();
        executeUseWeapon(player, var);
    }
    else{
        int32_t damage = getWeaponDamage(player, item);
        Combat::doCombatHealth(player, target, damage, damage, params);
    }

    onUsedWeapon(player, item, target->getTile());
    return true;
}

bool Weapon::internalUseWeapon(Player* player, Item* item, Tile* tile) const
{
    if(m_scripted){
        LuaVariant var;
        var.type = VARIANT_TARGETPOSITION;
        var.pos = tile->getPosition();
        executeUseWeapon(player, var);
    }
    else{
        Combat::postCombatEffects(player, tile->getPosition(), params);
        g_game.addMagicEffect(tile->getPosition(), NM_ME_PUFF);
    }

    onUsedWeapon(player, item, tile);
    return true;
}

void Weapon::onUsedWeapon(Player* player, Item* item, Tile* destTile) const
{
    if(ammoAction == AMMOACTION_REMOVECOUNT){
        int32_t newCount = std::max(0, item->getItemCount() - 1);
        #ifdef __XID_CVS_MODS__
        if(g_config.getString(ConfigManager::REMOVE_AMMUNATION) == "yes")
        #endif
        g_game.transformItem(item, item->getID(), newCount);
    }
    else if(ammoAction == AMMOACTION_REMOVECHARGE){
        int32_t newCharge = std::max(0, item->getItemCharge() - 1);
        #ifdef __XID_CVS_MODS__
        if(g_config.getString(ConfigManager::REMOVE_AMMUNATION) == "yes")
        #endif
        g_game.transformItem(item, item->getID(), newCharge);
    }
    else if(ammoAction == AMMOACTION_MOVE){
        g_game.internalMoveItem(item->getParent(), destTile, INDEX_WHEREEVER, item, 1, FLAG_NOLIMIT);
    }
    else if(item->hasCharges()){
        int32_t newCharge = std::max(0, item->getItemCharge() - 1);
        #ifdef __XID_CVS_MODS__
        if(g_config.getString(ConfigManager::REMOVE_AMMUNATION) == "yes")
        #endif
        g_game.transformItem(item, item->getID(), newCharge);
    }

    if(player->getAccessLevel() >= ACCESS_PROTECT){
        return;
    }

    skills_t skillType;
    uint32_t skillPoint = 0;
    if(getSkillType(player, item, skillType, skillPoint)){
        player->addSkillAdvance(skillType, skillPoint);
    }

    if(exhaustion){
        player->addExhaustionTicks();
    }

    int32_t manaCost = getManaCost(player);

    if(manaCost > 0){
        player->changeMana(-manaCost);
        player->addManaSpent(manaCost);
    }

    int32_t soulCost = soul;

    if(soulCost > 0){
        player->changeSoul(-soulCost);
    }
}

int32_t Weapon::getManaCost(const Player* player) const
{
    if(mana != 0){
        return mana;
    }

    if(manaPercent != 0){
        int32_t maxMana = player->getMaxMana();
        int32_t manaCost = (maxMana * manaPercent) / 100;
        return manaCost;
    }

    return 0;
}


bool Weapon::executeUseWeapon(Player* player, const LuaVariant& var) const
{
    //onUseWeapon(cid, var)
    if(m_scriptInterface->reserveScriptEnv()){
        ScriptEnviroment* env = m_scriptInterface->getScriptEnv();

        #ifdef __DEBUG_LUASCRIPTS__
        std::stringstream desc;
        desc << "onUseWeapon - " << player->getName();
        env->setEventDesc(desc.str());
        #endif
    
        env->setScriptId(m_scriptId, m_scriptInterface);
        env->setRealPos(player->getPosition());
    
        lua_State* L = m_scriptInterface->getLuaState();
    
        uint32_t cid = env->addThing(player);

        LuaVariant* pVar = new LuaVariant(var);
        uint32_t variant = env->addVariant(pVar);

        m_scriptInterface->pushFunction(m_scriptId);
        lua_pushnumber(L, cid);
        lua_pushnumber(L, variant);

        int32_t result = m_scriptInterface->callFunction(2);
        m_scriptInterface->releaseScriptEnv();
        
        return (result == LUA_NO_ERROR);
    }
    else{
        return false;
    }
}


WeaponMelee::WeaponMelee(LuaScriptInterface* _interface) :
    Weapon(_interface)
{
    //
}

bool WeaponMelee::configureEvent(xmlNodePtr p)
{
    if(!Weapon::configureEvent(p)){
        return false;
    }

    return true;
}

bool WeaponMelee::configureWeapon(const ItemType& it)
{
    m_scripted = false;
    
    params.blockedByArmor = true;
    params.blockedByShield = true;
    params.combatType = COMBAT_PHYSICALDAMAGE;
    return true;
}

void WeaponMelee::onUsedWeapon(Player* player, Item* item, Tile* destTile) const
{
    Weapon::onUsedWeapon(player, item, destTile);
}

bool WeaponMelee::getSkillType(const Player* player, const Item* item,
    skills_t& skill, uint32_t& skillpoint) const
{
    skillpoint = player->getSkillPoint();
    WeaponType_t weaponType = item->getWeaponType();

    switch(weaponType){
        case WEAPON_SWORD:
        {
            skill = SKILL_SWORD;
            return true;
            break;
        }

        case WEAPON_CLUB:
        {
            skill = SKILL_CLUB;
            return true;
            break;
        }

        case WEAPON_AXE:
        {
            skill = SKILL_AXE;
            return true;
            break;
        }
        default:
        {
            return false;
            break;
        }
    }
}

int32_t WeaponMelee::getWeaponDamage(const Player* player, const Item* item) const
{
    WeaponType_t weaponType = item->getWeaponType();

    int32_t attackSkill = 0;

    switch(weaponType){
        case WEAPON_SWORD:
            attackSkill = player->getSkill(SKILL_SWORD, SKILL_LEVEL);
            break;

        case WEAPON_CLUB:
        {
            attackSkill = player->getSkill(SKILL_CLUB, SKILL_LEVEL);
            break;
        }

        case WEAPON_AXE:
        {
            attackSkill = player->getSkill(SKILL_AXE, SKILL_LEVEL);
            break;
        }
        default:
        {
            break;
        }
    }

    int32_t attackStrength = player->getAttackStrength();
    int32_t attackValue = item->getAttack();
    int32_t maxDamage = Weapons::getMaxWeaponDamage(attackSkill, attackValue);
    return -(random_range(0, maxDamage) * attackStrength) / 100;
}

WeaponDistance::WeaponDistance(LuaScriptInterface* _interface) :
    Weapon(_interface)
{
    hitChance = 50;
    ammuAttackValue = 20;
}

bool WeaponDistance::configureEvent(xmlNodePtr p)
{
    if(!Weapon::configureEvent(p)){
        return false;
    }

    int intValue;

    if(readXMLInteger(p, "hitchance", intValue)){
        hitChance = intValue;
    }
    
    return true;
}

bool WeaponDistance::configureWeapon(const ItemType& it)
{
    m_scripted = false;

    ammoAction = AMMOACTION_REMOVECOUNT;
    params.blockedByArmor = true;
    params.combatType = COMBAT_PHYSICALDAMAGE;

    id = it.id;
    ammuAttackValue = it.attack;
    params.distanceEffect = it.shootType;

    switch(it.amuType){
        case AMMO_ARROW:
        {
            hitChance = 80;
            range = 5;
            break;
        }

        case AMMO_BOLT:
        {
            hitChance = 90;
            range = 6;
            break;
        }

        default:
            hitChance = 50;
            range = 4;
            break;
    }

    return true;
}

bool WeaponDistance::playerWeaponCheck(Player* player, Creature* target) const
{
    if(!Weapon::playerWeaponCheck(player, target)){
        return false;
    }

    return true;
}

bool WeaponDistance::useWeapon(Player* player, Item* item, Creature* target) const
{
    if(!playerWeaponCheck(player, target)){
        return false;
    }

    Position destPos = target->getPosition();
    Tile* destTile = target->getTile();

    if(rand() % 100 < hitChance){
        Weapon::internalUseWeapon(player, item, target);
    }
    else{
        //miss target
        int dx = random_range(-1, 1);
        int dy = random_range(-1, 1);

        destTile = g_game.getTile(destPos.x + dx, destPos.y + dy, destPos.z);
        
        if(destTile && !destTile->hasProperty(BLOCKINGANDNOTMOVEABLE)){
            destPos.x += dx;
            destPos.y += dy;
        }
        else{
            destTile = target->getTile();
        }

        Weapon::internalUseWeapon(player, item, destTile);
    }

    return true;
}

void WeaponDistance::onUsedWeapon(Player* player, Item* item, Tile* destTile) const
{
    Weapon::onUsedWeapon(player, item, destTile);
}

int32_t WeaponDistance::getWeaponDamage(const Player* player, const Item* item) const
{
    int32_t attackSkill = player->getSkill(SKILL_DIST, SKILL_LEVEL);

    int32_t attackStrength = player->getAttackStrength();
    int32_t maxDamage = Weapons::getMaxWeaponDamage(attackSkill, ammuAttackValue);
    return -(random_range(0, maxDamage) * attackStrength) / 100;
}

bool WeaponDistance::getSkillType(const Player* player, const Item* item,
    skills_t& skill, uint32_t& skillpoint) const
{
    skill = SKILL_DIST;
    skillpoint = player->getSkillPoint();
    return true;
}

WeaponWand::WeaponWand(LuaScriptInterface* _interface) :
    Weapon(_interface)
{
    minChange = 0;
    maxChange = 0;
}

bool WeaponWand::configureEvent(xmlNodePtr p)
{
    if(!Weapon::configureEvent(p)){
        return false;
    }

    int intValue;

    if(readXMLInteger(p, "min", intValue)){
        minChange = intValue;
    }

    if(readXMLInteger(p, "max", intValue)){
        maxChange = intValue;
    }

    return true;
}

int32_t WeaponWand::getWeaponDamage(const Player* player, const Item* item) const
{
    return random_range(-minChange, -maxChange);
}
 
line 580 and line 694
Code:
random_range(0, maxDamage)
you would have to change 0 to some formula generated minimal damage (see how maxDamage is done) or something like maxDamage * 0.7

after that you have to compile
 
Changing formula:
Code:
random_range(0, maxDamage)
will heavily change average damage per second of knights and paladins.
If you set it to constant value (remove 'random_range'):
Code:
maxDamage
hits will be still random (less, but still). There is second part of that formula: defense/armor of attacked monster/player. It's also random value 'from 0 to armor value':
 
Back
Top