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

Solved CUSTOM SKILLS have strange values

maxsaenz95

New Member
Joined
May 18, 2010
Messages
35
Reaction score
4
TFS 1.2
OTCLIENTV8 (10.98)
database added skills to player table (skill and skill tries)
schema.sql added skills and skill tries also
vocations.xml new skills and skill id were added

LUA:
<?xml version="1.0" encoding="UTF-8"?>
<vocations>
    <vocation id="0" clientid="0" name="None" description="none" gaincap="0" gainhp="5" gainmana="5" gainhpticks="6" gainhpamount="1" gainmanaticks="6" gainmanaamount="1" manamultiplier="4.0" attackspeed="2000" basespeed="220" soulmax="100" gainsoulticks="120" fromvoc="0">
        <formula meleeDamage="1.0" distDamage="1.0" defense="1.0" armor="1.0" />
        <skill id="0" multiplier="1.5" />
        <skill id="1" multiplier="2.0" />
        <skill id="2" multiplier="2.0" />
        <skill id="3" multiplier="2.0" />
        <skill id="4" multiplier="2.0" />
        <skill id="5" multiplier="1.5" />
        <skill id="6" multiplier="1.1" />
        <skill id="7" multiplier="2.0" />
        <skill id="8" multiplier="2.0" />
        <skill id="9" multiplier="2.0" />
        <skill id="10" multiplier="2.0" />
    </vocation>

Code:
    protected:
        friend class Vocations;

        std::map<uint32_t, uint64_t> cacheMana;
        std::map<uint32_t, uint32_t> cacheSkill[SKILL_LAST + 1];

        std::string name = "none";
        std::string description;

        float skillMultipliers[SKILL_LAST + 1] = {1.5f, 2.0f, 2.0f, 2.0f, 2.0f, 1.5f, 1.1f, 2.0f, 2.0f , 2.0f , 2.0f }; // new skills added here
        float manaMultiplier = 4.0f;

        uint32_t gainHealthTicks = 6;
        uint32_t gainHealthAmount = 1;
        uint32_t gainManaTicks = 6;
        uint32_t gainManaAmount = 1;
        uint32_t gainCap = 500;
        uint32_t gainMana = 5;
        uint32_t gainHP = 5;
        uint32_t fromVocation = VOCATION_NONE;
        uint32_t attackSpeed = 1500;
        uint32_t baseSpeed = 220;
        uint16_t id;

        uint16_t gainSoulTicks = 120;

        uint8_t soulMax = 100;
        uint8_t clientId = 0;

        static uint32_t skillBase[SKILL_LAST + 1];
};

Code:
...
    registerEnum(SKILL_ATTACK)
    registerEnum(SKILL_DEFENCE)
    registerEnum(SKILL_SPATTACK)
    registerEnum(SKILL_SPDEFENCE)
    registerEnum(SKILL_FISHING)
...
    registerEnum(CONDITION_PARAM_SKILL_ATTACK)
    registerEnum(CONDITION_PARAM_SKILL_DEFENCE)
    registerEnum(CONDITION_PARAM_SKILL_SPATTACK)
    registerEnum(CONDITION_PARAM_SKILL_SPDEFENCE)
    registerEnum(CONDITION_PARAM_SKILL_FISHING)
...
    registerEnum(CONDITION_PARAM_SKILL_ATTACKPERCENT)
    registerEnum(CONDITION_PARAM_SKILL_DEFENCEPERCENT)
    registerEnum(CONDITION_PARAM_SKILL_SPATTACKPERCENT)
    registerEnum(CONDITION_PARAM_SKILL_SPDEFENCEPERCENT)
    registerEnum(CONDITION_PARAM_SKILL_FISHINGPERCENT)

Code:
case CONDITION_PARAM_SKILL_FISHING: {
    skills[SKILL_FISHING] = value;
    return true;
}

case CONDITION_PARAM_SKILL_FISHINGPERCENT: {
    skillsPercent[SKILL_FISHING] = value;
    return true;
}

case CONDITION_PARAM_SKILL_ATTACK: {
    skills[SKILL_ATTACK] = value;
    return true;
}

case CONDITION_PARAM_SKILL_ATTACKPERCENT: {
    skillsPercent[SKILL_ATTACK] = value;
    return true;
}

case CONDITION_PARAM_SKILL_DEFENCE: {
    skills[SKILL_DEFENCE] = value;
    return true;
}

case CONDITION_PARAM_SKILL_DEFENCEPERCENT: {
    skillsPercent[SKILL_DEFENCE] = value;
    return true;
}

case CONDITION_PARAM_SKILL_SPATTACK: {
    skills[SKILL_SPATTACK] = value;
    return true;
}

case CONDITION_PARAM_SKILL_SPATTACKPERCENT: {
    skillsPercent[SKILL_SPATTACK] = value;
    return true;
}

case CONDITION_PARAM_SKILL_SPDEFENCE: {
    skills[SKILL_SPDEFENCE] = value;
    return true;
}

case CONDITION_PARAM_SKILL_SPDEFENCEPERCENT: {
    skillsPercent[SKILL_SPDEFENCE] = value;
    return true;
}

Code:
...
CONDITION_PARAM_SKILL_ATTACK = 25,
CONDITION_PARAM_SKILL_DEFENCE = 26,
CONDITION_PARAM_SKILL_SPATTACK = 27,
CONDITION_PARAM_SKILL_SPDEFENCE = 28,
CONDITION_PARAM_SKILL_SHIELD = 29,
CONDITION_PARAM_SKILL_FISHING = 30,
...
    CONDITION_PARAM_SKILL_ATTACKPERCENT = 46,
    CONDITION_PARAM_SKILL_DEFENCEPERCENT = 47,
    CONDITION_PARAM_SKILL_SPATTACKPERCENT = 48,
    CONDITION_PARAM_SKILL_SPDEFENCEPERCENT = 49,
    CONDITION_PARAM_SKILL_SHIELDPERCENT = 50,
    CONDITION_PARAM_SKILL_FISHINGPERCENT = 51,
    ...
enum skills_t : uint8_t {
...
    SKILL_FISHING = 6,
    SKILL_ATTACK = 7,
    SKILL_DEFENCE = 8,
    SKILL_SPATTACK = 9,
    SKILL_SPDEFENCE = 10,
...
    SKILL_FIRST = SKILL_FIST,
    SKILL_LAST = SKILL_SPDEFENCE
};


Code:
bool IOLoginData::loadPlayerById(Player* player, uint32_t id)
{
    std::ostringstream query;
    query << "SELECT `id`, `name`, `account_id`, `group_id`, `sex`, `vocation`, `experience`, `level`, `maglevel`, `health`, `healthmax`, `blessings`, `mana`, `manamax`, `manaspent`, `soul`, `lookbody`, `lookfeet`, `lookhead`, `looklegs`, `looktype`, `lookaddons`, `posx`, `posy`, `posz`, `cap`, `lastlogin`, `lastlogout`, `lastip`, `conditions`, `skulltime`, `skull`, `town_id`, `balance`, `offlinetraining_time`, `offlinetraining_skill`, `stamina`, `skill_fist`, `skill_fist_tries`, `skill_club`, `skill_club_tries`, `skill_sword`, `skill_sword_tries`, `skill_axe`, `skill_axe_tries`, `skill_dist`, `skill_dist_tries`, `skill_shielding`, `skill_shielding_tries`, `skill_fishing`, `skill_fishing_tries`, `skill_attack`, `skill_attack_tries`, `skill_defence`, `skill_defence_tries`, `skill_spattack`, `skill_spattack_tries`, `skill_spdefence`, `skill_spdefence_tries` FROM `players` WHERE `id` = " << id;
    return loadPlayer(player, Database::getInstance()->storeQuery(query.str()));
}

bool IOLoginData::loadPlayerByName(Player* player, const std::string& name)
{
    Database* db = Database::getInstance();
    std::ostringstream query;
    query << "SELECT `id`, `name`, `account_id`, `group_id`, `sex`, `vocation`, `experience`, `level`, `maglevel`, `health`, `healthmax`, `blessings`, `mana`, `manamax`, `manaspent`, `soul`, `lookbody`, `lookfeet`, `lookhead`, `looklegs`, `looktype`, `lookaddons`, `posx`, `posy`, `posz`, `cap`, `lastlogin`, `lastlogout`, `lastip`, `conditions`, `skulltime`, `skull`, `town_id`, `balance`, `offlinetraining_time`, `offlinetraining_skill`, `stamina`, `skill_fist`, `skill_fist_tries`, `skill_club`, `skill_club_tries`, `skill_sword`, `skill_sword_tries`, `skill_axe`, `skill_axe_tries`, `skill_dist`, `skill_dist_tries`, `skill_shielding`, `skill_shielding_tries`, `skill_fishing`, `skill_fishing_tries`, `skill_attack`, `skill_attack_tries`, `skill_defence`, `skill_defence_tries`, `skill_spattack`, `skill_spattack_tries`, `skill_spdefence`, `skill_spdefence_tries` FROM `players` WHERE `name` = " << db->escapeString(name);
    return loadPlayer(player, db->storeQuery(query.str()));
...
    static const std::string skillNames[] = {"skill_fist", "skill_club", "skill_sword", "skill_axe", "skill_dist", "skill_shielding", "skill_fishing", "skill_attack", "skill_defence", "skill_spattack", "skill_spdefence"};
    static const std::string skillNameTries[] = {"skill_fist_tries", "skill_club_tries", "skill_sword_tries", "skill_axe_tries", "skill_dist_tries", "skill_shielding_tries", "skill_fishing", "skill_attack", "skill_defence", "skill_spattack", "skill_spdefence"};
    static constexpr size_t size = sizeof(skillNames) / sizeof(std::string);
    for (uint8_t i = 0; i < size; ++i) {
        uint16_t skillLevel = result->getNumber<uint16_t>(skillNames[i]);
        uint64_t skillTries = result->getNumber<uint64_t>(skillNameTries[i]);
        uint64_t nextSkillTries = player->vocation->getReqSkillTries(i, skillLevel + 1);
        if (skillTries > nextSkillTries) {
            skillTries = 0;
        }

        player->skills[i].level = skillLevel;
        player->skills[i].tries = skillTries;
        player->skills[i].percent = Player::getPercentLevel(skillTries, nextSkillTries);
    }
}
...
    query << "`skill_fishing` = " << player->skills[SKILL_FISHING].level << ',';
    query << "`skill_fishing_tries` = " << player->skills[SKILL_FISHING].tries << ',';
    query << "`skill_attack` = " << player->skills[SKILL_ATTACK].level << ',';
    query << "`skill_attack_tries` = " << player->skills[SKILL_ATTACK].tries << ',';
    query << "`skill_defence` = " << player->skills[SKILL_DEFENCE].level << ',';
    query << "`skill_defence_tries` = " << player->skills[SKILL_DEFENCE].tries << ',';
    query << "`skill_spattack` = " << player->skills[SKILL_SPATTACK].level << ',';
    query << "`skill_spattack_tries` = " << player->skills[SKILL_SPATTACK].tries << ',';
    query << "`skill_spdefence` = " << player->skills[SKILL_SPDEFENCE].level << ',';
    query << "`skill_spdefence_tries` = " << player->skills[SKILL_SPDEFENCE].tries << ',';

Code:
 ...
            it.getAbilities().skills[SKILL_ATTACK] = pugi::cast<int32_t>(valueAttribute.value());
        }else if (tmpStrValue == "skillattack") {
            it.getAbilities().skills[SKILL_DEFENCE] = pugi::cast<int32_t>(valueAttribute.value());
        } else if (tmpStrValue == "skilldefence") {
            it.getAbilities().skills[SKILL_SPATTACK] = pugi::cast<int32_t>(valueAttribute.value());
        }else if (tmpStrValue == "skillspattack") {
            it.getAbilities().skills[SKILL_SPDEFENCE] = pugi::cast<int32_t>(valueAttribute.value());
        }else if (tmpStrValue == "skillspdefence") {
            it.getAbilities().skills[SKILL_FISHING] = pugi::cast<int32_t>(valueAttribute.value());
        } else if (tmpStrValue == "skillshield") {

Code:
std::string getSkillName(uint8_t skillid)
{
    switch (skillid) {
       ....

        case SKILL_FISHING:
            return "fishing";

        case SKILL_ATTACK:
            return "attack";

        case SKILL_DEFENCE:
            return "defence";

        case SKILL_SPATTACK:
            return "spattack";

        case SKILL_SPDEFENCE:
            return "spdefence";

...

        default:
            return "unknown";
    }
}

Code:
/* Vocation stat gains per level
        - Ordered by vocation ID
        - Currently used for admin_skills page. */
$config['vocations_gain'] = array(
    0 => array(
        'hp' => 5,
        'mp' => 5,
        'cap' => 0,
        'attack' => 2, // adjust the gain value as needed
        'defence' => 2, // adjust the gain value as needed
        'spattack' => 2, // adjust the gain value as needed
        'spdefence' => 2, // adjust the gain value as needed
    ),


...

$config['player'] = array(
        'base' => array(
            'level' => 1,
            'health' => 925,
            'mana' => 40,
            'cap' => 6,
        'attack' => 2, // adjust the gain value as needed
        'defence' => 2, // adjust the gain value as needed
        'spattack' => 2, // adjust the gain value as needed
        'spdefence' => 2, // adjust the gain value as needed
            'soul' => 100
        ),
        // health, mana cap etc are calculated with $config['vocations_gain'] and 'base' values of $config['player']
        'create' => array(
            'level' => 8,
            'novocation' => array( // vocation id 0 (No vocation) special settings
                'level' => 1, // Level
                'forceTown' => true,
                'townId' => 34
            ),
            'skills' => array( // See $config['vocations'] for proper vocation names of these IDs
                // No vocation
                0 => array(
...
                    'fishing' => 1,
                    'attack' => 1,
                    'defence' => 1,
                    'spattack' => 1,
                    'spdefence' => 1,
                ),
...

Code:
<?php require_once 'engine/init.php'; include 'layout/overall/header.php';
protect_page();
admin_only($user_data);

// PREP: Create a function that echos player skills
function playerSkill($skills, $id) {
    if (!$skills) return 0;
    else {
        return $skills[$id]['value'];
    }
}

// UPDATE SKILLS POST
if (isset($_POST['pid']) && (int)$_POST['pid'] > 0) {
    $pid = (int)$_POST['pid'];
    if ($config['TFSVersion']!= 'TFS_10') $status = user_is_online($pid);
    else $status = user_is_online_10($pid);

    if (!$status) {
        // New player level
        $level = (int)$_POST['level'];
        
        // Fetch stat gain for vocation
        $statgain = $config['vocations_gain'][(int)$_POST['vocation']];
        $playercnf = $config['player'];

        /*
        if ((int)$_POST['vocation']!== 0) {
            // Fetch base level and stats:
            $baselevel = $config['level'];
            $basehealth = $config['health'];
            $basemana = $config['mana'];
            $basecap = $config['cap'];
            $baseattack = $config['attack'];
            $basedefence = $config['defence'];
            $basespattack = $config['spattack'];
            $basespdefence = $config['spdefence'];
        } else { // No vocation stats
            // Fetch base level and stats:
            $baselevel = $config['nvlevel'];
            $basehealth = $config['nvHealth'];
            $basemana = $config['nvMana'];
            $basecap = $config['nvCap'];
            $baseattack = $config['nvattack'];
            $basedefence = $config['nvdefence'];
            $basespattack = $config['nvspattack'];
            $basespdefence = $config['nvspdefence'];
        }
        */
        
        $LevelsFromBase = $level - $playercnf['base']['level'];
        $newhp = $playercnf['base']['health'] + ($statgain['hp'] * $LevelsFromBase);
        $newmp = $playercnf['base']['mana'] + ($statgain['mp'] * $LevelsFromBase);
        $newcap = $playercnf['base']['cap'] + ($statgain['cap'] * $LevelsFromBase);
        $newattack = $playercnf['base']['attack'] + ($statgain['attack'] * $LevelsFromBase);
        $newdefence = $playercnf['base']['defence'] + ($statgain['defence'] * $LevelsFromBase);
        $newspattack = $playercnf['base']['spattack'] + ($statgain['spattack'] * $LevelsFromBase);
        $newspdefence = $playercnf['base']['spdefence'] + ($statgain['spdefence'] * $LevelsFromBase);

// Calibrate hp/mana/cap
if ($config['TFSVersion'] != 'TFS_10') {
...
    mysql_update("UPDATE `player_skills` SET `value`='". (int)$_POST['fish'] ."' WHERE `player_id`='$pid' AND `skillid`='6' LIMIT 1;");
    mysql_update("UPDATE `player_skills` SET `value`='". (int)$_POST['attack'] ."' WHERE `player_id`='$pid' AND `skillid`='7' LIMIT 1;");
    mysql_update("UPDATE `player_skills` SET `value`='". (int)$_POST['defence'] ."' WHERE `player_id`='$pid' AND `skillid`='8' LIMIT 1;");
    mysql_update("UPDATE `player_skills` SET `value`='". (int)$_POST['spattack'] ."' WHERE `player_id`='$pid' AND `skillid`='9' LIMIT 1;");
    mysql_update("UPDATE `player_skills` SET `value`='". (int)$_POST['spdefence'] ."' WHERE `player_id`='$pid' AND `skillid`='10' LIMIT 1;");
    mysql_update("UPDATE `players` SET `maglevel`='". (int)$_POST['magic'] ."' WHERE `id`='$pid' LIMIT 1;");
    mysql_update("UPDATE `players` SET `vocation`='". (int)$_POST['vocation'] ."' WHERE `id`='$pid' LIMIT 1;");
    mysql_update("UPDATE `players` SET `level`='". $level ."' WHERE `id`='$pid' LIMIT 1;");
    mysql_update("UPDATE `players` SET `experience`='". level_to_experience($level) ."' WHERE `id`='$pid' LIMIT 1;");
// Update HP/mana/cap accordingly to level & vocation
mysql_update("UPDATE `players` SET `health`='". $newhp ."', `healthmax`='". $newhp ."', `mana`='". $newmp ."', `manamax`='". $newmp ."', `cap`='". $newcap .", `attack`='". $newattack .", `defence`='". $newdefence .", `spattack`='". $newspattack .", `spdefence`='". $newspdefence ."' WHERE `id`='$pid' LIMIT 1;");
        } else {
            mysql_update("UPDATE `players` SET `health`='". $newhp ."', `healthmax`='". $newhp ."', `mana`='". $newmp ."', `manamax`='". $newmp ."', `cap`='". $newcap ."', `vocation`='". $newcap .", `attack`='". $newattack .", `defence`='". $newdefence .", `spattack`='". $newspattack .", `SpDefence`='". $newspdefense .(int)$_POST['vocation'] ."', `skill_fist`='". (int)$_POST['fist'] ."', `skill_club`='". (int)$_POST['club'] ."', `skill_sword`='". (int)$_POST['sword'] ."', `skill_axe`='". (int)$_POST['axe'] ."', `skill_dist`='". (int)$_POST['dist'] ."', `skill_shielding`='". (int)$_POST['shield'] ."', `skill_fishing`='". (int)$_POST['fish'] ."', `maglevel`='". (int)$_POST['magic'] ."', `level`='". $level ."', `experience`='". level_to_experience($level) ."' WHERE `id`='$pid' LIMIT 1;");
        }
?>
<h1>Player skills updated!</h1>
<?php
} else {
    ?>
    <font color="red" size="7">Player must be offline!</font>
    <?php
}

// Stage 1: Fetch name
if (isset($_GET['name'])) {
    $name = getValue($_GET['name']);
} else $name = false;
//if (isset($_POST['name'])) $name = getValue($_POST['name']);

// Stage 2: Fetch user id and skills
$skills = false;
$pid = 0;
if ($name !== false) {
    if (user_character_exist($name)) {
        $pid = user_character_id($name);

        if ($config['TFSVersion'] != 'TFS_10') {
            $skills = mysql_select_multi("SELECT `value` FROM `player_skills` WHERE `player_id`='$pid' LIMIT 7;");
            $player = mysql_select_single("SELECT `maglevel`, `level`, `vocation` FROM `players` WHERE `id`='$pid' LIMIT 1;");
            $skills[] = array('value' => $player['maglevel']);
            $skills[] = array('value' => $player['level']);
            $skills[] = array('value' => $player['vocation']);
        } else {
            $player = mysql_select_single("SELECT `skill_fist`, `skill_club`, `skill_sword`, `skill_axe`, `skill_dist`, `skill_shielding`, `skill_fishing`, `skill_attack`, `skill_defence`, `skill_spattack`, `skill_spdefence`, `maglevel`, `level`, `vocation` FROM `players` WHERE `id`='$pid' LIMIT 1;");
            $skills = array(
...
                6 => array('value' => $player['skill_fishing']),
                7 => array('value' => $player['skill_attack']),
                8 => array('value' => $player['skill_defence']),
                9 => array('value' => $player['skill_spattack']),
                10 => array('value' => $player['skill_spdefence']),
                11 => array('value' => $player['maglevel']),
                12 => array('value' => $player['level']),
                13 => array('value' => $player['vocation'])
            );
        }

        //data_dump($skills, false, "Player skills");
    } else $name = false;
}

Code:
-- @docconsts @{

...
Skill = {
  Fist = 0,
  Club = 1,
  Sword = 2,
  Axe = 3,
  Distance = 4,
  Shielding = 5,
  Fishing = 6,
  Attack = 7,
  Defence = 8,
  SpAttack = 9,
  SpDefence = 10,
  CriticalChance = 11,
  CriticalDamage = 12,
  LifeLeechChance = 13,
  LifeLeechAmount = 14,
  ManaLeechChance = 15,
  ManaLeechAmount = 16
}

Code:
    SkillButton
      id: skillId6
      SkillNameLabel
        !text: tr('Fishing')
      SkillValueLabel
      SkillPercentPanel

    SkillButton
      id: skillId7
      SkillNameLabel
        !text: tr('Attack')
      SkillValueLabel
      SkillPercentPanel

    SkillButton
      id: skillId8
      SkillNameLabel
        !text: tr('Defence')
      SkillValueLabel
      SkillPercentPanel

    SkillButton
      id: skillId9
      SkillNameLabel
        !text: tr('SpAttack')
      SkillValueLabel
      SkillPercentPanel

    SkillButton
      id: skillId10
      SkillNameLabel
        !text: tr('SpDefence')
      SkillValueLabel
      SkillPercentPanel

    SmallSkillButton
      id: skillId11
      SkillNameLabel
        !text: tr('Critical Hit Chance')
      SkillValueLabel

    SmallSkillButton
      id: skillId12
      SkillNameLabel
        !text: tr('Critical Hit Damage')
      SkillValueLabel

    SmallSkillButton
      id: skillId13
      SkillNameLabel
        !text: tr('Life Leech Chance')
      SkillValueLabel

    SmallSkillButton
      id: skillId14
      SkillNameLabel
        !text: tr('Life Leech Amount')
      SkillValueLabel

    SmallSkillButton
      id: skillId15
      SkillNameLabel
        !text: tr('Life Leech Chance')
      SkillValueLabel

    SmallSkillButton
      id: skillId16
      SkillNameLabel
        !text: tr('Life Leech Amount')
      SkillValueLabel

New skills:
ATTACK
DEFENCE
SPATTACK
SPDEFENCE

all skills are configured the same in src files (based from fishing skill)
1722790589909.webp
1rst problem : attack skill displays expected and assigned value (10) but others display strange values wich i dont know where they are coming from but all skills were configured the same so i would expect all to show errors ?

1722790261243.webpspdefence value 2560-2550?? value asigned is 10

1722790296222.webpspattack shows 10-10 and value shows as red colored?

1722790329409.webpcritical hit chance shows as 2560 i didnt even mess with this one???

2nd problem : addskill command not updating player skills (original code from tfs1.2 just added new skills wich i added on src files and compiled no errors)
Code:
local function getSkillId(skillName)
    if skillName == "club" then
        return SKILL_CLUB
    elseif skillName == "sword" then
        return SKILL_SWORD
    elseif skillName == "axe" then
        return SKILL_AXE
            elseif skillName == "attack" then
        return SKILL_ATTACK
            elseif skillName == "defence" then
        return SKILL_DEFENCE
            elseif skillName == "spattack" then
        return SKILL_SPATTACK
            elseif skillName == "spdefence" then
        return SKILL_SPDEFENCE
    elseif skillName:sub(1, 4) == "dist" then
        return SKILL_DISTANCE
    elseif skillName:sub(1, 6) == "shield" then
        return SKILL_SHIELD
    elseif skillName:sub(1, 4) == "fish" then
        return SKILL_FISHING
    else
        return SKILL_FIST
    end
end

local function getExpForLevel(level)
    level = level - 1
    return ((50 * level * level * level) - (150 * level * level) + (400 * level)) / 3
end

function onSay(player, words, param)
    if not player:getGroup():getAccess() then
        return true
    end

    if player:getAccountType() < ACCOUNT_TYPE_GOD then
        return false
    end

    local split = param:split(",")
    if split[2] == nil then
        player:sendCancelMessage("Insufficient parameters.")
        return false
    end

    local target = Player(split[1])
    if target == nil then
        player:sendCancelMessage("A player with that name is not online.")
        return false
    end

    -- Trim left
    split[2] = split[2]:gsub("^%s*(.-)$", "%1")

    local count = 1
    if split[3] ~= nil then
        count = tonumber(split[3])
    end

    local ch = split[2]:sub(1, 1)
    for i = 1, count do
        if ch == "l" or ch == "e" then
            target:addExperience(getExpForLevel(target:getLevel() + 1) - target:getExperience(), false)
        elseif ch == "m" then
            target:addManaSpent(target:getVocation():getRequiredManaSpent(target:getBaseMagicLevel() + 1) - target:getManaSpent())
        else
            local skillId = getSkillId(split[2])
            target:addSkillTries(skillId, target:getVocation():getRequiredSkillTries(skillId, target:getSkillLevel(skillId) + 1) - target:getSkillTries(skillId))
        end
    end
    return false
end
 

Attachments

  • 1722790460258.webp
    1722790460258.webp
    25.2 KB · Views: 10 · VirusTotal
Back
Top