jRiddick
Rustacian
- Joined
- Oct 11, 2011
- Messages
- 14
- Reaction score
- 7
Attributes MOD
jRiddick
INTRODUCTION
When working on my server I saw that you could have attributes to your items which could increase player skill or stats and add other tasty treats like absorb, increased maximum life and mana. And I thought, "Perfect! This will fit my *censored* system perfectly!", but this thought was abruptly interrupted when the harsh mistress called reality came and slapped me very hard. She reminded me that I can ONLY add these attributes to items in items.xml but not dynamically using the ol' wonderful thing called LUA. For a while my master schemes came crashing down and I went to my corner and started crying, when it suddenly hit me like a freight train, the Attributes MOD. Basically what this does is allow you to use those attributes you would use in items.xml except in LUA. The brilliance of it all is that you just add attributes like you normally would in LUA except now attributes like skilldist is available, perfect eh?
Now you might be screaming at the top of your lungs, HOW DOES IT ALL WORK? To which I answer that it is quite simple really. It works just like the other attribute system does, the effects are just there when you have the specific item equipped and it gets removed when you "dequip" it. The effects are also visible when looking on the item just like the regular system would be, in fact it integrates into the old system so for example, the amazon armor that has a base of +3 dist when given +10 dist using my system will appear as +13 dist when looking on it, neat huh?.
NOTE
There are two current catches with this system as of now. The first one is that I am working on a later rev in the premium board for client version 9.31 but if the rev you are using differs much from mine I can and will help you upon request, just tell me which version or rev you are using. The second one is that I have not been able to thoroughly bug test this other than putting armors and weapons on and off multiple times and closing the server without save etc to see if the points can be bugged to be permanent, or if you can stack points in a way that should not be possible. If you have an active server that you want this system on I will gladly be of service and give more personal and quicker help, just hit me up. I will give credits here for all the bug reports and help I have gotten.
CURRENTLY AVAILABLE ATTRIBUTES
- skillsword
- skillaxe
- skillclub
- skilldist
- skillfish
- skillshield
- skillfist
- maxhealthpoints
- maxmanapoints
- soulpoints
- magiclevel
The implementation of this is small and very easy, the code is also documented so that you can understand what all the parts are doing. There are two files we modify and two files we create and the files we modify are 'movement.cpp' and 'item.cpp'. The files we create are 'attributesmod.cpp' and 'attributesmod.h'.
NOTE: All these changes takes place in the same folder you have the rest of the source files!
- Create attributesmod.h and add the following code:
PHP://////////////////////////////////////////////////////////////////////// // OpenTibia - an opensource roleplaying game //////////////////////////////////////////////////////////////////////// // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. //////////////////////////////////////////////////////////////////////// #ifndef __ATTRIBUTES_MOD__ #define __ATTRIBUTES_MOD__ // Holds our conversion arrays that we use to convert the SKILL and STAT enums to // the matching attributes. // // Example: CONVERSION::SKILL[SKILL_DIST] returns "skilldist" struct CONVERSION { static const char* SKILL[7]; static const char* STATS[5]; }; #endif
- Create attributesmod.cpp and add the following code:
PHP://////////////////////////////////////////////////////////////////////// // OpenTibia - an opensource roleplaying game //////////////////////////////////////////////////////////////////////// // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. //////////////////////////////////////////////////////////////////////// #include "otpch.h" #include "attributesmod.h" // Creates the lookup array for the skills_t enum. These can be changed // but you need to make sure you do not switch positions or remove any of them // since these rely on the fact that CONVERSION::SKILL[0] is fist fighting and // CONVERSION::SKILL[6] is fish skill. // // These are the names that you use when adding attributes in LUA, so to add an // fist skill attribute to an armor you use the text described here. const char* CONVERSION::SKILL[7] = { "skillfist", "skillclub", "skillsword", "skillaxe", "skilldist", "skillshield", "skillfish" }; // Creates the lookup array for the stats_t enum. These can be changed // but you need to make sure you do not switch positions or remove any of them // since these rely on the fact that CONVERSION::STATS[0] is max health potion and // CONVERSION::STATS[4] is magic level. // // NOTE: // // These are the names that you use when adding attributes in LUA, so to add an // magic level attribute you use the text in the last one const char* CONVERSION::STATS[5] = { "maxhealthpoints", // Increase max health "maxmanapoints", // Increase max mana "soulpoints", // Soul points bonus "level_not_implemented", // Level increase not implemented, but can easily be // Just set an appropriate attribute name here and follow the comments in movement.cpp and item.cpp "magiclevel" // Magic level bonus };
- Find the following code in movement.cpp:
PHP:#include "combat.h" #include "game.h"
- Add this underneath:
PHP:// BEGIN ATTRIBUTES MOD #include "attributesmod.h" // END ATTRIBUTES MOD
- Find the following code in movement.cpp:
PHP:bool needUpdateSkills = false; for(uint32_t i = SKILL_FIRST; i <= SKILL_LAST; ++i) { if(it.abilities.skills[i]) { player->setVarSkill((skills_t)i, it.abilities.skills[i]); if(!needUpdateSkills) needUpdateSkills = true; } if(it.abilities.skillsPercent[i]) { player->setVarSkill((skills_t)i, (int32_t)(player->getSkill((skills_t)i, SKILL_LEVEL) * ((it.abilities.skillsPercent[i] - 100) / 100.f))); if(!needUpdateSkills) needUpdateSkills = true; }
- Add this underneath:
PHP:// BEGIN ATTRIBUTES MOD boost::any skillValue = item->getAttribute(CONVERSION::SKILL[i]); if (!skillValue.empty()) { if ((skillValue.type() == typeid(float)) || (skillValue.type() == typeid(int32_t))) { player->setVarSkill((skills_t)i, boost::any_cast<int32_t>(skillValue)); if(!needUpdateSkills) needUpdateSkills = true; } } // END ATTRIBUTES MOD
- Find the following code in movement.cpp:
PHP:bool needUpdateStats = false; for(uint32_t s = STAT_FIRST; s <= STAT_LAST; ++s) { if(it.abilities.stats[s]) { player->setVarStats((stats_t)s, it.abilities.stats[s]); if(!needUpdateStats) needUpdateStats = true; } if(it.abilities.statsPercent[s]) { player->setVarStats((stats_t)s, (int32_t)(player->getDefaultStats((stats_t)s) * ((it.abilities.statsPercent[s] - 100) / 100.f))); if(!needUpdateStats) needUpdateStats = true; }
- Add this underneath:
PHP:// BEGIN ATTRIBUTES MOD if((stats_t)s != STAT_LEVEL) { boost::any statValue = item->getAttribute(CONVERSION::STATS[s]); if (!statValue.empty()) { if ((statValue.type() == typeid(float)) || (statValue.type() == typeid(int32_t))) { player->setVarStats((stats_t)s, boost::any_cast<int32_t>(statValue)); if(!needUpdateStats) needUpdateStats = true; } } } // END ATTRIBUTES MOD
- Find the following code in movement.cpp:
PHP:bool needUpdateSkills = false; for(uint32_t i = SKILL_FIRST; i <= SKILL_LAST; ++i) { if(it.abilities.skills[i]) { needUpdateSkills = true; player->setVarSkill((skills_t)i, -it.abilities.skills[i]); } if(it.abilities.skillsPercent[i]) { needUpdateSkills = true; player->setVarSkill((skills_t)i, -(int32_t)(player->getSkill((skills_t)i, SKILL_LEVEL) * ((it.abilities.skillsPercent[i] - 100) / 100.f))); }
- Add this underneath:
PHP:// BEGIN ATTRIBUTES MOD boost::any skillValue = item->getAttribute(CONVERSION::SKILL[i]); if (!skillValue.empty()) { if ((skillValue.type() == typeid(float)) || (skillValue.type() == typeid(int32_t))) { player->setVarSkill((skills_t)i, -boost::any_cast<int32_t>(skillValue)); if(!needUpdateSkills) needUpdateSkills = true; } } // END ATTRIBUTES MOD
- Find the following code in movement.cpp:
PHP:bool needUpdateStats = false; for(uint32_t s = STAT_FIRST; s <= STAT_LAST; ++s) { if(it.abilities.stats[s]) { needUpdateStats = true; player->setVarStats((stats_t)s, -it.abilities.stats[s]); } if(it.abilities.statsPercent[s]) { needUpdateStats = true; player->setVarStats((stats_t)s, -(int32_t)(player->getDefaultStats((stats_t)s) * ((it.abilities.statsPercent[s] - 100) / 100.f))); }
- Add this underneath:
PHP:// BEGIN ATTRIBUTES MOD if ((stats_t)s != STAT_LEVEL) { boost::any statValue = item->getAttribute(CONVERSION::STATS[s]); if (!statValue.empty()) { if ((statValue.type() == typeid(float)) || (statValue.type() == typeid(int32_t))) { player->setVarStats((stats_t)s, -boost::any_cast<int32_t>(statValue)); if(!needUpdateStats) needUpdateStats = true; } } } // END ATTRIBUTES MOD
- Find the following code in item.cpp:
PHP:#include "movement.h"
- Add this underneath:
PHP:// BEGIN ATTRIBUTES MOD #include "attributesmod.h" // END ATTRIBUTES MOd
- Find the following code in item.cpp
PHP:else if(it.weaponType != WEAPON_AMMO && it.weaponType != WEAPON_WAND) { if(it.attack || it.extraAttack || (item && (item->getAttack() || item->getExtraAttack()))) { begin = false; s << " (Atk:"; if(it.abilities.elementType != COMBAT_NONE) { s << std::max((int32_t)0, int32_t((item ? item->getAttack() : it.attack) - it.abilities.elementDamage)); if(it.extraAttack || (item && item->getExtraAttack())) s << " " << std::showpos << int32_t(item ? item->getExtraAttack() : it.extraAttack) << std::noshowpos; s << " physical + " << it.abilities.elementDamage << " " << getCombatName(it.abilities.elementType); } else { s << int32_t(item ? item->getAttack() : it.attack); if(it.extraAttack || (item && item->getExtraAttack())) s << " " << std::showpos << int32_t(item ? item->getExtraAttack() : it.extraAttack) << std::noshowpos; } } if(it.defense || it.extraDefense || (item && (item->getDefense() || item->getExtraDefense()))) { if(begin) { begin = false; s << " ("; } else s << ", "; s << "Def:" << int32_t(item ? item->getDefense() : it.defense); if(it.extraDefense || (item && item->getExtraDefense())) s << " " << std::showpos << int32_t(item ? item->getExtraDefense() : it.extraDefense) << std::noshowpos; } } for(uint16_t i = SKILL_FIRST; i <= SKILL_LAST; ++i) { if(!it.abilities.skills[i]) continue; if(begin) { begin = false; s << " ("; } else s << ", "; s << getSkillName(i) << " " << std::showpos << (int32_t)it.abilities.skills[i] << std::noshowpos; } if(it.abilities.stats[STAT_MAGICLEVEL]) { if(begin) { begin = false; s << " ("; } else s << ", "; s << "magic level " << std::showpos << (int32_t)it.abilities.stats[STAT_MAGICLEVEL] << std::noshowpos; }
- Replace with:
PHP:else if(it.weaponType != WEAPON_AMMO && it.weaponType != WEAPON_WAND) { if(it.attack || it.extraAttack || (item && (item->getAttack() || item->getExtraAttack()))) { begin = false; s << " (Atk:"; if(it.abilities.elementType != COMBAT_NONE) { s << std::max((int32_t)0, int32_t((item ? item->getAttack() : it.attack) - it.abilities.elementDamage)); if(it.extraAttack || (item && item->getExtraAttack())) s << " " << std::showpos << int32_t(item ? item->getExtraAttack() : it.extraAttack) << std::noshowpos; s << " physical + " << it.abilities.elementDamage << " " << getCombatName(it.abilities.elementType); } else { s << int32_t(item ? item->getAttack() : it.attack); if(it.extraAttack || (item && item->getExtraAttack())) s << " " << std::showpos << int32_t(item ? item->getExtraAttack() : it.extraAttack) << std::noshowpos; } } if(it.defense || it.extraDefense || (item && (item->getDefense() || item->getExtraDefense()))) { if(begin) { begin = false; s << " ("; } else s << ", "; s << "Def:" << int32_t(item ? item->getDefense() : it.defense); if(it.extraDefense || (item && item->getExtraDefense())) s << " " << std::showpos << int32_t(item ? item->getExtraDefense() : it.extraDefense) << std::noshowpos; } } for(uint16_t i = SKILL_FIRST; i <= SKILL_LAST; ++i) { // BEGIN ATTRIBUTES MOD // Get the value if any from items attribute list boost::any attr = item->getAttribute(CONVERSION::SKILL[i]); // Make sure we either have abilities or an attribute if(!it.abilities.skills[i] && attr.empty()) continue; if(begin) { begin = false; s << " ("; } else s << ", "; // Holds the total increased value int32_t val = 0; // If we have an ability then include that into the answer if (it.abilities.skills[i]) val = (int32_t)it.abilities.skills[i]; // Make sure the value exist and that it is either float or integer. if (!attr.empty() && ((attr.type() == typeid(float)) || (attr.type() == typeid(int32_t)))) { // Add attribute value to val val += boost::any_cast<int32_t>(attr); } s << getSkillName(i) << " " << std::showpos << val << std::noshowpos; // END ATTRIBUTES MOD } // BEGIN ATTRIBUTES MOD boost::any attr = item->getAttribute(CONVERSION::STATS[STAT_MAGICLEVEL]); if(it.abilities.stats[STAT_MAGICLEVEL] || !attr.empty()) { if(begin) { begin = false; s << " ("; } else s << ", "; // Holds the total increased value int32_t val = 0; // If we have an ability then include that into the value if (it.abilities.stats[STAT_MAGICLEVEL]) val = (int32_t)it.abilities.stats[STAT_MAGICLEVEL]; // Make sure the value exist and that it is either float or integer. if (!attr.empty() && ((attr.type() == typeid(float)) || (attr.type() == typeid(int32_t)))) { // Add attribute value to val val += boost::any_cast<int32_t>(attr); } s << "magic level " << std::showpos << val << std::noshowpos; } // END ATTRIBUTES MOD
- Find the following code in item.cpp
PHP:else if(it.armor || (item && item->getArmor()) || it.showAttributes) { int32_t tmp = it.armor; if(item) tmp = item->getArmor(); bool begin = true; if(tmp) { s << " (Arm:" << tmp; begin = false; } for(uint16_t i = SKILL_FIRST; i <= SKILL_LAST; ++i) { if(!it.abilities.skills[i]) continue; if(begin) { begin = false; s << " ("; } else s << ", "; s << getSkillName(i) << " " << std::showpos << (int32_t)it.abilities.skills[i] << std::noshowpos; } if(it.abilities.stats[STAT_MAGICLEVEL]) { if(begin) { begin = false; s << " ("; } else s << ", "; s << "magic level " << std::showpos << (int32_t)it.abilities.stats[STAT_MAGICLEVEL] << std::noshowpos; }
- Replace with:
PHP:else if(it.armor || (item && item->getArmor()) || it.showAttributes) { int32_t tmp = it.armor; if(item) tmp = item->getArmor(); bool begin = true; if(tmp) { s << " (Arm:" << tmp; begin = false; } for(uint16_t i = SKILL_FIRST; i <= SKILL_LAST; ++i) { // BEGIN ATTRIBUTES MOD // Get the value if any from items attribute list boost::any attr = item->getAttribute(CONVERSION::SKILL[i]); // Make sure we either have abilities or an attribute if(!it.abilities.skills[i] && attr.empty()) continue; if(begin) { begin = false; s << " ("; } else s << ", "; // Holds the total increased value int32_t val = 0; // If we have an ability then include that into the value if (it.abilities.skills[i]) val = (int32_t)it.abilities.skills[i]; // Make sure the value exist and that it is either float or integer. if (!attr.empty() && ((attr.type() == typeid(float)) || (attr.type() == typeid(int32_t)))) { // Add attribute value to val val += boost::any_cast<int32_t>(attr); } s << getSkillName(i) << " " << std::showpos << val << std::noshowpos; // END ATTRIBUTES MOD } // BEGIN ATTRIBUTES MOD boost::any attr = item->getAttribute(CONVERSION::STATS[STAT_MAGICLEVEL]); if(it.abilities.stats[STAT_MAGICLEVEL] || !attr.empty()) { if(begin) { begin = false; s << " ("; } else s << ", "; // Holds the total increased value int32_t val = 0; // If we have an ability then include that into the value if (it.abilities.stats[STAT_MAGICLEVEL]) val = (int32_t)it.abilities.stats[STAT_MAGICLEVEL]; // Make sure the value exist and that it is either float or integer. if (!attr.empty() && ((attr.type() == typeid(float)) || (attr.type() == typeid(int32_t)))) { // Add attribute value to val val += boost::any_cast<int32_t>(attr); } s << "magic level " << std::showpos << val << std::noshowpos; } // END ATTRIBUTES MOD
- Open attributesmod.cpp and find this code:
PHP:"level_not_implemented", // Level increase not implemented, but can easily be
- Replace it with:
PHP:"playerlevel", // Level increase not implemented, but can easily be
- Find the following code in movement.cpp:
PHP:// BEGIN ATTRIBUTES MOD if ((stats_t)s != STAT_LEVEL) { boost::any statValue = item->getAttribute(CONVERSION::STATS[s]); if (!statValue.empty()) { if ((statValue.type() == typeid(float)) || (statValue.type() == typeid(int32_t))) { player->setVarStats((stats_t)s, -boost::any_cast<int32_t>(statValue)); if(!needUpdateStats) needUpdateStats = true; } } } // END ATTRIBUTES MOD
- Replace it with:
PHP:// BEGIN ATTRIBUTES MOD boost::any statValue = item->getAttribute(CONVERSION::STATS[s]); if (!statValue.empty()) { if ((statValue.type() == typeid(float)) || (statValue.type() == typeid(int32_t))) { player->setVarStats((stats_t)s, -boost::any_cast<int32_t>(statValue)); if(!needUpdateStats) needUpdateStats = true; } } // END ATTRIBUTES MOD
- Find the following code in movement.cpp:
PHP:// BEGIN ATTRIBUTES MOD if((stats_t)s != STAT_LEVEL) { boost::any statValue = item->getAttribute(CONVERSION::STATS[s]); if (!statValue.empty()) { if ((statValue.type() == typeid(float)) || (statValue.type() == typeid(int32_t))) { player->setVarStats((stats_t)s, boost::any_cast<int32_t>(statValue)); if(!needUpdateStats) needUpdateStats = true; } } } // END ATTRIBUTES MOD
- Replace with:
PHP:// BEGIN ATTRIBUTES MOD boost::any statValue = item->getAttribute(CONVERSION::STATS[s]); if (!statValue.empty()) { if ((statValue.type() == typeid(float)) || (statValue.type() == typeid(int32_t))) { player->setVarStats((stats_t)s, boost::any_cast<int32_t>(statValue)); if(!needUpdateStats) needUpdateStats = true; } } // END ATTRIBUTES MOD
If you are on an Linux distribution you need to make sure that you add attributesmod.cpp into your Makefile or if you use autoconf then you need to change Makefile.am and rerun ./configure with the needed parameters. I have Makefile.am so I added attributesmod.cpp to the end of "theforgottenserver_SOURCES" list so it looks like:
PHP:
vocation.h attributesmod.cpp
SMALL TUTORIAL ON HOW TO USE IT
Code:
-- When you have the unique id of an item you can just add
-- attributes like normal except now you have the ones from the mod as well.
-- This one adds 10 levels of fist fighting skill as an attributes.
doItemSetAttribute(item.uid, "skillfist", 10)
There are a couple of things you should now, these effects are NOT added if you do not add the item that should give the bonus to movement.xml so that it executes onEquip and onDeEquip. It is also worth noting that these affects can be added to all items but unless it's an equipable item that executes said functions it will not have any effect at all, apart from showing up when looking on the item.
WHAT SHOULD I DO AFTER I HAVE USED THIS AWESOME MOD IN MY SERVER?
This is totally up to you but I welcome rep and credit. But above all else I would like to know what you use it for, what exciting and cool new features and mods have you been able to create using this mod? You do not need to tell the whole world just tell me and give me ideas on how I can improve it!
You can also request other mods by PM, if I find them cool enough and I have the time I will create the mod and give you credit. By mods I mean something bigger not a small change that accomplishes nothing useful.
Last edited: