Pesquisar na Comunidade
Mostrando resultados para as tags ''C++''.
Encontrado 127 registros
-
GameShop in-game OTClientv8 / Downgrade Nekiro TFS 1.5 V7.72
Olá Tibiakings, Source: https://github.com/OTCv8/forgottenserver/commit/2839d4d7a8ad3597eff6c786f4ceb9b1b4b4456b#diff-e26c2fbabf42953e9a2f3e4c5749d0699804da9406cb340cc393afd3b7be4955 Datapack: creaturescripts/creaturescripts.xml <event type="extendedopcode" name="GameStore" script="game_store.lua" /> creaturescripts/scripts/login.lua player:registerEvent("GameStore") creaturescripts/scripts/game_store.lua https://github.com/OTCv8/otcv8-tools/blob/main/server/shop/shop.lua lib/lib.lua dofile('data/lib/core/json.lua') lib/core/json.lua https://github.com/OTCv8/otcv8-tools/blob/main/server/json.lua OTClientv8: otclient/modules/game_features/features.lua if(version >= 770) then g_game.enableFeature(GameLooktypeU16) g_game.enableFeature(GameMessageStatements) g_game.enableFeature(GameLoginPacketEncryption) g_game.enableFeature(GameExtendedOpcode) end Database: CREATE TABLE `shop_history` ( `id` int(11) NOT NULL, `account` int(11) NOT NULL, `player` int(11) NOT NULL, `date` datetime NOT NULL, `title` varchar(100) NOT NULL, `cost` int(11) NOT NULL, `details` varchar(500) NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; ALTER TABLE `shop_history` ADD PRIMARY KEY (`id`); ALTER TABLE `shop_history` MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
-
Debug no cliente 8.60 custom BufferSize>0 Network.cpp
Olá pessoal, preciso da ajuda de vocês, já estou 3 meses nesse bug e não consigo resolver... Eu uso um cliente próprio versão 8.60 tfs 1.3 Já fiz alterações em diversas locais para tentar resolver essa imcompatibilidade. Player.cpp 361: exception occurred, reason: Network.cpp 946: exception occurred (ErrorCode = 0), reason: Network.cpp 921: assertion failed (BufferSize = 0) (NextToWrite = 16394), reason: BufferSize>0 Essa mensagem que aparece quando tem muitos players na mesma stack. Alguém sabe qual é o problema? Obrigado.
-
Fist Fighting/Attackspeed
Nome: Fist Fighting/Attackspeed Tipo: C++ Autor: Oneshot Já vi alguns pedidos no fórum sobre a skill Fist Fighting, onde quanto mais você treinasse ela, mais rápido você atacaria no jogo, e parece que isto é um feature do Tibia. Como é uma modificação muito fácil nas sources, resolvi passar aí para a galera. Por padrão, o intervalo entre ataques do Tibia é 2000ms, ou seja, um ataque físico a cada dois segundos. Eu fiz uma pequena modificação nas sources onde o Fist Fighting seria inversamente proporcional ao tal intervalo, ou seja, quanto maior o valor da skill, menor seria o intervalo. Fiz de um modo que um jogador com Fist Fighting de nível 200, então, teria uma redução de 75% no intervalo de ataque, ou seja, um ataque a cada meio segundo ou dois ataques por segundo Leve em consideração que ele pega como base o attackspeed da vocação ou da arma usada, ou seja, se seu servidor já tem o tal chamado "fast attack", de nada adianta adicionar esse código C++. Abra seu player.cpp, procure por isso: Player::getAttackSpeed() Substitua toda a função, dependendo da versão de seu servidor: 0.3.6 uint32_t Player::getAttackSpeed() { Item* weapon = getWeapon(); if(weapon && weapon->getAttackSpeed() != 0) return std::ceil(weapon->getAttackSpeed() * (1 - (getSkill(SKILL_FIST, SKILL_LEVEL) * 0.00375))); return std::ceil(vocation->getAttackSpeed() * (1 - (getSkill(SKILL_FIST, SKILL_LEVEL) * 0.00375))); } 0.4 uint32_t Player::getAttackSpeed() const { return std::ceil(((weapon && weapon->getAttackSpeed() != 0) ? weapon->getAttackSpeed() * (1 - (getSkill(SKILL_FIST, SKILL_LEVEL) * 0.00375)) : (vocation->getAttackSpeed() / std::max((size_t)1, getWeapons().size()) * (1 - (getSkill(SKILL_FIST, SKILL_LEVEL) * 0.00375))))); } Isso adiciona uma utilidade para a skill Fist Fighting que em muitos dos servidores é algo deixado de lado e inútil.
-
Square System TFS 1.X
Square System Esse sistema irá possibilitar você colocar uma square em determinado player. Em luascript.cpp, acima de registerMethod("Player", "isPlayer", LuaScriptInterface::luaPlayerIsPlayer); coloque isso registerMethod("Player", "sendCreatureSquare", LuaScriptInterface::luaSendCreatureSquare); Agora, acima de int LuaScriptInterface::luaPlayerCreate(lua_State* L) Coloque isso int LuaScriptInterface::luaSendCreatureSquare(lua_State* L) { // player:sendCreatureSquare(creature, color) Player* player = getUserdata<Player>(L, 1); if (!player) { lua_pushnil(L); return 1; } Creature* creature = getUserdata<Creature>(L, 2); if (!creature) { lua_pushnil(L); return 1; } uint32_t color = getNumber<uint32_t>(L, 3, 0); player->sendCreatureSquare(creature, static_cast <SquareColor_t>(color)); lua_pushboolean(L, true); return 1; } Agora em luascript.h, antes de static int luaPlayerIsPlayer(lua_State* L); adicione isso static int luaSendCreatureSquare(lua_State* L); Agora para utilizar use player:sendCreatureSquare(creature, color) Boa criatividade. Créditos: 100% meu
-
In Game Account Manager TFS 1.3 / OTX3
Not completely tested and not all features are working but I am releasing this so that it is considered to be fixed and merged with the official branch. Sql INSERT INTO `players` (`id`, `name`, `group_id`, `account_id`, `level`, `vocation`, `health`, `healthmax`, `experience`, `lookbody`, `lookfeet`, `lookhead`, `looklegs`, `looktype`, `lookaddons`, `maglevel`, `mana`, `manamax`, `manaspent`, `soul`, `town_id`, `posx`, `posy`, `posz`, `conditions`, `cap`, `sex`, `lastlogin`, `lastip`, `save`, `skull`, `skulltime`, `lastlogout`, `blessings`, `onlinetime`, `deletion`, `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`) VALUES (1, 'Account Manager', 1, 1, 1, 1, 150, 150, 0, 0, 0, 0, 0, 110, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, '', 40000, 1, 1535311649, 16777343, 1, 0, 0, 1535311709, 0, 2958, 0, 0, 43200, -1, 2520, 10, 0, 10, 0, 10, 0, 10, 0, 10, 0, 10, 0, 10, 0); INSERT INTO `accounts` (`id`, `name`, `password`, `secret`, `type`, `premdays`, `lastday`, `vippoints`, `email`, `creation`) VALUES (1, '1', '356a192b7913b04c54574d18c28d46e6395428ab', NULL, 1, 365, 1535263046, '', '', 0); configmanager.cpp // account manager boolean[ACCOUNT_MANAGER] = getGlobalBoolean(L, "accountManager", true); integer[AM_LEVEL] = getGlobalNumber(L, "startLevel", 1); integer[AM_EXPERIENCE] = getGlobalNumber(L, "experience", 0); integer[AM_MAGLEVEL] = getGlobalNumber(L, "level", 0); integer[AM_FIST] = getGlobalNumber(L, "fist", 10); integer[AM_CLUB] = getGlobalNumber(L, "club", 10); integer[AM_SWORD] = getGlobalNumber(L, "sword", 10); integer[AM_AXE] = getGlobalNumber(L, "axe", 10); integer[AM_SHIELD] = getGlobalNumber(L, "shield", 10); integer[AM_DIST] = getGlobalNumber(L, "distance", 10); integer[AM_FISH] = getGlobalNumber(L, "fish", 10); integer[AM_SOUL] = getGlobalNumber(L, "soul", 100); integer[AM_BALANCE] = getGlobalNumber(L, "balance", 0); integer[AM_OFFLINE_TRAIN] = getGlobalNumber(L, "offlineTrainingTime", 0); integer[AM_STAMINA] = getGlobalNumber(L, "stamina", 0); integer[AM_LOOK_ADDONS] = getGlobalNumber(L, "lookAddons", 0); integer[AM_MOUNT_ID] = getGlobalNumber(L, "mountId", 0); integer[AM_TOWN_ID] = getGlobalNumber(L, "startTownId", 1); integer[AM_SPAWNPOS_X] = getGlobalNumber(L, "temple_x", 0); integer[AM_SPAWNPOS_Y] = getGlobalNumber(L, "temple_y", 0); integer[AM_SPAWNPOS_Z] = getGlobalNumber(L, "temple_z", 0); integer[AM_MAX_HP] = getGlobalNumber(L, "baseHP", 150); integer[AM_MAX_MP] = getGlobalNumber(L, "baseMP", 0); integer[AM_MAX_CAP] = getGlobalNumber(L, "baseCAP", 400); integer[AM_MALE] = getGlobalNumber(L, "maleOutfit", 128); integer[AM_FEMALE] = getGlobalNumber(L, "femaleOutfit", 136); boolean[AM_CHOOSEVOC] = getGlobalBoolean(L, "chooseVocation", false); boolean[AM_GENERATE_ACCOUNT_NUMBER] = getGlobalBoolean(L, "generateAccountNumber", false); // xml or lua boolean[USE_XML] = getGlobalBoolean(L, "useXml", false); configmanager.h booleans // account manager ACCOUNT_MANAGER, NAMELOCK_MANAGER, AM_CHOOSEVOC, AM_GENERATE_ACCOUNT_NUMBER, // -- // lua or xml USE_XML, integers // account manager AM_LEVEL, AM_EXPERIENCE, AM_MAGLEVEL, AM_FIST, AM_CLUB, AM_SWORD, AM_AXE, AM_SHIELD, AM_DIST, AM_FISH, AM_SOUL, AM_BALANCE, AM_OFFLINE_TRAIN, AM_STAMINA, AM_LOOK_ADDONS, AM_MOUNT_ID, AM_TOWN_ID, AM_SPAWNPOS_X, AM_SPAWNPOS_Y, AM_SPAWNPOS_Z, AM_MAX_HP, AM_MAX_MP, AM_MAX_CAP, AM_MALE, AM_FEMALE, // -- creature.h // account manager virtual bool isAccountManager() const { return false; } // -- game.cpp find this inside of Game:playerSay uint32_t muteTime = player->isMuted(); if (muteTime > 0) { std::ostringstream ss; ss << "You are still muted for " << muteTime << " seconds."; player->sendTextMessage(MESSAGE_STATUS_SMALL, ss.str()); return; } and place this right underneath // account manager if(player->isAccountManager()) { player->removeMessageBuffer(); internalCreatureSay(player, TALKTYPE_SAY, text, false); return; } // -- find this in Game::internalCreatureSay if (text.empty()) { return false; } and place this above it // account manager Player* player = creature->getPlayer(); if(player && player->isAccountManager()) { player->manageAccount(text); return true; } // -- add this to house.cpp // account manager void House::updateDoorDescription(std::string _name/* = ""*/) { std::string tmp = "house"; /* no isGuild method (atm) if(isGuild()) tmp = "hall"; */ char houseDescription[200]; const int32_t housePrice = g_config.getNumber(ConfigManager::HOUSE_PRICE); if(owner) { /* if(isGuild()) IOGuild::getInstance()->getGuildById(_name, owner); */ if(_name.empty()) IOLoginData::getInstance()->getNameByGuid(owner, _name); sprintf(houseDescription, "It belongs to %s '%s'. %s owns this %s.", tmp.c_str(), houseName.c_str(), _name.c_str(), tmp.c_str()); } else sprintf(houseDescription, "It belongs to %s '%s'. Nobody owns this %s. It costs %lu gold coins.", tmp.c_str(), houseName.c_str(), tmp.c_str(), ( housePrice != -1 ? (houseTiles.size() * housePrice) : 0 ) ); for (const auto& it : doorSet) { it->setSpecialDescription(houseDescription); } } // -- house.h find this void updateDoorDescription() const; and place this right underneath it void updateDoorDescription(std::string _name = ""); iologindata.cpp find this #include "game.h" and add this below it // account manager #include "vocation.h" // -- find this extern Game g_game; and add this below it // account manager extern Vocation g_vocations; // -- inside of this IOLoginData::loginserverAuthentication find this result = db.storeQuery(query.str()); if (result) { do { if (result->getNumber<uint64_t>("deletion") == 0) { account.characters.push_back(result->getString("name")); } } while (result->next()); std::sort(account.characters.begin(), account.characters.end()); } return true; and replace it with this result = db.storeQuery(query.str()); // account manager if(!result){ // give the account the account manager to use if they have no account account.characters.push_back("Account Manager"); return true; } // -- if (result) { // allow them to access the account manager if there are players on the account if (account.id != 1){ account.characters.push_back("Account Manager"); } do { if (result->getNumber<uint64_t>("deletion") == 0) { account.characters.push_back(result->getString("name")); } } while (result->next()); std::sort(account.characters.begin(), account.characters.end()); } return true; add this // account manager bool IOLoginData::getAccountId(const std::string& name, uint32_t& number) { if(!name.length()) return false; Database& db = Database::getInstance(); std::ostringstream query; query << "SELECT `id` FROM `accounts` WHERE `name` LIKE " << db.escapeString(name) << " LIMIT 1;"; DBResult_ptr result; if(!(result = db.storeQuery(query.str()))) return false; number = result->getNumber<uint32_t>("id"); return true; } // -- find this in IOLoginData::updateOnlineStatus if (g_config.getBoolean(ConfigManager::ALLOW_CLONES)) { return; } and add this // account manager if(g_config.getBoolean(ConfigManager::ACCOUNT_MANAGER)){ return; } // -- add this // account manager bool IOLoginData::getNameByGuid(uint32_t guid, std::string& name) { std::ostringstream query; query << "SELECT `name` FROM `players` WHERE `id` = " << guid << " AND `deleted` = 0 LIMIT 1;"; DBResult_ptr result = Database::getInstance().storeQuery(query.str()); if (!result) { return false; } name = result->getString("name"); nameCacheMap[guid] = name; return true; } // -- Now add all these // account manager bool IOLoginData::accountIdExists(uint32_t accountId) { Database& db = Database::getInstance(); std::ostringstream query; query << "SELECT `id` FROM `accounts` WHERE `id` = " << accountId << " LIMIT 1"; DBResult_ptr result = db.storeQuery(query.str()); if(!result) return false; return true; } bool IOLoginData::accountNameExists(const std::string& name) { Database& db = Database::getInstance(); std::ostringstream query; query << "SELECT `id` FROM `accounts` WHERE `name` LIKE " << db.escapeString(name) << " LIMIT 1"; DBResult_ptr result = db.storeQuery(query.str()); if(!result) return false; return true; } bool IOLoginData::getPassword(uint32_t accountId, std::string& password, std::string name/* = ""*/) { Database& db = Database::getInstance(); std::ostringstream query; query << "SELECT `password` FROM `accounts` WHERE `id` = " << accountId << " LIMIT 1"; DBResult_ptr result = db.storeQuery(query.str()); if(!result) return false; if(name.empty() || name == "Account Manager") { password = result->getString("password"); return true; } std::string tmpPassword = result->getString("password"); query.str(""); query << "SELECT `name` FROM `players` WHERE `account_id` = " << accountId; result = db.storeQuery(query.str()); if(!result) return false; do { if(result->getString("name") != name) continue; password = tmpPassword; return true; } while(result->next()); return false; } // accountId should be id because the server references the index and not the actual account data bool IOLoginData::setPassword(uint32_t accountId, std::string newPassword) { std::string ePassword = transformToSHA1(newPassword); Database& db = Database::getInstance(); std::ostringstream query; query << "UPDATE `accounts` SET `password` = " << db.escapeString(ePassword) << " WHERE `id` = " << accountId << ";"; return db.executeQuery(query.str()); } // not using recovery key atm but still good to have bool IOLoginData::validRecoveryKey(uint32_t accountId, std::string recoveryKey) { std::string nRecoveryKey = transformToSHA1(recoveryKey); Database& db = Database::getInstance(); std::ostringstream query; query << "SELECT `id` FROM `accounts` WHERE `id` = " << accountId << " AND `recoverykey` "; query << "LIKE " << db.escapeString(nRecoveryKey) << " LIMIT 1"; DBResult_ptr result = db.storeQuery(query.str()); if(!result) return false; return true; } bool IOLoginData::setRecoveryKey(uint32_t accountId, std::string newRecoveryKey) { std::string nRecoveryKey = transformToSHA1(newRecoveryKey); Database& db = Database::getInstance(); std::ostringstream query; std::cout << "account id " << accountId << std::endl; query << "UPDATE `accounts` SET `recoverykey` = " << db.escapeString(nRecoveryKey) << " WHERE `name` = " << accountId << ";"; return db.executeQuery(query.str()); } uint64_t IOLoginData::createAccount(std::string name, std::string password) { std::string ePassword = transformToSHA1(password); Database& db = Database::getInstance(); std::ostringstream query; query << "INSERT INTO `accounts` (`id`, `name`, `password`) VALUES (NULL, " << db.escapeString(name) << ", " << db.escapeString(ePassword) << ")"; if(!db.executeQuery(query.str())) return 0; return db.getLastInsertId(); } // not tested yet bool IOLoginData::changeName(uint32_t guid, std::string newName, std::string oldName) { Database& db = Database::getInstance(); std::ostringstream query; query << "INSERT INTO `player_namelocks` (`player_id`, `name`, `new_name`, `date`) VALUES ("<< guid << ", " << db.escapeString(oldName) << ", " << db.escapeString(newName) << ", " << time(NULL) << ")"; DBResult_ptr result = db.storeQuery(query.str()); if (!result) { return false; } query.str(""); query << "UPDATE `players` SET `name` = " << db.escapeString(newName) << " WHERE `id` = " << guid << " LIMIT 1"; result = db.storeQuery(query.str()); if (!result) { return false; } GuidCacheMap::iterator it = guidCacheMap.find(oldName); if(it != guidCacheMap.end()) { guidCacheMap.erase(it); guidCacheMap[newName] = guid; } nameCacheMap[guid] = newName; return true; } bool IOLoginData::createCharacter(uint32_t accountId, std::string characterName, uint32_t vocationId /*int32_t vocationId */, uint16_t sex) { if(playerExists(characterName)){ return false; } // a little bulky but whatever lol uint32_t healthMax = g_config.getNumber(ConfigManager::AM_MAX_HP); uint32_t manaMax = g_config.getNumber(ConfigManager::AM_MAX_MP); uint32_t capMax = g_config.getNumber(ConfigManager::AM_MAX_CAP); uint16_t lookType = (sex % 2) ? g_config.getNumber(ConfigManager::AM_MALE) : g_config.getNumber(ConfigManager::AM_FEMALE); uint16_t lookAddons = g_config.getNumber(ConfigManager::AM_LOOK_ADDONS); uint16_t magLevel = g_config.getNumber(ConfigManager::AM_MAGLEVEL); uint16_t townId = g_config.getNumber(ConfigManager::AM_TOWN_ID); uint16_t spawnX = g_config.getNumber(ConfigManager::AM_SPAWNPOS_X); uint16_t spawnY = g_config.getNumber(ConfigManager::AM_SPAWNPOS_Y); uint16_t spawnZ = g_config.getNumber(ConfigManager::AM_SPAWNPOS_Z); uint32_t level = g_config.getNumber(ConfigManager::AM_LEVEL); uint64_t exp = g_config.getNumber(ConfigManager::AM_EXPERIENCE); uint32_t fist = g_config.getNumber(ConfigManager::AM_FIST); uint32_t club = g_config.getNumber(ConfigManager::AM_CLUB); uint32_t sword = g_config.getNumber(ConfigManager::AM_SWORD); uint32_t axe = g_config.getNumber(ConfigManager::AM_AXE); uint32_t shield = g_config.getNumber(ConfigManager::AM_SHIELD); uint32_t dist = g_config.getNumber(ConfigManager::AM_DIST); uint32_t fish = g_config.getNumber(ConfigManager::AM_FISH); uint32_t soul = g_config.getNumber(ConfigManager::AM_SOUL); uint64_t balance = g_config.getNumber(ConfigManager::AM_BALANCE); int32_t offlineTrainingTime = g_config.getNumber(ConfigManager::AM_OFFLINE_TRAIN); uint16_t stamina = g_config.getNumber(ConfigManager::AM_STAMINA); if(level > 1){ exp += Player::getExpForLevel(level); healthMax *= level; manaMax *= level; capMax *= level; } Database& db = Database::getInstance(); std::string name = db.escapeString(characterName); std::ostringstream query, initialQuery, lastQuery, selectChar; // since character creation wants to use the account name when it setups up the player as the account id // we'll ask the database to get the id of the account since that is how the players are listed in the character list selectChar << "SELECT `id` FROM `accounts` WHERE `name` = " << accountId << ";"; DBResult_ptr result = db.storeQuery(selectChar.str()); if (!result){ return false; } accountId = result->getNumber<uint32_t>("id"); // this is to counteract the foreign key issue initialQuery << "SET FOREIGN_KEY_CHECKS=0;"; lastQuery << "SET FOREIGN_KEY_CHECKS=1;"; db.executeQuery(initialQuery.str()); query << "INSERT INTO `players` (`name`, `group_id`, `account_id`, `level`, `vocation`, `health`, `healthmax`, `experience`, `lookbody`, `lookfeet`, `lookhead`, `looklegs`, `looktype`, `lookaddons`, `maglevel`, `mana`, `manamax`, `manaspent`, `soul`, `town_id`, `posx`, `posy`, `posz`, `conditions`, `cap`, `sex`, `lastlogin`, `lastip`, `save`, `skull`, `skulltime`, `lastlogout`, `blessings`, `onlinetime`, `deletion`, `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`) VALUES (" << name << ", 1, " << accountId << ", " << level << ", " << vocationId << ", " << healthMax << ", " << healthMax << ", " << exp << ", 0, 0, 0, 0, " << lookType << ", " << lookAddons << ", " << magLevel << ", " << manaMax << ", " << manaMax << ", 0, " << soul << ", " << townId << ", " << spawnX << ", " << spawnY << ", " << spawnZ << ", 0x0, " << capMax << ", " << sex << ", 0, 0, 1, 0, 0, 0, 0, 0, 0, " << balance << ", " << offlineTrainingTime << ", -1, " << stamina << ", " << fist << ", 0, " << club << ", 0, " << sword << ", 0, " << axe << ", 0, " << dist << ", 0, " << shield << ", 0, " << fish << ", 0);"; if(db.executeQuery(query.str())){ db.executeQuery(lastQuery.str()); return true; } db.executeQuery(lastQuery.str()); return false; } // not tested DeleteCharacter_t IOLoginData::deleteCharacter(uint32_t accountId, const std::string characterName) { if(g_game.getPlayerByName(characterName)) return DELETE_ONLINE; Database& db = Database::getInstance(); std::ostringstream query; query << "SELECT `id` FROM `players` WHERE `name` LIKE " << db.escapeString(characterName) << " AND `account_id` = " << accountId << " AND `deleted` = 0 LIMIT 1"; DBResult_ptr result = db.storeQuery(query.str()); if(!result) return DELETE_INTERNAL; uint32_t id = result->getNumber<uint32_t>("id"); House* house = g_game.map.houses.getHouseByPlayerId(id); if(house) return DELETE_HOUSE; /* if(IOGuild::getInstance()->getGuildLevel(id) == 3) return DELETE_LEADER; */ query.str(""); query << "UPDATE `players` SET `deleted` = 1 WHERE `id` = " << id << ";"; if(!db.executeQuery(query.str())) return DELETE_INTERNAL; query.str(""); query << "DELETE FROM `guild_invites` WHERE `player_id` = " << id; db.executeQuery(query.str()); query.str(""); query << "DELETE FROM `player_viplist` WHERE `vip_id` = " << id; db.executeQuery(query.str()); /* for(AutoList<Player>::iterator it = Player::autoList.begin(); it != Player::autoList.end(); ++it) { VIPListSet::iterator it_ = it->second->VIPList.find(id); if(it_ != it->second->VIPList.end()) it->second->VIPList.erase(it_); } */ return DELETE_SUCCESS; } bool IOLoginData::playerExists(uint32_t guid, bool multiworld /*= false*/, bool checkCache /*= true*/) { if(checkCache) { NameCacheMap::iterator it = nameCacheMap.find(guid); if(it != nameCacheMap.end()) return true; } Database& db = Database::getInstance(); std::ostringstream query; query << "SELECT `name` FROM `players` WHERE `id` = " << guid << " AND `deleted` = 0"; query << " LIMIT 1"; DBResult_ptr result = db.storeQuery(query.str()); if(!result) return false; const std::string name = result->getString("name"); nameCacheMap[guid] = name; return true; } bool IOLoginData::playerExists(std::string& name, bool multiworld /*= false*/, bool checkCache /*= true*/) { if(checkCache) { GuidCacheMap::iterator it = guidCacheMap.find(name); if(it != guidCacheMap.end()) { name = it->first; return true; } } Database& db = Database::getInstance(); std::ostringstream query; query << "SELECT `id`, `name` FROM `players` WHERE `name` LIKE " << db.escapeString(name); /*<< " AND `deleted` = 0"; */ query << " LIMIT 1"; DBResult_ptr result = db.storeQuery(query.str()); if(!result) return false; name = result->getString("name"); guidCacheMap[name] = result->getNumber<int32_t>("id"); return true; } // -- iologindata.h find this #include "database.h" and place this right underneath // account manager enum DeleteCharacter_t { DELETE_INTERNAL, DELETE_LEADER, DELETE_HOUSE, DELETE_ONLINE, DELETE_SUCCESS }; // -- find this class IOLoginData { public: and add this under it // account manager virtual ~IOLoginData() {} static IOLoginData* getInstance() { static IOLoginData instance; return &instance; } // -- find this static AccountType_t getAccountType(uint32_t accountId); and place this right below it // account manager bool accountIdExists(uint32_t accountId); bool accountNameExists(const std::string& name); bool getAccountId(const std::string& name, uint32_t& number); bool getNameByGuid(uint32_t guid, std::string& name); bool playerExists(uint32_t guid, bool multiworld = false, bool checkCache = true); bool playerExists(std::string& name, bool multiworld = false, bool checkCache = true); bool changeName(uint32_t guid, std::string newName, std::string oldName); bool createCharacter(uint32_t accountId, std::string characterName, uint32_t vocationId /* int32_t vocationId */, uint16_t sex); DeleteCharacter_t deleteCharacter(uint32_t accountId, const std::string characterName); bool getPassword(uint32_t accountId, std::string& password, std::string name = ""); bool setPassword(uint32_t accountId, std::string newPassword); bool validRecoveryKey(uint32_t accountId, std::string recoveryKey); bool setRecoveryKey(uint32_t accountId, std::string newRecoveryKey); uint64_t createAccount(std::string name, std::string password); // -- find this static void removePremiumDays(uint32_t accountId, int32_t removeDays); and place this right below it // account manager protected: struct StringCompareCase { bool operator()(const std::string& l, const std::string& r) const { return strcasecmp(l.c_str(), r.c_str()) < 0; } }; typedef std::map<std::string, uint32_t, StringCompareCase> GuidCacheMap; GuidCacheMap guidCacheMap; typedef std::map<uint32_t, std::string> NameCacheMap; NameCacheMap nameCacheMap; // -- luascript.cpp find this registerEnum(ZONE_NORMAL) and place this underneath // account manager registerEnum(MANAGER_NONE) registerEnum(MANAGER_NEW) registerEnum(MANAGER_ACCOUNT) registerEnum(MANAGER_NAMELOCK) // -- find this registerMethod("Player", "getLastLogout", LuaScriptInterface::luaPlayerGetLastLogout); and place this right underneath // account manager registerMethod("Player", "getAccountManager", LuaScriptInterface::luaGetPlayerAccountManager); // -- find this int LuaScriptInterface::luaPlayerGetLastLogout(lua_State* L) { // player:getLastLogout() Player* player = getUserdata<Player>(L, 1); if (player) { lua_pushnumber(L, player->getLastLogout()); } else { lua_pushnil(L); } return 1; } and place this under it int32_t LuaScriptInterface::luaGetPlayerAccountManager(lua_State* L) { // player:getAccountManager() Player* player = getUserdata<Player>(L, 1); if (player) { lua_pushnumber(L, player->accountManager); } else { lua_pushnil(L); } return 1; } luascript.h find this static int luaPlayerGetLastLogout(lua_State* L); and place this under it // account manager static int luaGetPlayerAccountManager(lua_State* L); // -- map.cpp look for bool Map:placeCreature(const Position& centerPos, Creature* creature, bool extendedPos/* = false*/, bool forceLogin/* = false*/) look for this Tile* tile = getTile(centerPos.x, centerPos.y, centerPos.z); if (tile) { placeInPZ = tile->hasFlag(TILESTATE_PROTECTIONZONE); ReturnValue ret = tile->queryAdd(0, *creature, 1, FLAG_IGNOREBLOCKITEM); foundTile = forceLogin || ret == RETURNVALUE_NOERROR || ret == RETURNVALUE_PLAYERISNOTINVITED; } else { placeInPZ = false; foundTile = false; } and replace it with this Tile* tile = getTile(centerPos.x, centerPos.y, centerPos.z); if (tile) { placeInPZ = tile->hasFlag(TILESTATE_PROTECTIONZONE); // account manager uint32_t flags = FLAG_IGNOREBLOCKITEM; if(creature->isAccountManager()) flags |= FLAG_IGNOREBLOCKCREATURE; // -- ReturnValue ret = tile->queryAdd(0, *creature, 1, flags); foundTile = forceLogin || ret == RETURNVALUE_NOERROR || ret == RETURNVALUE_PLAYERISNOTINVITED; } else { placeInPZ = false; foundTile = false; } player.cpp find this #include <bitset> and place this under it // account manager #include "ban.h" // -- look for this Player::Player(ProtocolGame_ptr p) : Creature(), lastPing(OTSYS_TIME()), lastPong(lastPing), inbox(new Inbox(ITEM_INBOX)), client(std::move(p)) { inbox->incrementReferenceCounter(); } and replace it with this Player::Player(ProtocolGame_ptr p) : Creature(), lastPing(OTSYS_TIME()), lastPong(lastPing), inbox(new Inbox(ITEM_INBOX)), client(std::move(p)) { // account manager for(int8_t i = 0; i <= states; i++) { talkState[i] = false; } accountManager = MANAGER_NONE; // -- inbox->incrementReferenceCounter(); } add this // account manager void Player::manageAccount(const std::string &text) { std::stringstream msg; msg << "Account Manager: "; bool noSwap = true; switch(accountManager) { case MANAGER_NAMELOCK: { if(!talkState[1]) { managerString = text; trimString(managerString); if(managerString.length() < 4) msg << "Your name you want is too short, please select a longer name."; else if(managerString.length() > 20) msg << "The name you want is too long, please select a shorter name."; else if(!isValidName(managerString)) msg << "That name seems to contain invalid symbols, please choose another name."; else if(IOLoginData::getInstance()->playerExists(managerString, true)) msg << "A player with that name already exists, please choose another name."; else { std::string tmp = asLowerCaseString(managerString); if(tmp.substr(0, 4) != "god " && tmp.substr(0, 3) != "cm " && tmp.substr(0, 3) != "gm ") { talkState[1] = true; talkState[2] = true; msg << managerString << ", are you sure?"; } else msg << "Your character is not a staff member, please tell me another name!"; } } else if(checkText(text, "no") && talkState[2]) { talkState[1] = talkState[2] = false; msg << "What else would you like to name your character?"; } else if(checkText(text, "yes") && talkState[2]) { if(!IOLoginData::getInstance()->playerExists(managerString, true)) { uint32_t tmp = IOLoginData::getInstance()->getGuidByName(managerString2); if(tmp != 0 && IOLoginData::getInstance()->changeName(tmp, managerString, managerString2) && IOBan::isPlayerNamelocked(tmp)) { if(House* house = g_game.map.houses.getHouseByPlayerId(tmp)) house->updateDoorDescription(managerString); talkState[1] = true; talkState[2] = false; msg << "Your character has been successfully renamed, you should now be able to login at it without any problems."; } else { talkState[1] = talkState[2] = false; msg << "Failed to change your name, please try again."; } } else { talkState[1] = talkState[2] = false; msg << "A player with that name already exists, please choose another name."; } } else msg << "Sorry, but I can't understand you, please try to repeat that!"; break; } case MANAGER_ACCOUNT: { // pr(" manage account case ", managerNumber); Account account = IOLoginData::getInstance()->loadAccount(managerNumber); if(checkText(text, "cancel") || (checkText(text, "account") && !talkState[1])) { talkState[1] = true; for(int8_t i = 2; i <= 12; i++) talkState[i] = false; msg << "Do you want to change your 'password', add a 'character', or 'delete' a character?"; } else if(checkText(text, "delete") && talkState[1]) { talkState[1] = false; talkState[2] = true; msg << "Which character would you like to delete?"; } else if(talkState[2]) { std::string tmp = text; trimString(tmp); if(!isValidName(tmp, false)) msg << "That name contains invalid characters, try to say your name again, you might have typed it wrong."; else { talkState[2] = false; talkState[3] = true; managerString = tmp; msg << "Do you really want to delete the character named " << managerString << "?"; } } else if(checkText(text, "yes") && talkState[3]) { switch(IOLoginData::getInstance()->deleteCharacter(managerNumber, managerString)) { case DELETE_INTERNAL: msg << "An error occured while deleting your character. Either the character does not belong to you or it doesn't exist."; break; case DELETE_SUCCESS: msg << "Your character has been deleted."; break; case DELETE_HOUSE: msg << "Your character owns a house. To make sure you really want to lose your house by deleting your character, you have to login and leave the house or pass it to someone else first."; break; case DELETE_LEADER: msg << "Your character is the leader of a guild. You need to disband or pass the leadership someone else to delete your character."; break; case DELETE_ONLINE: msg << "A character with that name is currently online, to delete a character it has to be offline."; break; } talkState[1] = true; for(int8_t i = 2; i <= 12; i++) talkState[i] = false; } else if(checkText(text, "no") && talkState[3]) { talkState[1] = true; talkState[3] = false; msg << "Tell me what character you want to delete."; } else if(checkText(text, "password") && talkState[1]) { talkState[1] = false; talkState[4] = true; msg << "Tell me your new password please."; } else if(talkState[4]) { std::string tmp = text; trimString(tmp); if(tmp.length() < 6) msg << "That password is too short, at least 6 digits are required. Please select a longer password."; else if(!isValidPassword(tmp)) msg << "Your password contains invalid characters... please tell me another one."; else { talkState[4] = false; talkState[5] = true; managerString = tmp; msg << "Should '" << managerString << "' be your new password?"; } } else if(checkText(text, "yes") && talkState[5]) { talkState[1] = true; for(int8_t i = 2; i <= 12; i++) talkState[i] = false; IOLoginData::getInstance()->setPassword(managerNumber, managerString); msg << "Your password has been changed."; } else if(checkText(text, "no") && talkState[5]) { talkState[1] = true; for(int8_t i = 2; i <= 12; i++) talkState[i] = false; msg << "Then not."; } else if(checkText(text, "character") && talkState[1]) { if(account.characters.size() <= 15) { talkState[1] = false; talkState[6] = true; msg << "What would you like as your character name?"; } else { talkState[1] = true; for(int8_t i = 2; i <= 12; i++) talkState[i] = false; msg << "Your account reach the limit of 15 players, you can 'delete' a character if you want to create a new one."; } } else if(talkState[6]) { managerString = text; trimString(managerString); if(managerString.length() < 4) msg << "Your name you want is too short, please select a longer name."; else if(managerString.length() > 20) msg << "The name you want is too long, please select a shorter name."; else if(!isValidName(managerString)) msg << "That name seems to contain invalid symbols, please choose another name."; else if(IOLoginData::getInstance()->playerExists(managerString, true)) msg << "A player with that name already exists, please choose another name."; else { std::string tmp = asLowerCaseString(managerString); if(tmp.substr(0, 4) != "god " && tmp.substr(0, 3) != "cm " && tmp.substr(0, 3) != "gm ") { talkState[6] = false; talkState[7] = true; msg << managerString << ", are you sure?"; } else msg << "Your character is not a staff member, please tell me another name!"; } } else if(checkText(text, "no") && talkState[7]) { talkState[6] = true; talkState[7] = false; msg << "What else would you like to name your character?"; } else if(checkText(text, "yes") && talkState[7]) { talkState[7] = false; talkState[8] = true; msg << "Should your character be a 'male' or a 'female'."; } else if(talkState[8] && (checkText(text, "female") || checkText(text, "male"))) { talkState[8] = false; talkState[9] = true; if(checkText(text, "female")) { msg << "A female, are you sure?"; managerSex = PLAYERSEX_FEMALE; } else { msg << "A male, are you sure?"; managerSex = PLAYERSEX_MALE; } } else if(checkText(text, "no") && talkState[9]) { talkState[8] = true; talkState[9] = false; msg << "Tell me... would you like to be a 'male' or a 'female'?"; } else if(checkText(text, "yes") && talkState[9]) { if(g_config.getBoolean(ConfigManager::AM_CHOOSEVOC)) { talkState[9] = false; talkState[11] = true; bool firstPart = true; g_vocations.getVocationMap(firstPart, msg); } else if(!IOLoginData::getInstance()->playerExists(managerString, true)) { talkState[1] = true; for(int8_t i = 2; i <= 12; i++) talkState[i] = false; if(IOLoginData::getInstance()->createCharacter(managerNumber, managerString, managerNumber2, (uint16_t)managerSex)) msg << "Your character has been created."; else msg << "Your character couldn't be created, please try again."; } else { talkState[6] = true; talkState[9] = false; msg << "A player with that name already exists, please choose another name."; } } else if(talkState[11]) { g_vocations.getVocationConfirmation(text, talkState[11], talkState[12], managerNumber2, msg); if(msg.str().length() == 17) msg << "I don't understand what vocation you would like to be... could you please repeat it?"; } else if(checkText(text, "yes") && talkState[12]) { if(!IOLoginData::getInstance()->playerExists(managerString, true)) { talkState[1] = true; for(int8_t i = 2; i <= 12; i++) talkState[i] = false; if(IOLoginData::getInstance()->createCharacter(managerNumber, managerString, managerNumber2, (uint16_t)managerSex)) msg << "Your character has been created."; else msg << "Your character couldn't be created, please try again."; } else { talkState[6] = true; talkState[9] = false; msg << "A player with that name already exists, please choose another name."; } } else if(checkText(text, "no") && talkState[12]) { talkState[11] = true; talkState[12] = false; msg << "No? Then what would you like to be?"; } else if(checkText(text, "recovery key") && talkState[1]) { talkState[1] = false; talkState[10] = true; msg << "Would you like a recovery key?"; } else if(checkText(text, "yes") && talkState[10]) { // std::cout << "recovery key " << account.recoveryKey << " empty? " << account.recoveryKey.empty() <<std::endl; /* if(!account.recoveryKey.empty()) msg << "Sorry, you already have a recovery key, for security reasons I may not give you a new one."; else { managerString = generateRecoveryKey(4, 4); IOLoginData::getInstance()->setRecoveryKey(managerNumber, managerString); msg << "Your recovery key is: " << managerString << "."; } */ talkState[1] = true; for(int8_t i = 2; i <= 12; i++) talkState[i] = false; } else if(checkText(text, "no") && talkState[10]) { msg << "Then not."; talkState[1] = true; for(int8_t i = 2; i <= 12; i++) talkState[i] = false; } else msg << "Please read the latest message that I have specified, I don't understand the current requested action."; break; } case MANAGER_NEW: { if(checkText(text, "account") && !talkState[1]) { msg << "What would you like your password to be?"; talkState[1] = true; talkState[2] = true; } else if(talkState[2]) { std::string tmp = text; trimString(tmp); if(tmp.length() < 6) msg << "That password is too short, at least 6 digits are required. Please select a longer password."; else if(!isValidPassword(tmp)) msg << "Your password contains invalid characters... please tell me another one."; else { talkState[3] = true; talkState[2] = false; managerString = tmp; msg << managerString << " is it? 'yes' or 'no'?"; } } else if(checkText(text, "yes") && talkState[3]) { if(g_config.getBoolean(ConfigManager::AM_GENERATE_ACCOUNT_NUMBER)) { do sprintf(managerChar, "%d%d%d%d%d%d%d", random_range(2, 9), random_range(2, 9), random_range(2, 9), random_range(2, 9), random_range(2, 9), random_range(2, 9), random_range(2, 9)); while(IOLoginData::getInstance()->accountNameExists(managerChar)); uint32_t id = (uint32_t)IOLoginData::getInstance()->createAccount(managerChar, managerString); if(id) { accountManager = MANAGER_ACCOUNT; managerNumber = id; noSwap = talkState[1] = false; msg << "Your account has been created, you may manage it now, but remember your account name: '" << managerChar << "' and password: '" << managerString << "'! If the account name is too hard to remember, please note it somewhere."; } else msg << "Your account could not be created, please try again."; for(int8_t i = 2; i <= 5; i++) talkState[i] = false; } else { msg << "What would you like your account name to be?"; talkState[3] = false; talkState[4] = true; } } else if(checkText(text, "no") && talkState[3]) { talkState[2] = true; talkState[3] = false; msg << "What would you like your password to be then?"; } else if(talkState[4]) { std::string tmp = text; trimString(tmp); if(tmp.length() < 3) msg << "That account name is too short, at least 3 digits are required. Please select a longer account name."; else if(tmp.length() > 25) msg << "That account name is too long, not more than 25 digits are required. Please select a shorter account name."; else if(!isValidAccountName(tmp)) msg << "Your account name contains invalid characters, please choose another one."; else if(asLowerCaseString(tmp) == asLowerCaseString(managerString)) msg << "Your account name cannot be same as password, please choose another one."; else { sprintf(managerChar, "%s", tmp.c_str()); msg << managerChar << ", are you sure?"; talkState[4] = false; talkState[5] = true; } } else if(checkText(text, "yes") && talkState[5]) { if(!IOLoginData::getInstance()->accountNameExists(managerChar)) { uint32_t id = (uint32_t)IOLoginData::getInstance()->createAccount(managerChar, managerString); if(id) { accountManager = MANAGER_ACCOUNT; managerNumber = id; noSwap = talkState[1] = false; msg << "Your account has been created, you may manage it now, but remember your account name: '" << managerChar << "' and password: '" << managerString << "'!"; } else msg << "Your account could not be created, please try again."; for(int8_t i = 2; i <= 5; i++) talkState[i] = false; } else { msg << "An account with that name already exists, please try another account name."; talkState[4] = true; talkState[5] = false; } } else if(checkText(text, "no") && talkState[5]) { talkState[5] = false; talkState[4] = true; msg << "What else would you like as your account name?"; } else if(checkText(text, "recover") && !talkState[6]) { talkState[6] = true; talkState[7] = true; msg << "What was your account name?"; } else if(talkState[7]) { managerString = text; if(IOLoginData::getInstance()->getAccountId(managerString, (uint32_t&)managerNumber)) { talkState[7] = false; talkState[8] = true; msg << "What was your recovery key?"; } else { msg << "Sorry, but account with such name doesn't exists."; talkState[6] = talkState[7] = false; } } else if(talkState[8]) { managerString2 = text; if(IOLoginData::getInstance()->validRecoveryKey(managerNumber, managerString2) && managerString2 != "0") { sprintf(managerChar, "%s%d", g_config.getString(ConfigManager::SERVER_NAME).c_str(), random_range(100, 999)); IOLoginData::getInstance()->setPassword(managerNumber, managerChar); msg << "Correct! Your new password is: " << managerChar << "."; } else msg << "Sorry, but this key doesn't match to account you gave me."; talkState[7] = talkState[8] = false; } else msg << "Sorry, but I can't understand you, please try to repeat that."; break; } default: return; break; } sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, msg.str().c_str()); if(!noSwap) sendTextMessage(MESSAGE_STATUS_CONSOLE_ORANGE, "Hint: Type 'account' to manage your account and if you want to start over then type 'cancel'."); } // -- player.h find class Guild; and add this below it // account manager enum AccountManager_t { MANAGER_NONE, MANAGER_NEW, MANAGER_ACCOUNT, MANAGER_NAMELOCK }; // -- next find this void addList() override; and add this below // account manager void manageAccount(const std::string& text); bool isAccountManager() const {return (accountManager != MANAGER_NONE);} template <typename T> inline void pr(std::string s, T const& v) { std::cout << s << v << std::endl; }; // -- find this void disconnect() { if (client) { client->disconnect(); } } place this under it // account manager bool isVirtual() const { return (getID() == 0); } // -- find this int32_t idleTime = 0; and place this below it // account manager int32_t managerNumber; uint32_t managerNumber2; std::string managerString, managerString2; int8_t states = 13; bool talkState[13]; // -- find this OperatingSystem_t operatingSystem = CLIENTOS_NONE; and place this below it // account manager AccountManager_t accountManager = MANAGER_NONE; PlayerSex_t managerSex; char managerChar[100]; // -- protocolgame.cpp find this void ProtocolGame::login(const std::string& name, uint32_t accountId, OperatingSystem_t operatingSystem) find this void ProtocolGame::login(const std::string& name, uint32_t accountId, OperatingSystem_t operatingSystem) { //dispatcher thread Player* foundPlayer = g_game.getPlayerByName(name); if (!foundPlayer || g_config.getBoolean(ConfigManager::ALLOW_CLONES)) { player = new Player(getThis()); player->setName(name); player->incrementReferenceCounter(); player->setID(); if (!IOLoginData::preloadPlayer(player, name)) { disconnectClient("Your character could not be loaded."); return; } if (IOBan::isPlayerNamelocked(player->getGUID())) { disconnectClient("Your character has been namelocked."); return; } and replace it with this void ProtocolGame::login(const std::string& name, uint32_t accountId, OperatingSystem_t operatingSystem) { //dispatcher thread Player* foundPlayer = g_game.getPlayerByName(name); if(!foundPlayer || name == "Account Manager" || g_config.getBoolean(ConfigManager::ALLOW_CLONES) ) { player = new Player(getThis()); player->setName(name); player->incrementReferenceCounter(); player->setID(); if (!IOLoginData::preloadPlayer(player, name)) { disconnectClient("Your character could not be loaded."); return; } if (IOBan::isPlayerNamelocked(player->getGUID()) && accountId != 1) { if(g_config.getBoolean(ConfigManager::NAMELOCK_MANAGER)) { player->name = "Account Manager"; player->accountManager = MANAGER_NAMELOCK; player->managerNumber = accountId; player->managerString2 = name; } else { disconnectClient("Your character has been namelocked."); return; } }else if(player->getName() == "Account Manager" && g_config.getBoolean(ConfigManager::ACCOUNT_MANAGER)) { if(accountId != 1) { player->accountManager = MANAGER_ACCOUNT; player->managerNumber = accountId; } else { player->accountManager = MANAGER_NEW; } } in that same method underneath this if (g_game.getGameState() == GAME_STATE_CLOSING && !player->hasFlag(PlayerFlag_CanAlwaysLogin)) { disconnectClient("The game is just going down.\nPlease try again later."); return; } if (g_game.getGameState() == GAME_STATE_CLOSED && !player->hasFlag(PlayerFlag_CanAlwaysLogin)) { disconnectClient("Server is currently closed.\nPlease try again later."); return; } place this // account manager if (g_config.getBoolean(ConfigManager::ONE_PLAYER_ON_ACCOUNT) && !player->isAccountManager() && player->getAccountType() < ACCOUNT_TYPE_GAMEMASTER && g_game.getPlayerByAccount(player->getAccount())) { bool found = false; std::vector<Player*> tmp; tmp.push_back(g_game.getPlayerByAccount(accountId)); for(std::vector<Player*>::iterator it = tmp.begin(); it != tmp.end(); ++it) { if((*it)->getName() != name) continue; found = true; break; } if(tmp.size() > 0 && !found) { disconnectClient("You may only login with one character\nof your account at the same time."); return; } } // -- find this void ProtocolGame::onRecvFirstMessage(NetworkMessage& msg) inside of there you are going to look for this std::string& token = sessionArgs[2]; uint32_t tokenTime = 0; try { tokenTime = std::stoul(sessionArgs[3]); } catch (const std::invalid_argument&) { disconnectClient("Malformed token packet."); return; } catch (const std::out_of_range&) { disconnectClient("Token time is too long."); return; } if (accountName.empty()) { disconnectClient("You must enter your account name."); return; } replace it with this // account manager if (accountName.empty()) { accountName = "1"; } if (password.empty()) { password = "1"; } // -- within that same method you will look for this uint32_t accountId = IOLoginData::gameworldAuthentication(accountName, password, characterName, token, tokenTime); if (accountId == 0) { disconnectClient("Account name or password is not correct."); return; } g_dispatcher.addTask(createTask(std::bind(&ProtocolGame::login, getThis(), characterName, accountId, operatingSystem))); } and replace it with this uint32_t accountId = atoi(accountName.c_str()); g_dispatcher.addTask(createTask(std::bind(&ProtocolGame::login, getThis(), characterName, accountId, operatingSystem))); } this whole method you are going to replace void ProtocolGame::parsePacket(NetworkMessage& msg) void ProtocolGame::parsePacket(NetworkMessage& msg) { if (!acceptPackets || g_game.getGameState() == GAME_STATE_SHUTDOWN || msg.getLength() <= 0) { return; } uint8_t recvbyte = msg.getByte(); if (!player) { if (recvbyte == 0x0F) { disconnect(); } return; } //a dead player can not performs actions if (player->isRemoved() || player->getHealth() <= 0) { if (recvbyte == 0x0F) { disconnect(); return; } if (recvbyte != 0x14) { return; } } // account manager if(player->isAccountManager()) { switch(recvbyte) { case 0x14: g_dispatcher.addTask(createTask(std::bind(&ProtocolGame::logout, getThis(), true, false))); break; break; case 0x96: parseSay(msg); break; default: sendCancelWalk(); break; } } else { // -- switch (recvbyte) { case 0x14: g_dispatcher.addTask(createTask(std::bind(&ProtocolGame::logout, getThis(), true, false))); break; case 0x1D: addGameTask(&Game::playerReceivePingBack, player->getID()); break; case 0x1E: addGameTask(&Game::playerReceivePing, player->getID()); break; case 0x32: parseExtendedOpcode(msg); break; //otclient extended opcode case 0x64: parseAutoWalk(msg); break; case 0x65: addGameTask(&Game::playerMove, player->getID(), DIRECTION_NORTH); break; case 0x66: addGameTask(&Game::playerMove, player->getID(), DIRECTION_EAST); break; case 0x67: addGameTask(&Game::playerMove, player->getID(), DIRECTION_SOUTH); break; case 0x68: addGameTask(&Game::playerMove, player->getID(), DIRECTION_WEST); break; case 0x69: addGameTask(&Game::playerStopAutoWalk, player->getID()); break; case 0x6A: addGameTask(&Game::playerMove, player->getID(), DIRECTION_NORTHEAST); break; case 0x6B: addGameTask(&Game::playerMove, player->getID(), DIRECTION_SOUTHEAST); break; case 0x6C: addGameTask(&Game::playerMove, player->getID(), DIRECTION_SOUTHWEST); break; case 0x6D: addGameTask(&Game::playerMove, player->getID(), DIRECTION_NORTHWEST); break; case 0x6F: addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerTurn, player->getID(), DIRECTION_NORTH); break; case 0x70: addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerTurn, player->getID(), DIRECTION_EAST); break; case 0x71: addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerTurn, player->getID(), DIRECTION_SOUTH); break; case 0x72: addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerTurn, player->getID(), DIRECTION_WEST); break; case 0x77: parseEquipObject(msg); break; case 0x78: parseThrow(msg); break; case 0x79: parseLookInShop(msg); break; case 0x7A: parsePlayerPurchase(msg); break; case 0x7B: parsePlayerSale(msg); break; case 0x7C: addGameTask(&Game::playerCloseShop, player->getID()); break; case 0x7D: parseRequestTrade(msg); break; case 0x7E: parseLookInTrade(msg); break; case 0x7F: addGameTask(&Game::playerAcceptTrade, player->getID()); break; case 0x80: addGameTask(&Game::playerCloseTrade, player->getID()); break; case 0x82: parseUseItem(msg); break; case 0x83: parseUseItemEx(msg); break; case 0x84: parseUseWithCreature(msg); break; case 0x85: parseRotateItem(msg); break; case 0x87: parseCloseContainer(msg); break; case 0x88: parseUpArrowContainer(msg); break; case 0x89: parseTextWindow(msg); break; case 0x8A: parseHouseWindow(msg); break; case 0x8C: parseLookAt(msg); break; case 0x8D: parseLookInBattleList(msg); break; case 0x8E: /* join aggression */ break; case 0x96: parseSay(msg); break; case 0x97: addGameTask(&Game::playerRequestChannels, player->getID()); break; case 0x98: parseOpenChannel(msg); break; case 0x99: parseCloseChannel(msg); break; case 0x9A: parseOpenPrivateChannel(msg); break; case 0x9E: addGameTask(&Game::playerCloseNpcChannel, player->getID()); break; case 0xA0: parseFightModes(msg); break; case 0xA1: parseAttack(msg); break; case 0xA2: parseFollow(msg); break; case 0xA3: parseInviteToParty(msg); break; case 0xA4: parseJoinParty(msg); break; case 0xA5: parseRevokePartyInvite(msg); break; case 0xA6: parsePassPartyLeadership(msg); break; case 0xA7: addGameTask(&Game::playerLeaveParty, player->getID()); break; case 0xA8: parseEnableSharedPartyExperience(msg); break; case 0xAA: addGameTask(&Game::playerCreatePrivateChannel, player->getID()); break; case 0xAB: parseChannelInvite(msg); break; case 0xAC: parseChannelExclude(msg); break; case 0xBE: addGameTask(&Game::playerCancelAttackAndFollow, player->getID()); break; case 0xC9: /* update tile */ break; case 0xCA: parseUpdateContainer(msg); break; case 0xCB: parseBrowseField(msg); break; case 0xCC: parseSeekInContainer(msg); break; case 0xD2: addGameTask(&Game::playerRequestOutfit, player->getID()); break; case 0xD3: parseSetOutfit(msg); break; case 0xD4: parseToggleMount(msg); break; case 0xDC: parseAddVip(msg); break; case 0xDD: parseRemoveVip(msg); break; case 0xDE: parseEditVip(msg); break; case 0xE6: parseBugReport(msg); break; case 0xE7: /* thank you */ break; case 0xE8: parseDebugAssert(msg); break; case 0xF0: addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerShowQuestLog, player->getID()); break; case 0xF1: parseQuestLine(msg); break; case 0xF2: parseRuleViolationReport(msg); break; case 0xF3: /* get object info */ break; case 0xF4: parseMarketLeave(); break; case 0xF5: parseMarketBrowse(msg); break; case 0xF6: parseMarketCreateOffer(msg); break; case 0xF7: parseMarketCancelOffer(msg); break; case 0xF8: parseMarketAcceptOffer(msg); break; case 0xF9: parseModalWindowAnswer(msg); break; default: // std::cout << "Player: " << player->getName() << " sent an unknown packet header: 0x" << std::hex << static_cast<uint16_t>(recvbyte) << std::dec << "!" << std::endl; break; } } // end of else if (msg.isOverrun()) { disconnect(); } } protocollogin.cpp within this method void ProtocolLogin::onRecvFirstMessage(NetworkMessage& msg) find this std::string accountName = msg.getString(); if (accountName.empty()) { disconnectClient("Invalid account name.", version); return; } std::string password = msg.getString(); if (password.empty()) { disconnectClient("Invalid password.", version); return; } replace it with this std::string accountName = msg.getString(); if (accountName.empty()) { // account manager if(!g_config.getBoolean(ConfigManager::ACCOUNT_MANAGER)){ disconnectClient("Invalid account name.", version); return; } accountName = "1"; } std::string password = msg.getString(); if (password.empty()) { // account manager if(!g_config.getBoolean(ConfigManager::ACCOUNT_MANAGER)){ disconnectClient("Invalid password.", version); return; } password = "1"; // sha1 "356a192b7913b04c54574d18c28d46e6395428ab" } tools.cpp add this to the bottom of the file // account manager int32_t round(float v) { int32_t t = (int32_t)std::floor(v); if((v - t) > 0.5) return t + 1; return t; } uint32_t rand24b() { return ((rand() << 12) ^ (rand())) & (0xFFFFFF); } float box_muller(float m, float s) { // normal random variate generator // mean m, standard deviation s float x1, x2, w, y1; static float y2; static bool useLast = false; if(useLast) // use value from previous call { y1 = y2; useLast = false; return (m + y1 * s); } do { double r1 = (((float)(rand()) / RAND_MAX)); double r2 = (((float)(rand()) / RAND_MAX)); x1 = 2.0 * r1 - 1.0; x2 = 2.0 * r2 - 1.0; w = x1 * x1 + x2 * x2; } while(w >= 1.0); w = sqrt((-2.0 * log(w)) / w); y1 = x1 * w; y2 = x2 * w; useLast = true; return (m + y1 * s); } int32_t random_range(int32_t lowestNumber, int32_t highestNumber, DistributionType_t type /*= DISTRO_UNIFORM*/) { if(highestNumber == lowestNumber) return lowestNumber; if(lowestNumber > highestNumber) std::swap(lowestNumber, highestNumber); switch(type) { case DISTRO_UNIFORM: return (lowestNumber + ((int32_t)rand24b() % (highestNumber - lowestNumber + 1))); case DISTRO_NORMAL: return (lowestNumber + int32_t(float(highestNumber - lowestNumber) * (float)std::min((float)1, std::max((float)0, box_muller(0.5, 0.25))))); default: break; } const float randMax = 16777216; return (lowestNumber + int32_t(float(highestNumber - lowestNumber) * float(1.f - sqrt((1.f * rand24b()) / randMax)))); } bool isLowercaseLetter(char character) { return (character >= 97 && character <= 122); } bool isUppercaseLetter(char character) { return (character >= 65 && character <= 90); } bool isNumber(char character) { return (character >= 48 && character <= 57); } bool isNumbers(std::string text) { uint32_t textLength = text.length(); for(uint32_t size = 0; size < textLength; size++) { if(!isNumber(text[size])) return false; } return true; } bool checkText(std::string text, std::string str) { trimString(text); return asLowerCaseString(text) == str; } std::string generateRecoveryKey(int32_t fieldCount, int32_t fieldLenght) { std::stringstream key; int32_t i = 0, j = 0, lastNumber = 99, number = 0; char character = 0, lastCharacter = 0; bool madeNumber = false, madeCharacter = false; do { do { madeNumber = madeCharacter = false; if((bool)random_range(0, 1)) { number = random_range(2, 9); if(number != lastNumber) { key << number; lastNumber = number; madeNumber = true; } } else { character = (char)random_range(65, 90); if(character != lastCharacter) { key << character; lastCharacter = character; madeCharacter = true; } } } while((!madeCharacter && !madeNumber) ? true : (++j && j < fieldLenght)); lastCharacter = character = number = j = 0; lastNumber = 99; if(i < fieldCount - 1) key << "-"; } while(++i && i < fieldCount); return key.str(); } bool isValidAccountName(std::string text) { toLowerCaseString(text); uint32_t textLength = text.length(); for(uint32_t size = 0; size < textLength; size++) { if(!isLowercaseLetter(text[size]) && !isNumber(text[size])) return false; } return true; } bool isValidPassword(std::string text) { toLowerCaseString(text); uint32_t textLength = text.length(); for(uint32_t size = 0; size < textLength; size++) { if(!isLowercaseLetter(text[size]) && !isNumber(text[size]) && !isPasswordCharacter(text[size])) return false; } return true; } bool isPasswordCharacter(char character) { return ((character >= 33 && character <= 47) || (character >= 58 && character <= 64) || (character >= 91 && character <= 96) || (character >= 123 && character <= 126)); } bool isValidName(std::string text, bool forceUppercaseOnFirstLetter/* = true*/) { uint32_t textLength = text.length(), lenBeforeSpace = 1, lenBeforeQuote = 1, lenBeforeDash = 1, repeatedCharacter = 0; char lastChar = 32; if(forceUppercaseOnFirstLetter) { if(!isUppercaseLetter(text[0])) return false; } else if(!isLowercaseLetter(text[0]) && !isUppercaseLetter(text[0])) return false; for(uint32_t size = 1; size < textLength; size++) { if(text[size] != 32) { lenBeforeSpace++; if(text[size] != 39) lenBeforeQuote++; else { if(lenBeforeQuote <= 1 || size == textLength - 1 || text[size + 1] == 32) return false; lenBeforeQuote = 0; } if(text[size] != 45) lenBeforeDash++; else { if(lenBeforeDash <= 1 || size == textLength - 1 || text[size + 1] == 32) return false; lenBeforeDash = 0; } if(text[size] == lastChar) { repeatedCharacter++; if(repeatedCharacter > 2) return false; } else repeatedCharacter = 0; lastChar = text[size]; } else { if(lenBeforeSpace <= 1 || size == textLength - 1 || text[size + 1] == 32) return false; lenBeforeSpace = lenBeforeQuote = lenBeforeDash = 0; } if(!(isLowercaseLetter(text[size]) || text[size] == 32 || text[size] == 39 || text[size] == 45 || (isUppercaseLetter(text[size]) && text[size - 1] == 32))) return false; } return true; } // -- tools.h find this #include "enums.h" and place this under it // account manager enum DistributionType_t { DISTRO_UNIFORM, DISTRO_SQUARE, DISTRO_NORMAL }; enum FileType_t { FILE_TYPE_XML, FILE_TYPE_LOG, FILE_TYPE_OTHER, FILE_TYPE_CONFIG, FILE_TYPE_MOD }; // -- find this std::string ucfirst(std::string str); std::string ucwords(std::string str); bool booleanString(const std::string& str); place this under it // account manager bool isValidAccountName(std::string text); bool isValidPassword(std::string text); bool isValidName(std::string text, bool forceUppercaseOnFirstLetter = true); bool isNumber(char character); bool isNumbers(std::string text); bool isPasswordCharacter(char character); bool checkText(std::string text, std::string str); std::string generateRecoveryKey(int32_t fieldCount, int32_t fieldLength); int32_t random_range(int32_t lowest_number, int32_t highest_number, DistributionType_t type = DISTRO_UNIFORM); int32_t round(float v); uint32_t rand24b(); float box_muller(float m, float s); // -- vocation.cpp // account manager void Vocations::getVocationMap(bool &firstPart, std::stringstream &msg) { for(auto it = vocationsMap.begin(); it != vocationsMap.end(); ++it) { bool isXml = g_config.getBoolean(ConfigManager::USE_XML); if(it->first == ((isXml) ? it->second.getFromVocation() : it->second.getId()) && it->first != 0) { if(firstPart) { msg << "What do you want to be... " << ((isXml) ? it->second.getVocDescription() : it->second.getVocName()); firstPart = false; } else if(it->first - 1 != 0){ msg << ", " << ((isXml) ? it->second.getVocDescription() : it->second.getVocName()); } else{ msg << " or " << ((isXml) ? it->second.getVocDescription() : it->second.getVocName()) << "."; } } } } // needs to set the proper vocation id void Vocations::getVocationConfirmation(std::string text, bool &talkState1, bool &talkState2, uint32_t &number /* int32_t &number */, std::stringstream &msg) { for(auto it = vocationsMap.begin(); it != vocationsMap.end(); ++it) { bool isXml = g_config.getBoolean(ConfigManager::USE_XML); std::string tmp = asLowerCaseString(it->second.getVocName()); if(checkText(text, tmp) && it != vocationsMap.end() && it->first == ((isXml) ? it->second.getFromVocation() : it->second.getId()) && it->first != 0) { msg << "So you would like to be " << ((isXml) ? it->second.getVocDescription() : it->second.getVocName()) << "... are you sure?"; number = it->first; talkState1 = false; talkState2 = true; } } } vocation.h find uint16_t getPromotedVocation(uint16_t vocationId) const; and place this underneath // account manager void getVocationMap(bool &firstPart, std::stringstream &msg); void getVocationConfirmation(std::string, bool&, bool&, uint32_t& /* int32_t& */, std::stringstream&); // -- modify your login.lua function onLogin(player) local accountManager = player:getAccountManager() if(accountManager == MANAGER_NONE) then local loginStr = "Welcome to " .. configManager.getString(configKeys.SERVER_NAME) .. "!" if player:getLastLoginSaved() <= 0 then loginStr = loginStr .. " Please choose your outfit." player:sendOutfitWindow() else if loginStr ~= "" then player:sendTextMessage(MESSAGE_STATUS_DEFAULT, loginStr) end loginStr = string.format("Your last visit was on %s.", os.date("%a %b %d %X %Y", player:getLastLoginSaved())) end player:sendTextMessage(MESSAGE_STATUS_DEFAULT, loginStr) elseif(accountManager == MANAGER_NAMELOCK) then player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "Hello, it appears that your character has been namelocked, what would you like as your new name?") elseif(accountManager == MANAGER_ACCOUNT) then player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "Hello, type 'account' to manage your account and if you want to start over then type 'cancel'.") elseif(accountManager == MANAGER_NEW) then player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "Hello, type 'account' to create an account or type 'recover' to recover an account.") end and finally add this to your config.lua -- Account Manager accountManager = true -- how many points do you want to give each account -- this applies only when they create the account vipPoints = 0 -- the default stats to start with experience = 0 -- uint64_t level = 1 soul = 100 -- uint8_t magic = 0 fist = 10 club = 10 sword = 10 axe = 10 shield = 10 distance = 10 fish = 10 -- incase you want to give the player a little starter money in their bank :) balance = 0 -- uint64_t offlineTrainingTime = 43200 -- int32_t stamina = 2520 -- uint16_t startTownId = 1 -- uint32_t temple_x = 0 temple_y = 0 temple_z = 0 baseHP = 150 baseMP = 0 baseCAP = 400 maleOutfit = 128 femaleOutfit = 136 -- give the player addons for their corresponding outfit if any lookAddons = 0 -- start the player off with a mount mountId = 0 chooseVocation = true generateAccountNumber = false useXML = true Here is a video demonstration of it working in OTX3 8.6, I have not made a video for it in TFS 1.3 because I added somethings to after making this video
-
[TFS 0.4] Summon andando do lado do jogador
Olá senhores. Gostaria de uma ajuda com um script de summon que venho trabalhando no momento, gostaria que o summon andasse do lado do jogador, entretanto o mesmo sempre fica para trás ou a frente do jogador. Efetuei a alteração na source creature.cpp: void Creature::getPathSearchParams(const Creature* creature, FindPathParams& fpp) const { fpp.fullPathSearch = !hasFollowPath; fpp.clearSight = true; if(creature->isPlayerSummon()) { if(creature->getName() == "Summon Name") fpp.clearSight = false; } fpp.maxSearchDist = 12; fpp.minTargetDist = fpp.maxTargetDist = 1; } fpp.maxTargetDist = 1; Porém ele sempre mantem 1 de distancia do jogador, alterando para zero o "Zero" summon nem segue o jogador. Resultado: Agradeço desde já.
-
[TFS 0.4] CreatureScripts (onOpenRuleViolation) + Telegram Notification
O propósito é criar uma nova função em creaturescripts que será acionada toda vez que um novo report (CTRL + R) for aberto. Eu implementei para enviar uma notificação no grupo do Telegram, contendo os dados do report. Isso garantirá que os GMs tenham acesso aos reports dos jogadores mesmo quando não estiverem logados, e também evitará que algum report seja perdido caso o jogador saia do servidor. A parte do Telegram é apenas um exemplo. Você pode ajustar o script para executar outras ações desejadas. creatureevent.cpp: Dentro deste arquivo, localize a função: uint32_t CreatureEvent::executeChannelLeave(Player* player, uint16_t channelId, UsersMap usersMap) abaixo dela, adicione: uint32_t CreatureEvent::executeOpenRuleViolation(Player* player, std::string message) { if (!m_interface->reserveEnv()) { std::clog << "[Error - CreatureEvent::executeOpenRuleViolation] Call stack overflow." << std::endl; return 0; } ScriptEnviroment* env = m_interface->getEnv(); env->setScriptId(m_scriptId, m_interface); lua_State* L = m_interface->getState(); m_interface->pushFunction(m_scriptId); lua_pushnumber(L, env->addThing(player)); lua_pushstring(L, message.c_str()); bool result = m_interface->callFunction(2); m_interface->releaseEnv(); return result; } Após, procure por: std::string CreatureEvent::getScriptEventName() const abaixo de: case CREATURE_EVENT_CHANNEL_LEAVE: return "onLeaveChannel"; adicione: case CREATURE_EVENT_OPEN_RULE_VIOLATION: return "onOpenRuleViolation"; Agora, procure por: std::string CreatureEvent::getScriptEventParams() const abaixo de: case CREATURE_EVENT_CHANNEL_LEAVE: return "cid, channel, users"; adicione: case CREATURE_EVENT_OPEN_RULE_VIOLATION: return "cid, message"; Procure por: bool CreatureEvent::configureEvent(xmlNodePtr p) abaixo de: else if(tmpStr == "leavechannel") m_type = CREATURE_EVENT_CHANNEL_LEAVE; adicione: else if(tmpStr == "openruleviolation") m_type = CREATURE_EVENT_OPEN_RULE_VIOLATION; creatureevent.h: Dentro deste arquivo, localize: enum CreatureEventType_t adicione "CREATURE_EVENT_OPEN_RULE_VIOLATION" como o último item de enum CreatureEventType_t Exemplo: enum CreatureEventType_t { // ... CREATURE_EVENT_OPEN_RULE_VIOLATION }; Agora, procure por: uint32_t executeChannelLeave(Player* player, uint16_t channelId, UsersMap usersMap); abaixo dela, adicione: uint32_t executeOpenRuleViolation(Player* player, std::string message); game.cpp: Dentro deste arquivo, localize: bool Game::playerReportRuleViolation(Player* player, const std::string& text) e substitua por: bool Game::playerReportRuleViolation(Player* player, const std::string& text) { //Do not allow reports on multiclones worlds since reports are name-based if(g_config.getNumber(ConfigManager::ALLOW_CLONES)) { player->sendTextMessage(MSG_INFO_DESCR, "Rule violation reports are disabled."); return false; } cancelRuleViolation(player); boost::shared_ptr<RuleViolation> rvr(new RuleViolation(player, text, time(NULL))); ruleViolations[player->getID()] = rvr; ChatChannel* channel = g_chat.getChannelById(CHANNEL_RVR); if(!channel) return false; for(UsersMap::const_iterator it = channel->getUsers().begin(); it != channel->getUsers().end(); ++it) it->second->sendToChannel(player, SPEAK_RVR_CHANNEL, text, CHANNEL_RVR, rvr->time); CreatureEventList joinEvents = player->getCreatureEvents(CREATURE_EVENT_OPEN_RULE_VIOLATION); for(CreatureEventList::iterator it = joinEvents.begin(); it != joinEvents.end(); ++it) (*it)->executeOpenRuleViolation(player, text); return true; } Agora é só compilar a source. depois em "data > creaturescripts > creaturescripts.xml", adicione: <event type="login" name="loginNotifyRuleViolation" script="notifyRuleViolation.lua"/> <event type="openruleviolation" name="openNotifyRuleViolation" script="notifyRuleViolation.lua"/> em "data > creaturescripts > scripts", crie um arquivo notifyRuleViolation.lua e adicione: function onOpenRuleViolation(cid, message) local config = { token = "", -- Token do seu BOT no Telegram chatId = "" -- ID do chat do Telegram que será enviado a notificação. } local message = "Player: "..getCreatureName(cid).."\n\nReport:\n"..message.."" message = string.gsub(message, "\n", "%%0A") local url = "https://api.telegram.org/bot"..config.token.."/sendMessage" local data = "chat_id="..config.chatId.."&text="..message.."" local curl = io.popen('curl -d "'..data..'" "'..url..'"'):read("*a") return true end function onLogin(cid) registerCreatureEvent(cid, "openNotifyRuleViolation") return true end Demonstração: 1. Jogador abre um novo report (CTRL + R) 2. notifyRuleViolation.lua, definido em creaturescripts.xml, é acionado para enviar uma notificação ao grupo do Telegram.
-
AutoLoot Sources pra Otx2 (modificando o script do naze)
salve rapaziada, do TK, esses tempos eu tava a procura de um autoloot bom e eficiente, e como vocês devem saber, o autoloot usado nos scripts .lua são bem "pesados" o @Naze fez um excelentíssimo trabalho e disponibilizou um script de autoloot na source, porém como a vida é dificil, ele tem alguns bugs de função e um deles é no "remove autoloot" que quando o player digita "!autoloot remove, nome-do-item" nada acontece, eu decidi mexer e vim disponibilizar arrumado pra vcs. LEMBRANDO QUE 100% DOS CRÉDITOS SÃO AO @Naze porém se vc procurar no tópico dele ainda vai estar bugado, então resolvi deixar aqui certinho pra ajudar quem precisar. Link do tópico do naze: https://tibiaking.com/forums/topic/101086-otimizado-autoloot-in-sources-for-otx2-ou-menor/ caso alguém ja utilize esse autoloot e esteja com o problema de remover items do autoloot só ir em player.cpp e trocar isso: void Player::removeAutoLoot(uint16_t id) { if(checkAutoLoot(id)) { return; } AutoLoot.remove(id); } por isso: void Player::removeAutoLoot(uint16_t id) { if(!checkAutoLoot(id)) return; for(std::list<uint16_t>::iterator it = AutoLoot.begin(); it != AutoLoot.end(); ++it) { if((*it) == id) { AutoLoot.erase(it); break; } } } todos os créditos ao Naze, não sei se alguém mais fez esse código, mas como peguei do link citado acima que é post dele deixo aqui o autoloot 100% funcional direto nas sources
-
AUMENTANDO TELA DE VISÃO OTCLIENTE EM REV3884 8.60 0.4
REV3884 TFS 0.4 OTC 0.6.6 OBSERVAÇÃO: Bom galera hoje vim trazer um código de expansão de visão para o otclient alterado por mim, usado somente para rev3884 e então se você usa um tfs 0.3.6 ou tfs 1.0 não vai dar certo, *Esse código foi alterado por mim para ser usado apenas para rev3884, procolo 8.60 e TFS 0.4 que no caso não achei e resolvi compartilhar essas alterações. 1° TUTORIAL 1 SOURCE DO SEU SERVIDOR. Abra Protocollgame.cpp 1 Ache ((x >= myPos.x - 8 + offsetz) && (x <= myPos.x + 9 + offsetz) && //(y >= myPos.y - 6 + offsetz) && (y <= myPos.y + 7 + offsetz)); ALTERE PARA: ((x >= myPos.x - Map::maxClientViewportX + offsetz) && (x <= myPos.x + (Map::maxClientViewportX+1) + offsetz) && (y >= myPos.y - Map::maxClientViewportY + offsetz) && (y <= myPos.y + (Map::maxClientViewportY+1) + offsetz)); 2 Ache: GetMapDescription(pos.x - 8, pos.y - 6, pos.z, 18, 14, msg); ALTERE PARA: GetMapDescription(pos.x - Map::maxClientViewportX, pos.y - Map::maxClientViewportY, pos.z, (Map::maxClientViewportX+1)*2, (Map::maxClientViewportY+1)*2, msg); 3 Ache: if(oldPos.y > newPos.y) // north, for old x { msg->put<char>(0x65); GetMapDescription(oldPos.x - 8, newPos.y - 6, newPos.z, 18, 1, msg); } else if(oldPos.y < newPos.y) // south, for old x { msg->put<char>(0x67); GetMapDescription(oldPos.x - 8, newPos.y + 7, newPos.z, 18, 1, msg); } if(oldPos.x < newPos.x) // east, [with new y] { msg->put<char>(0x66); GetMapDescription(newPos.x + 9, newPos.y - 6, newPos.z, 1, 14, msg); } else if(oldPos.x > newPos.x) // west, [with new y] { msg->put<char>(0x68); GetMapDescription(newPos.x - 8, newPos.y - 6, newPos.z, 1, 14, msg); } ALTERE PARA: if (oldPos.y > newPos.y) { // north, for old x msg->put<char>(0x65); GetMapDescription(oldPos.x - Map::maxClientViewportX, newPos.y - Map::maxClientViewportY, newPos.z, (Map::maxClientViewportX+1)*2, 1, msg); } else if (oldPos.y < newPos.y) { // south, for old x msg->put<char>(0x67); GetMapDescription(oldPos.x - Map::maxClientViewportX, newPos.y + (Map::maxClientViewportY+1), newPos.z, (Map::maxClientViewportX+1)*2, 1, msg); } if (oldPos.x < newPos.x) { // east, [with new y] msg->put<char>(0x66); GetMapDescription(newPos.x + (Map::maxClientViewportX+1), newPos.y - Map::maxClientViewportY, newPos.z, 1, (Map::maxClientViewportY+1)*2, msg); } else if (oldPos.x > newPos.x) { // west, [with new y] msg->put<char>(0x68); GetMapDescription(newPos.x - Map::maxClientViewportX, newPos.y - Map::maxClientViewportY, newPos.z, 1, (Map::maxClientViewportY+1)*2, msg); } 4 ache GetFloorDescription(msg, oldPos.x - 8, oldPos.y - 6, 5, 18, 14, 3, skip); //(floor 7 and 6 already set) GetFloorDescription(msg, oldPos.x - 8, oldPos.y - 6, 4, 18, 14, 4, skip); GetFloorDescription(msg, oldPos.x - 8, oldPos.y - 6, 3, 18, 14, 5, skip); GetFloorDescription(msg, oldPos.x - 8, oldPos.y - 6, 2, 18, 14, 6, skip); GetFloorDescription(msg, oldPos.x - 8, oldPos.y - 6, 1, 18, 14, 7, skip); GetFloorDescription(msg, oldPos.x - 8, oldPos.y - 6, 0, 18, 14, 8, skip); Altere para GetFloorDescription(msg, oldPos.x - Map::maxClientViewportX, oldPos.y - Map::maxClientViewportY, 5, (Map::maxClientViewportX+1)*2, (Map::maxClientViewportY+1)*2, 3, skip); //(floor 7 and 6 already set) GetFloorDescription(msg, oldPos.x - Map::maxClientViewportX, oldPos.y - Map::maxClientViewportY, 4, (Map::maxClientViewportX+1)*2, (Map::maxClientViewportY+1)*2, 4, skip); GetFloorDescription(msg, oldPos.x - Map::maxClientViewportX, oldPos.y - Map::maxClientViewportY, 3, (Map::maxClientViewportX+1)*2, (Map::maxClientViewportY+1)*2, 5, skip); GetFloorDescription(msg, oldPos.x - Map::maxClientViewportX, oldPos.y - Map::maxClientViewportY, 2, (Map::maxClientViewportX+1)*2, (Map::maxClientViewportY+1)*2, 6, skip); GetFloorDescription(msg, oldPos.x - Map::maxClientViewportX, oldPos.y - Map::maxClientViewportY, 1, (Map::maxClientViewportX+1)*2, (Map::maxClientViewportY+1)*2, 7, skip); GetFloorDescription(msg, oldPos.x - Map::maxClientViewportX, oldPos.y - Map::maxClientViewportY, 0, (Map::maxClientViewportX+1)*2, (Map::maxClientViewportY+1)*2, 8, skip); 5 ache GetFloorDescription(msg, oldPos.x - 8, oldPos.y - 6, oldPos.z - 3, 18, 14, 3, skip); ALTERE PARA GetFloorDescription(msg, oldPos.x - Map::maxClientViewportX, oldPos.y - Map::maxClientViewportY, oldPos.z - 3, (Map::maxClientViewportX+1)*2, (Map::maxClientViewportY+1)*2, 3, skip); 6 ACHE GetMapDescription(oldPos.x - 8, oldPos.y + 1 - 6, newPos.z, 1, 14, msg); ALTERE PARA GetMapDescription(oldPos.x - Map::maxClientViewportX, oldPos.y - (Map::maxClientViewportY-1), newPos.z, 1, (Map::maxClientViewportY+1)*2, msg); 7 ACHE GetMapDescription(oldPos.x - 8, oldPos.y - 6, newPos.z, 18, 1, msg); ALTERE PARA GetMapDescription(oldPos.x - Map::maxClientViewportX, oldPos.y - Map::maxClientViewportY, newPos.z, (Map::maxClientViewportX+1)*2, 1, msg); 8 Ache GetFloorDescription(msg, oldPos.x - 8, oldPos.y - 6, newPos.z, 18, 14, -1, skip); GetFloorDescription(msg, oldPos.x - 8, oldPos.y - 6, newPos.z + 1, 18, 14, -2, skip); GetFloorDescription(msg, oldPos.x - 8, oldPos.y - 6, newPos.z + 2, 18, 14, -3, skip); Altere para GetFloorDescription(msg, oldPos.x - Map::maxClientViewportX, oldPos.y - Map::maxClientViewportY, newPos.z, (Map::maxClientViewportX+1)*2, (Map::maxClientViewportY+1)*2, -1, skip); GetFloorDescription(msg, oldPos.x - Map::maxClientViewportX, oldPos.y - Map::maxClientViewportY, newPos.z + 1, (Map::maxClientViewportX+1)*2, (Map::maxClientViewportY+1)*2, -2, skip); GetFloorDescription(msg, oldPos.x - Map::maxClientViewportX, oldPos.y - Map::maxClientViewportY, newPos.z + 2, (Map::maxClientViewportX+1)*2, (Map::maxClientViewportY+1)*2, -3, skip); 9 ache GetFloorDescription(msg, oldPos.x - 8, oldPos.y - 6, newPos.z + 2, 18, 14, -3, skip); Altere para GetFloorDescription(msg, oldPos.x - Map::maxClientViewportX, oldPos.y - Map::maxClientViewportY, newPos.z + 2, (Map::maxClientViewportX+1)*2, (Map::maxClientViewportY+1)*2, -3, skip); 10 ache GetMapDescription(oldPos.x + 9, oldPos.y - 1 - 6, newPos.z, 1, 14, msg); Altere para GetMapDescription(oldPos.x + Map::maxClientViewportX+1, oldPos.y - (Map::maxClientViewportY+1), newPos.z, 1, ((Map::maxClientViewportY+1)*2), msg); AGORA EM MAP.H ache static const int32_t maxViewportX = 11; //min value: maxClientViewportX + 1 static const int32_t maxViewportY = 11; //min value: maxClientViewportY + 1 static const int32_t maxClientViewportX = 8; static const int32_t maxClientViewportY = 6; altere para: static const int32_t maxViewportX = 18; //min value: maxClientViewportX + 1 static const int32_t maxViewportY = 18; //min value: maxClientViewportY + 1 static const int32_t maxClientViewportX = 14; static const int32_t maxClientViewportY = 12; Pronto agora é só compilar a source do seu servidor 8.60 rev3884 PARTE 2 OTCLIENT 0.6.6 abra o map.cpp da source do otclient ache void Map::resetAwareRange() { AwareRange range; range.left = 8; range.top = 6; range.bottom = 7; range.right = 9; setAwareRange(range); } ALTERE PARA void Map::resetAwareRange() { AwareRange range; range.left = 14; //Change this to = maxClientViewportX range.top = 12; //Change this to = maxClientViewportY range.bottom = range.top+1; range.right = range.left+1; setAwareRange(range); } COMPILE A SOURCE DO OTCLIENT E PRONTO!!! ESPERO TER SANADO VÁRIAS DÚVIDAS DE AUMENTAR TELA DO OTCLIENT EM REV3884. CRÉDITOS: Pra mim pela alterações funcionais. e pelo Flatlander por disponibilizar para tfs 0.3.6 e 1.0 pois esse está alterado pra tfs 0.4
-
[TFS 1x] IncreaseMagicPercent
Para quem deseja utilizar o 'IncreaseMagicPercent' no arquivo items.xml, que aumenta o dano mágico em porcentagem No arquivo game.cpp depois. Player* attackerPlayer; if (attacker) { attackerPlayer = attacker->getPlayer(); } else { attackerPlayer = nullptr; } Player* targetPlayer = target->getPlayer(); if (attackerPlayer && targetPlayer && attackerPlayer->getSkull() == SKULL_BLACK && attackerPlayer->getSkullClient(targetPlayer) == SKULL_NONE) { return false; } damage.primary.value = std::abs(damage.primary.value); damage.secondary.value = std::abs(damage.secondary.value); int32_t healthChange = damage.primary.value + damage.secondary.value; if (healthChange == 0) { return true; } adicionar // Inc Magic by lursky auto originList = { ORIGIN_RANGED, ORIGIN_MELEE, ORIGIN_CONDITION }; auto it = std::find(originList.begin(), originList.end(), damage.origin); if (attackerPlayer && it == originList.end()) { int32_t magicPercentBonus = 0; for (int32_t slot = CONST_SLOT_FIRST; slot <= CONST_SLOT_LAST; ++slot) { Item* item = attackerPlayer->inventory[slot]; if (item) { const ItemType& iiType = Item::items[item->getID()]; const int32_t& slotPosition = item->getSlotPosition(); if (iiType.increaseMagicPercent && (iiType.slotPosition & slotPosition)) { magicPercentBonus += iiType.increaseMagicPercent; } } } if (magicPercentBonus > 0) { damage.primary.value += damage.primary.value * (magicPercentBonus / 100.0f); } } No arquivo item.cpp post edit: Deve ter colocado duas vezes, ok? É só procurar no item.cpp essa linha e adicionar. Repita a busca pela mesma linha e adicione para exibir 'inc magic'. Pronto depois. if (it.abilities) { for (uint8_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 << it.abilities->skills[i] << std::noshowpos; } adicionar. if (it.increaseMagicPercent) { if (begin) { begin = false; s << " ("; } else { s << ", "; } s << "Inc.Magic " << std::showpos << it.increaseMagicPercent << '%' << std::noshowpos; } No arquivo items.cpp depois. {"worth", ITEM_PARSE_WORTH}, adicionar: { "increasemagicpercent", ITEM_PARSE_INCREASEMAGICPERCENT }, Novamente, no arquivo items.cpp: depois. case ITEM_PARSE_SUPPRESSCURSE: { if (valueAttribute.as_bool()) { abilities.conditionSuppressions |= CONDITION_CURSED; } break; } adicionar. case ITEM_PARSE_INCREASEMAGICPERCENT: { it.increaseMagicPercent = pugi::cast<int32_t>(valueAttribute.value()); break; } No arquivo items.h depois. uint64_t worth = 0; adicionar. int32_t increaseMagicPercent = 0; Novamente, no arquivo items.h: after. ITEM_PARSE_WORTH, adicionar: ITEM_PARSE_INCREASEMAGICPERCENT, FIM É só recompilar e testar Como funciona usar esses atributos? Veja aqui um exemplo item id="xxx" name="teste robe"> <attribute key="weight" value="7100"/> <attribute key="armor" value="18"/> <attribute key="slotType" value="body"/> <attribute key="increaseMagicPercent" value="50"/> </item> Obs: Esses adições na source foram feitas por Mateus Roberto, e Lurskcy fez uma correção de erro que estava causando um .crash no arquivo games.cpp. Está funcionando bem. Espero que gostem
-
Auto Loot Sytem for TFS 1.3 + revscripts
Depois de milhões de anos sem programar porcaria nenhuma... Eu desenvolvi este sistema como um método de estudo. (C++) Este sistema foi inspirado no Auto Loot System por @psychonaut. (OTland) Criei o mesmo na versão mais recente do tfs. Auto Loot System for TFS 1.3 Como funciona? Simples, quando você mata um monstro e abre o corpo (você precisa clicar no corpo), os itens vão para o seu personagem. Instalando em actions.cpp, encontre: if (corpseOwner != 0 && !player->canOpenCorpse(corpseOwner)) { return RETURNVALUE_YOUARENOTTHEOWNER; } e mude isso para: if (corpseOwner != 0 && !player->canOpenCorpse(corpseOwner)) { return RETURNVALUE_YOUARENOTTHEOWNER; } else { if (player->canOpenCorpse(corpseOwner) && player->autoLootList.size() != 0) { if (player->getCapacity() > 100 * 100) { //Minimum of Capacity for autoloot works. (100 CAP) for (Item* item : container->getItemList()) { if (player->getItemFromAutoLoot(item->getID())) { std::ostringstream msgAutoLoot; msgAutoLoot << "You looted a " << item->getItemCount() << "x " << item->getName() << "."; g_game.internalMoveItem(container, player, CONST_SLOT_WHEREEVER, item, item->getItemCount(), nullptr); player->sendTextMessage(MESSAGE_INFO_DESCR, msgAutoLoot.str()); } } } else { player->sendTextMessage(MESSAGE_INFO_DESCR, "Sorry, you don't have enough capacity to use auto loot, so it has been disabled. (100+ capacity is required)"); } } } em player.h, abaixo de: std::unordered_set<uint32_t> VIPList; adicione isso: std::set<uint32_t> autoLootList; ainda em player.h encontre: bool hasLearnedInstantSpell(const std::string& spellName) const; adicione em baixo: void addItemToAutoLoot(uint16_t itemId); void removeItemFromAutoLoot(uint16_t itemId); bool getItemFromAutoLoot(uint16_t itemId); em player.cpp no final do arquivo, adicione: void Player::addItemToAutoLoot(uint16_t itemId) { autoLootList.insert(itemId); } void Player::removeItemFromAutoLoot(uint16_t itemId) { autoLootList.erase(itemId); } bool Player::getItemFromAutoLoot(const uint16_t itemId) { return autoLootList.find(itemId) != autoLootList.end(); } em luascript.cpp encontre: registerMethod("Player", "getFightMode", LuaScriptInterface::luaPlayerGetFightMode); e adicione em baixo: registerMethod("Player", "addItemToAutoLoot", LuaScriptInterface::luaPlayerAddItemToAutoLoot); registerMethod("Player", "removeItemFromAutoLoot", LuaScriptInterface::luaPlayerRemoveItemFromAutoLoot); registerMethod("Player", "getItemFromAutoLoot", LuaScriptInterface::luaPlayerGetItemFromAutoLoot); registerMethod("Player", "getAutoLootList", LuaScriptInterface::luaPlayerGetAutoLootList); ainda em luascript.cpp encontre essa função: int LuaScriptInterface::luaPlayerGetFightMode(lua_State* L) { // player:getFightMode() Player* player = getUserdata<Player>(L, 1); if (player) { lua_pushnumber(L, player->fightMode); } else { lua_pushnil(L); } return 1; } abaixo dela, adicione: int LuaScriptInterface::luaPlayerAddItemToAutoLoot(lua_State* L) { // player:addItemToAutoLoot(itemId) Player* player = getUserdata<Player>(L, 1); if (!player) { lua_pushnil(L); return 1; } uint16_t itemId; if (isNumber(L, 2)) { itemId = getNumber<uint16_t>(L, 2); } else { itemId = Item::items.getItemIdByName(getString(L, 2)); if (itemId == 0) { lua_pushnil(L); return 1; } } player->addItemToAutoLoot(itemId); pushBoolean(L, true); return 1; } int LuaScriptInterface::luaPlayerRemoveItemFromAutoLoot(lua_State* L) { // player:removeItemFromAutoLoot(itemId) Player* player = getUserdata<Player>(L, 1); if (!player) { lua_pushnil(L); return 1; } uint16_t itemId; if (isNumber(L, 2)) { itemId = getNumber<uint16_t>(L, 2); } else { itemId = Item::items.getItemIdByName(getString(L, 2)); if (itemId == 0) { lua_pushnil(L); return 1; } } player->removeItemFromAutoLoot(itemId); pushBoolean(L, true); return 1; } int LuaScriptInterface::luaPlayerGetItemFromAutoLoot(lua_State* L) { // player:getItemFromAutoLoot(itemId) Player* player = getUserdata<Player>(L, 1); if (!player) { lua_pushnil(L); return 1; } uint16_t itemId; if (isNumber(L, 2)) { itemId = getNumber<uint16_t>(L, 2); } else { itemId = Item::items.getItemIdByName(getString(L, 2)); if (itemId == 0) { lua_pushnil(L); return 1; } } if (player->getItemFromAutoLoot(itemId)) { pushBoolean(L, true); } else { pushBoolean(L, false); } return 1; } int LuaScriptInterface::luaPlayerGetAutoLootList(lua_State* L) { // player:getAutoLootList() Player* player = getUserdata<Player>(L, 1); if (player) { std::set<uint32_t> value = player->autoLootList; if (value.size() == 0) { lua_pushnil(L); return 1; } int index = 0; lua_createtable(L, value.size(), 0); for(auto i : value) { lua_pushnumber(L, i); lua_rawseti(L, -2, ++index); } } else { lua_pushnil(L); } return 1; } em luascript.h encontre: static int luaPlayerGetFightMode(lua_State* L); adicione a baixo: static int luaPlayerAddItemToAutoLoot(lua_State* L); static int luaPlayerRemoveItemFromAutoLoot(lua_State* L); static int luaPlayerGetItemFromAutoLoot(lua_State* L); static int luaPlayerGetAutoLootList(lua_State* L); em iologindata.cpp encontre: //load storage map query.str(std::string()); query << "SELECT `key`, `value` FROM `player_storage` WHERE `player_id` = " << player->getGUID(); if ((result = db.storeQuery(query.str()))) { do { player->addStorageValue(result->getNumber<uint32_t>("key"), result->getNumber<int32_t>("value"), true); } while (result->next()); } e adicione em baixo: query.str(std::string()); query << "SELECT `autoloot_list` FROM `player_autoloot` WHERE `player_id` = " << player->getGUID(); if ((result = db.storeQuery(query.str()))) { unsigned long lootlistSize; const char* autolootlist = result->getStream("autoloot_list", lootlistSize); PropStream propStreamList; propStreamList.init(autolootlist, lootlistSize); int16_t value; int16_t item = propStreamList.read<int16_t>(value); while (item) { player->addItemToAutoLoot(value); item = propStreamList.read<int16_t>(value); } } acima de: //save inbox items adicione: query.str(std::string()); query << "DELETE FROM `player_autoloot` WHERE `player_id` = " << player->getGUID(); if (!db.executeQuery(query.str())) { return false; } PropWriteStream propWriteStreamAutoLoot; for (auto i : player->autoLootList) { propWriteStreamAutoLoot.write<uint16_t>(i); } size_t lootlistSize; const char* autolootlist = propWriteStreamAutoLoot.getStream(lootlistSize); query.str(std::string()); DBInsert autolootQuery("INSERT INTO `player_autoloot` (`player_id`, `autoloot_list`) VALUES "); query << player->getGUID() << ',' << db.escapeBlob(autolootlist, lootlistSize); if (!autolootQuery.addRow(query)) { return false; } if (!autolootQuery.execute()) { return false; } em sua database, execute esta query CREATE TABLE player_autoloot ( id int NOT NULL AUTO_INCREMENT, player_id int NOT NULL, autoloot_list blob, PRIMARY KEY (id) ); agora vá em data/scripts/talkactions e crie esse arquivo LUA autoloot.lua local talk = TalkAction("!autoloot") function talk.onSay(player, words, param) local i = player:getAutoLootList() local cache = "Check your loot list: " local split = param:split(",") local action = split[1] if param == "list" then if i then for _, item in ipairs(i) do cache = cache .. (ItemType(item)):getName() .. ", " end else player:sendTextMessage(MESSAGE_INFO_DESCR, "Your list is empty! Add some item and try again.") return false end player:sendTextMessage(MESSAGE_INFO_DESCR, cache:sub(1, -3)) return false end if action == "add" then local item = split[2]:gsub("%s+", "", 1) local itemType = ItemType(item) if itemType:getId() == 0 then itemType = ItemType(tonumber(item)) if itemType:getName() == '' then player:sendCancelMessage("There is no item with that id or name.") return false end end if player:getItemFromAutoLoot(itemType:getId()) then player:sendCancelMessage("You're already autolooting this item.") return false end player:addItemToAutoLoot(itemType:getId()) player:sendTextMessage(MESSAGE_INFO_DESCR, "You're now auto looting " .. itemType:getName()) return false elseif action == "remove" then local item = split[2]:gsub("%s+", "", 1) local itemType = ItemType(item) if itemType:getId() == 0 then itemType = ItemType(tonumber(item)) if itemType:getName() == '' then player:sendCancelMessage("There is no item with that id or name.") return false end end if player:getItemFromAutoLoot(itemType:getId()) then player:removeItemFromAutoLoot(itemType:getId()) player:sendTextMessage(MESSAGE_INFO_DESCR, "You removed the " .. itemType:getName() .. " from your loot list.") else player:sendCancelMessage("This item does not exist in your loot list.") end return false end player:sendTextMessage(MESSAGE_EVENT_ORANGE, "Auto Loot commands (items are automatically moved to your bp if you open monster corpse):"..'\n'.."!addloot add, nameItem - add item to auto loot by name"..'\n'.."!autoloot remove, itemName - remove item from auto loot by name"..'\n'.."!autoloot list - list your current auto loot items") return false end talk:separator(" ") talk:register() É isso, espero que gostem do sisteminha Se você encontrar algum bug, deixe-me saber. Falta fazer: Optimizar a mensagem de loot. Adicionar ModalWindow. Cheers~
-
[C++] Attack Speed
Salve Galera, olhando em minhas libs achei um script muito bom !! Em alguns servidores derivados o Skill Fist Fighting influencia no Attack Speed do Player, Então .... Segue a Formula na source que faz essa magica : Em Player.cpp Procure : uint32_t Player::getAttackSpeed() const Mude Atualize para esse função : uint32_t Player::getAttackSpeed() const { int32_t SpeedAttack; SpeedAttack = vocation->getAttackSpeed() - (getSkill(SKILL_FIST, SKILL_LEVEL) * 10); if (SpeedAttack < 500) { return 500; } else { return (uint32_t) SpeedAttack; } } Explicando : Ele irá verifica o Attack Speed padrão no Vocations.xml e irá verifica o Skill Fist do Player e retornará a velocidade, caso a velocidade retorne menor que 500 ele sempre limitará a 500ms para que não haja erros. Simples e Facil ! Creditos ? Emerson Henrique
-
DISCORD RPC - Para Tibia [New Update]
Bem eu não sei nem por onde começar já que. Acredito eu que faz uns 2 anos mais ou menos que não me envolvo com Tíbia então hoje vim trazer algo novo para todos nós. Bem o que è Discord RPC? Nada mais nada menos que, aquela janelinha que mostra qual jogo você está jogando no Discord. E ao passar o mouse em cima do úsuario você pode checar os detalhes. Bem o Tíbia em sí já tem o Discord RPC por padrão mas o que eu trouxe a vocês o Discord-RPC que você pode editar como você quiser. Por exemplo. Talvez seja uma contribuição bem clichê. Mas para aqueles que deseja que os players mostre para os outros que está jogando seu otserv é uma boa, que possa ajudar a divulgar também. Acredito eu. Enfim vamos lá. Bem primeiro de tudo você precisa criar sua aplicação no Discord. Vá até o site de Desenvolvimentos de Aplicações do Discord e crie o seu canal clicando em New Aplication. Após Criar seu canal com o nome do seu Otserv, Descrição e ter colocado sua logo ou sei lá. Seu Canal Está quase completo. (Lembrando que as imagens precisam ser ou 512x512 ou 1024x1024) Vá até Rich Presence: Escolha sua logo e também clique em Add Image(s) e adicione uma imagem para sua Logo do Discord-RPC. Para quando o cara passar o mouse por cima do úsuario enxergar ela. Ok até aqui tudo OK. Só precisa aguardar o Discord carregar sua logo e enquanto isso vamos fazer o restante. Baixe a source do Discord-RPC. Tibia-RPC.rar https://drive.google.com/file/d/1N9oOlxEdMmPZbwPNFRNMXI4z86_v_AbL/view?usp=share_link Após baixar e abrir. Vá até o Discord.Cpp e vai aparecer assim pra você. Primeiro precisamos definir o ID do nosso Canal que criamos no Discord. Quando criamos o canal ele já nos da o ID do nosso canal. Você cópia o ID e cola em cima do outro que está no Discord.CPP. Após isso vamos ver os detalhes. Em Rich Presence conseguimos visualizar a imagem que adicionamos Ok. Abaixo de Rich Presence tem o Art Assets que é onde adicionamos as imagens de logo e tal. Vamos em Visualizer Clicamos nele e então vamos editar nosso Dircord RPC da forma que desejarmos. Após Editar a direita veremos como ele vai ficar quando alguém passar o mouse em cima do úsuario. Vamos em Show Code e vemos como ficou o código nele. certo! bem de alguns não necessitamos necessáriamente a não ser que você queira adicionar mais alguma coisa. recomendo não mexer nessas linhas. DiscordRichPresence discordPresence; memset(&discordPresence, 0, sizeof(discordPresence)); discordPresence.startTimestamp = starttime; Discord_UpdatePresence(&discordPresence); Tudo ok. Então vá até o Discord.cpp e edite-as como quiser. E vemos tudo lindo assim: Após isso só compilar e importar a dll no seu cliente tibia. Se você não tem nenhum importador de .dll eu vou disponibilizar pra você o link do Stud_PE Com ele você pode importa sua dll no seu cliente e ser feliz. Para importar a dll ao seu cliente, instale o Stud_PE no seu computador, após instalar. Abra. Importe o Tibia ou arraste o Tibia pra dentro do Stud_PE Vá em Functions > Clique com o botão direito dentro da janela de Fuctions. Cliqu em Add New Import > Dll Select > Selecione a dll do Discord-RPC > Após isso Clique em > Select func. > Selecione a função > Após Clique em > Add to List e clique em ADD. Então só da Ok e abrir o seu Cliente. Bem eu vou mandar a versão que mostra o Nome e Level pra vocês assim: Essa versão mostra o Nome do personagem quando loga e o level. OBS: a dll faz um update de checagem a cada 5 segundos então precisa aguardar até seu nome aparecer. Em Discord.h você pode determinar o level máximo do seu servidor. fazendo cada linha por level. Você precisa procurar os endereços com o Cheat Engine. Se caso for procurar pelo Check Login pesquisa por 1 se o char estiver logado. E 0 quando estiver deslogado, vai procurando até encontar qual endereço muda quando logar e deslogar. Em questão do nome procura pro string e o nome do seu personagem. Na maioria das vezes é quase sempre o primeiro mas você pode mudar o nome no Cheat pra ter certeza. Em questão do Level Procura pelo valor do level por exemplo: 211, da um addskill pra level o level 212 e da um Next Scan na pesquisa com o valor novo pra ter certeza. e vai adicionando mais um pouco até ter a certeza. Supostamente eu encontrei na primeira pesquisa e só precisei upar 1 level mas vocês podem demorar um pouco. único coisa que não consegui identificar foi a vocação do personagem que eu queria muito mas com isso acho que já é o suficiente. Tibia-RPC N.L.rar✔️ https://drive.google.com/file/d/1N9oOlxEdMmPZbwPNFRNMXI4z86_v_AbL/view?usp=share_link perdõe se eu não fui autoexplicativo e se postei na sessão errada. Corrijam-me se eu falei algo errado ou fiz algo de errado. Essa Source não fui eu quem criei. Eu usei o VS 2019. Qualquer dúvida por favor sinta-se a vontade. Meu obtjetivo é trazer algo novo pra comunidade e que possa ajudar outras pessoas. Esse lançamento pode ser inútil pra alguns mas para outros pode servidor de grande favor. Para aqueles que jogam usando Discord e tal.
-
[Dll Injection] Old Client Auto Updater
Bom galera, estou voltando a trabalhar com tibia após alguns anos apenas nas Sombras =P Estou com um projeto de reviver o Pokémon SvkE, exatamente como ele era nos primordios de 2010, e para isso estou utilizando o old client, mas como vocês sabem para customizar o old dá um trabalho bem pesado, já faz algumas semanas que estou estudando injeção de dependencia no old e venho aqui mostrar o meu trabalho com o moduo de Auto Updater, segue abaixo o gif: Bom como eu trabalho o dia todo e ainda estou pegado no svke nos tempos livres, acaba que fico com pouco tempo de tentar postar algum tutorial do assunto, mas para quem tiver interesse nessa area de dll injecion, criação de janelas customizadas e etc.. Recomendo dar uma olhada no projeto do tibianic-dll vocês encontram fácil no github, ele é para 772 mas já é uma base para vocês verem como que funcionam as coisas em uma dll. Sobre o updater em sí o que eu fiz foram os seguintes passos: No cliente eu tenho um arquivo chamado version, nele vai ficar salvo a versão do cliente do jogador. Quando eu abro o cliente pego a versão dele e envio para uma API rest. Na API rest eu checo se a versão que foi informada pelo cliente está na mais atual ou não, se não estiver eu envio de volta pra ele a lista de arquivos que precisam ser baixados e a nova versão. No cliente eu recebo essas informações da API e começo o processo de download dos arquivos e só sucessos. OBS1: A API rest eu fiz em .net core mas pode ser em qualquer tecnologia, php, lua, um simples xml em uma pasta do seu site, vocês que vão decidir, eu optei dessa forma pois na minha concepção é a forma ideal, eu pretendo criar um programinha desktop ou até mesmo no meu site, uma integração com a api, onde eu seleciono os arquivos coloco a versão e subo essas coisas para a api salvar no banco e por sua vez repassar as informações novas para os clientes S2. OBS2: Quem tiver ficado interessado no projeto do svke só entrar no discord para jogar ou acompanhar o desenvolvimento, atualmente estamos em beta: https://discord.gg/8XZc64yN9G
-
Adicionar "Summoned by..." na descrição do monstro ao dar Look
[TUTORIAL] Adicionar "Summoned by..." na descrição do monstro ao dar Look Como é uma alteração pequena, apesar de simples achei legal compartilhar. Ao dar look no summon de algum jogador você consegue identificar quem foi que o invocou. Acesse monster.h Altere o código abaixo: Para: Não tirei nenhum print até o momento, caso você realize a alterações, por favor compartilhe aqui o resultado! :)
-
Retirar o efeito de sangue nos monstros quando receberem ataque
[TUTORIAL] Retirar o efeito de sangue nos monstros quando receberem ataque Dependendo do aspecto que você deseja dar ao seu servidor, pode ser que seja necessário retirar alguns efeitos. Na imagem abaixo você pode observar um efeito de respingo de sangue que ocorre quando os monstros levam dano. Pode ser interessante retirar este efeito em determinadas propostas de servidores alternativos, com ar mais amigáveis. (Ex: Pokemon) (Imagem demonstrando o efeito de sangue nos monstros) Acesse creature.cpp E remova o seguinte código: Agora remova o seguinte código: Agora acesse o game.cpp Dentro de void Game::combatGetTypeInfo(CombatType_t combatType, Creature* target, TextColor_t& color, uint8_t& effect) Remova os seguintes códigos: Após estas remoções, quando os monstros levarem dano, não será mais aplicado o efeito de sangue.
-
Monster Level TFS e OTX
Testado em tfs 0.4 e otx 2! Em monsters.h procure por: Em monsters.cpp procure por: Em monster.h procure por: Em monster.cpp procure por: Em map.cpp procure por: Em configmanager.h busque por; Em configmanager.cpp busque por: Agora no config.lua adicione: monsterHasLevel = true Como está programado, a cada nível, monstros ganham 10% de HP, 1% de dano e 0.5% de defesa. Para configurar o level minimo e máximo do monstro basta adicionar no XML do monstro a tag: <level min="1" max="10"/>
-
Otimizado: AutoLoot in Sources for Otx(2 ou menor)
Uns dos problemas das maiorias dos otserv casual que estão online é o autoloot, alguns com erros e defeito na checagem dos containers e sempre consumindo muito por ser em lua fazendo checagens de tile, corpo, itens e etc. Por isso decidi da uma contribuição a essa comunidade que me evoluiu bastante um autoloot diretamente na source na função que insere os itens no corpo, otimizando em 100% na hr da entrega do item, e não tendo problemas ocasionais como corpos sobre corpos ou varias bags. Esse sistema já é bem comum quando comprado, mas gratuito acredito que ainda não tinha disponível, então fiz esse totalmente do 0 na source tfs 0.4 rev 3999 (do yan) e passei para Otx corrigindo alguns erros de compatibilidade. Atenção na instalação! Qualquer problema pra compilar que envolva as linhas presente no código verifique novamente se instalou corretamente antes de pedir ajuda para evitar flood. Qualquer bug, erro ou mal funcionamento encontrado pode postar que estarei corrigindo, mas não irei customizar a seu agrado, você consegue fazer isso sozinho com atenção e calma, e se precisa de ajuda é só posta as duvidas. Imagens do Sistema: Características: -> Otimizado executando apenas funções necessarias -> Comandos simples e praticos -> !autoloot on/off //Autoloot estara sempre ligado, e caso queria desliga use '!autoloot off' -> !autoloot bank/bag //Define para onde ira o dinheiro coletado, direto banco ou no inventario -> !autoloot list //Lista todos itens adicionados para coleta -> !autoloot clear/clean //Remove todos itens da lista -> !autoloot add, name item, name item, ... //Adiciona itens na lista, podendo adicionar quantos quiser de uma vez. Ele retorna um messagem dizendo os itens que foram adicionados e os que deram erro e não foram adicionados, talvez por escrito errado, ja existente na lista, na lista de bloquados, etc. -> !autoloot remove, item name, item name, ... //Remove itens da lista, com a mesma logica do add. -> Opções configuravel pelo Config.lua -> AutoLoot_MoneyIDs //IDs dos Itens Moedas do servidor como gold coins, platinium coins, crystal coins. -> AutoLoot_BlockIDs //IDs dos itens bloqueados para autoloot -> AutoLoot_MaxItem //Quantidade maxima de itens para adicionar a lista -> Sistema livres de bugs ou logs de erros. Instalação: Toda instalação será feita na source, exceto por 1 linha em talkactions.xml e 3 variáveis no config.lua Otx : Link para TFS -> https://tibiaking.com/forums/topic/101079-otimizado-autoloot-in-sources-for-tfs-036-tfs-04/ Caso ocorra algum erro na compilação so mandar que ajudo a resolver, testei apenas em uma otx que tenho então nao sei se pode sugir outros problemas. Nas versões atual da Otx para servidores 10+ não ira funcionar pois algumas funções são diferente e não irei corrigir agora, em breve passo o mesmo pra tfs 1.3 e otx3. Esse sistema foi totalmente feito por mim, mas dou créditos também ao @Mathias Kenfi por me ajudar muito na evolução em lua/c++ e a tabela sql é a mesma usada no quick autoloot 1.3 postado por Pedriinz
-
CLIENT OLD 8.6 para estendido e protegido cab
este é um tutorial de como usar um cliente com spr/dat estendido e protegido pelo CAB. fiz um video mostrando como usar um client com spr/dat estendido sem o error, basta seguir passo a passo o que eu fiz, em resumo voce precisara copiar seu dat estendido pra outro local para poder editar ele, salvando ele como não estendido e dps renomeando para poder usar junto com o original e compilando tudo com o programa cab... A DLL ajuda o cliente a ler as 2 dat (compilada pelo programa cab) , sem ela o cliente não abre, caso o teu cliente tenha o bug do ctrl+n ou alguns outros bugs que eu vi em alguns casos, a DLL tem a função de arrumar a maioria deles. LINK DOWNLOAD https://www.mediafire.com/file/kxqkywxeijk8hn2/programa_para_oldclient.rar/file ou creator cab https://drive.google.com/file/d/19b-EqpS-RmOQ_1o7ubb2a7j6RKcijjUa/view?usp=sharing scan https://www.virustotal.com/gui/file/4398e414d680ac26addbedc1f20bd59a82b546a1633abbb02b13d2f7150e950e/detection DLL https://drive.google.com/file/d/1T8XgwgHJAO9sgw5WR809GjMfej2NvP3n/view?usp=sharing SCAN https://www.virustotal.com/gui/file/539f0f7187c668457a14d87603e075ab5d1b6f6ac6a17aabf2b591fc3c177741/detection stup-pe hoohar o client https://drive.google.com/file/d/1cwR1wj3M8IsvxmIt5pQhZ4-DlFSMQVal/view?usp=sharing scan https://www.virustotal.com/gui/file/d06889d61474ee9480947956e52916fb997a3717e9f59d3ed830b5c780e81340/detection
-
UPDATE 3: Moveitem + Antipush + Anti-Crash Elf Bot - Bug Fixes - TFS 0.4 11/04/2024
TUTORIAL BY L3K0T PT~EN Olá pessoal, trago a vocês uma atualização que fiz no sistema, contendo 3 novas funcionalidades de movimentação de itens e uma proteção contra Elf Bot. Estas adições foram cuidadosamente implementadas para aperfeiçoar a experiência de jogo e manter a integridade do seu servidor. As novas funcionalidades têm a função vital de impedir que jogadores deixem itens indesejados em locais inapropriados, como na entrada de sua casa, em cima de seus depósitos ou em teleportes. Agora, apenas proprietários, subproprietários e convidados têm permissão para manipular itens nesses locais. Este pacote de atualização foi meticulosamente revisado para evitar abusos por parte de jogadores mal-intencionados e garantir um ambiente de jogo justo e equilibrado para todos os usuários. Iniciando o Tutorial 1Abra o arquivo "creatureevents.cpp" com o editor de sua preferência. Eu pessoalmente recomendo o Notepad++. Em creatureevents.cpp: return "onPrepareDeath"; Adicione abaixo: case CREATURE_EVENT_MOVEITEM: return "onMoveItem"; case CREATURE_EVENT_MOVEITEM2: return "onMoveItem2"; Em: return "cid, deathList"; Adicione abaixo: case CREATURE_EVENT_MOVEITEM: return "moveItem, frompos, topos, cid"; case CREATURE_EVENT_MOVEITEM2: return "cid, item, count, toContainer, fromContainer, fromPos, toPos"; Em: m_type = CREATURE_EVENT_PREPAREDEATH; Adicione abaixo: else if(tmpStr == "moveitem") m_type = CREATURE_EVENT_MOVEITEM; else if(tmpStr == "moveitem2") m_type = CREATURE_EVENT_MOVEITEM2; Procure por: bool CreatureEvents::playerLogout(Player* player, bool forceLogout) { //fire global event if is registered bool result = true; for(CreatureEventList::iterator it = m_creatureEvents.begin(); it != m_creatureEvents.end(); ++it) { if((*it)->getEventType() == CREATURE_EVENT_LOGOUT && (*it)->isLoaded() && !(*it)->executeLogout(player, forceLogout) && result) result = false; } return result; } Adicione abaixo: uint32_t CreatureEvents::executeMoveItems(Creature* actor, Item* item, const Position& frompos, const Position& pos) { // fire global event if is registered for(CreatureEventList::iterator it = m_creatureEvents.begin(); it != m_creatureEvents.end(); ++it) { if((*it)->getEventType() == CREATURE_EVENT_MOVEITEM) { if(!(*it)->executeMoveItem(actor, item, frompos, pos)) return 0; } } return 1; } Em: bool CreatureEvents::playerLogin(Player* player) { //fire global event if is registered bool result = true; for(CreatureEventList::iterator it = m_creatureEvents.begin(); it != m_creatureEvents.end(); ++it) { if((*it)->getEventType() == CREATURE_EVENT_LOGIN && (*it)->isLoaded() && !(*it)->executeLogin(player) && result) result = false; } if (result) { for(CreatureEventList::iterator it = m_creatureEvents.begin(); it != m_creatureEvents.end(); ++it) { CreatureEvent* event = *it; if(event->isLoaded() && ( event->getRegister() == "player" || event->getRegister() == "all") ) player->registerCreatureEvent(event->getName()); } } return result; } Adicione Abaixo: uint32_t CreatureEvent::executeMoveItem(Creature* actor, Item* item, const Position& frompos, const Position& pos) { //onMoveItem(moveItem, frompos, position, cid) if(m_interface->reserveEnv()) { ScriptEnviroment* env = m_interface->getEnv(); if(m_scripted == EVENT_SCRIPT_BUFFER) { env->setRealPos(pos); std::stringstream scriptstream; env->streamThing(scriptstream, "moveItem", item, env->addThing(item)); env->streamPosition(scriptstream, "position", frompos, 0); env->streamPosition(scriptstream, "position", pos, 0); scriptstream << "local cid = " << env->addThing(actor) << std::endl; scriptstream << m_scriptData; bool result = true; if(m_interface->loadBuffer(scriptstream.str())) { lua_State* L = m_interface->getState(); result = m_interface->getGlobalBool(L, "_result", true); } m_interface->releaseEnv(); return result; } else { #ifdef __DEBUG_LUASCRIPTS__ char desc[35]; sprintf(desc, "%s", player->getName().c_str()); env->setEventDesc(desc); #endif env->setScriptId(m_scriptId, m_interface); env->setRealPos(pos); lua_State* L = m_interface->getState(); m_interface->pushFunction(m_scriptId); LuaInterface::pushThing(L, item, env->addThing(item)); LuaInterface::pushPosition(L, frompos, 0); LuaInterface::pushPosition(L, pos, 0); lua_pushnumber(L, env->addThing(actor)); bool result = m_interface->callFunction(4); m_interface->releaseEnv(); return result; } } else { std::clog << "[Error - CreatureEvent::executeMoveItem] Call stack overflow." << std::endl; return 0; } } uint32_t CreatureEvent::executeMoveItem2(Player* player, Item* item, uint8_t count, const Position& fromPos, const Position& toPos, Item* toContainer, Item* fromContainer, int16_t fstack) { //onMoveItem2(cid, item, count, toContainer, fromContainer, fromPos, toPos) if(m_interface->reserveEnv()) { ScriptEnviroment* env = m_interface->getEnv(); if(m_scripted == EVENT_SCRIPT_BUFFER) { env->setRealPos(player->getPosition()); std::stringstream scriptstream; scriptstream << "local cid = " << env->addThing(player) << std::endl; env->streamThing(scriptstream, "item", item, env->addThing(item)); scriptstream << "local count = " << count << std::endl; env->streamThing(scriptstream, "toContainer", toContainer, env->addThing(toContainer)); env->streamThing(scriptstream, "fromContainer", fromContainer, env->addThing(fromContainer)); env->streamPosition(scriptstream, "fromPos", fromPos, fstack); env->streamPosition(scriptstream, "toPos", toPos, 0); scriptstream << m_scriptData; bool result = true; if(m_interface->loadBuffer(scriptstream.str())) { lua_State* L = m_interface->getState(); result = m_interface->getGlobalBool(L, "_result", true); } m_interface->releaseEnv(); return result; } else { #ifdef __DEBUG_LUASCRIPTS__ char desc[30]; sprintf(desc, "%s", player->getName().c_str()); env->setEvent(desc); #endif env->setScriptId(m_scriptId, m_interface); env->setRealPos(player->getPosition()); lua_State* L = m_interface->getState(); m_interface->pushFunction(m_scriptId); lua_pushnumber(L, env->addThing(player)); LuaInterface::pushThing(L, item, env->addThing(item)); lua_pushnumber(L, count); LuaInterface::pushThing(L, toContainer, env->addThing(toContainer)); LuaInterface::pushThing(L, fromContainer, env->addThing(fromContainer)); LuaInterface::pushPosition(L, fromPos, fstack); LuaInterface::pushPosition(L, toPos, 0); //lua_pushnumber(L, env->addThing(actor)); bool result = m_interface->callFunction(7); m_interface->releaseEnv(); return result; } } else { std::clog << "[Error - CreatureEvent::executeMoveItem] Call stack overflow." << std::endl; return 0; } } Agora em em creatureevents.h: CREATURE_EVENT_PREPAREDEATH, Adicione abaixo: CREATURE_EVENT_MOVEITEM, CREATURE_EVENT_MOVEITEM2 Em: uint32_t executePrepareDeath(Creature* creature, DeathList deathList); Adicione abaixo: uint32_t executeMoveItem(Creature* actor, Item* item, const Position& frompos, const Position& pos); uint32_t executeMoveItem2(Player* player, Item* item, uint8_t count, const Position& fromPos, const Position& toPos, Item* toContainer, Item* fromContainer, int16_t fstack); Em: bool playerLogout(Player* player, bool forceLogout); Abaixo adicone também uint32_t executeMoveItems(Creature* actor, Item* item, const Position& frompos, const Position& pos); uint32_t executeMoveItem2(Player* player, Item* item, uint8_t count, const Position& fromPos, const Position& toPos, Item* toContainer, Item* fromContainer, int16_t fstack); Agora em em game.cpp: if(!canThrowObjectTo(mapFromPos, mapToPos) && !player->hasCustomFlag(PlayerCustomFlag_CanThrowAnywhere)) { player->sendCancelMessage(RET_CANNOTTHROW); return false; } ReturnValue ret = internalMoveItem(player, fromCylinder, toCylinder, toIndex, item, count, NULL); if(ret == RET_NOERROR) return true; player->sendCancelMessage(ret); return false; } Altere para: if (!canThrowObjectTo(mapFromPos, mapToPos) && !player->hasCustomFlag(PlayerCustomFlag_CanThrowAnywhere)) { player->sendCancelMessage(RET_CANNOTTHROW); return false; } bool success = true; CreatureEventList moveitemEvents = player->getCreatureEvents(CREATURE_EVENT_MOVEITEM2); for (CreatureEventList::iterator it = moveitemEvents.begin(); it != moveitemEvents.end(); ++it) { Item* toContainer = toCylinder->getItem(); Item* fromContainer = fromCylinder->getItem(); if (!(*it)->executeMoveItem2(player, item, count, fromPos, toPos, (toContainer ? toContainer : 0), (fromContainer ? fromContainer : 0), fromStackpos) && success) success = false; } if (!success) return false; if (g_config.getBool(ConfigManager::ANTI_PUSH)) { std::string antiPushItems = g_config.getString(ConfigManager::ANTI_PUSH_ITEMS); IntegerVec tmpVec = vectorAtoi(explodeString(antiPushItems, ",")); if (tmpVec[0] != 0) { for (IntegerVec::iterator it = tmpVec.begin(); it != tmpVec.end(); ++it) { if (item->getID() == uint32_t(*it) && player->hasCondition(CONDITION_EXHAUST, 1)) { player->sendTextMessage(MSG_STATUS_SMALL, "Please wait a few seconds to move this item."); return false; } } } } int32_t delay = g_config.getNumber(ConfigManager::ANTI_PUSH_DELAY); if (Condition* condition = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_EXHAUST, delay, 0, false, 1)) player->addCondition(condition); if (!g_creatureEvents->executeMoveItems(player, item, mapFromPos, mapToPos)) return false; ReturnValue ret = internalMoveItem(player, fromCylinder, toCylinder, toIndex, item, count, NULL); if (ret != RET_NOERROR) { player->sendCancelMessage(ret); return false; } player->setNextAction(OTSYS_TIME() + g_config.getNumber(ConfigManager::ACTIONS_DELAY_INTERVAL) - 10); return true; } Agora em configmanager.h ADMIN_ENCRYPTION_DATA Adicione abaixo: ANTI_PUSH_ITEMS, em: STAMINA_DESTROY_LOOT, Adicione abaixo: ANTI_PUSH_DELAY, em: ADDONS_PREMIUM, Adicione abaixo: ANTI_PUSH Agora você pode compilar a Source. Configurando no servidor: Abra seu config.lua do servidor e adicione isso dentro qualquer lugar: -- Anti-Push useAntiPush = true antiPushItems = "2148,2152,2160,3976" antiPushDelay = 500 Navegue até o diretório 'creaturescripts' e localize o arquivo 'login.lua'. em resgistros de eventos adicione: login.lua registerCreatureEvent(cid, "MoveItem") registerCreatureEvent(cid, "MoveItem2") Agora abra o aquivo creaturescript .xml <event type="moveitem" name="MoveItem" event="script" value="houseprotecao.lua"/> <event type="moveitem2" name="MoveItem2" event="script" value="moveitem2.lua"/> Crie um novo arquivo lua em scripts com o nome houseprotecao.lua e adicione isso: function onMoveItem(moveItem, frompos, position, cid) if position.x == CONTAINER_POSITION then return true end local house = getHouseFromPos(frompos) or getHouseFromPos(position) --correção 100% if type(house) == "number" then local owner = getHouseOwner(house) if owner == 0 then return false, doPlayerSendCancel(cid, "Isso não é Possível.") end if owner ~= getPlayerGUID(cid) then local sub = getHouseAccessList(house, 0x101):explode("\n") local guest = getHouseAccessList(house, 0x100):explode("\n") local isInvited = false if (#sub > 0) and isInArray(sub, getCreatureName(cid)) then isInvited = true end if (#guest > 0) and isInArray(guest, getCreatureName(cid)) then isInvited = true end if not isInvited then return false, doPlayerSendCancel(cid, "Desculpe, você não está invitado.") end end end return true end Crie um novo arquivo lua em scripts com o nome moveitem2.lua e adicione isso abaixo: local depottiles = {} --piso pra n jogar local depots = {2589} --id dos dps local group = 3 --id dos group 6 é todos. local function checkIfThrow(pos,topos) if topos.x == 0xffff then return false end local thing = getThingFromPos(pos) if isInArray(depottiles,thing.itemid) then if not isInArea(topos,{x=pos.x-1,y=pos.y-1,z=pos.z},{x=pos.x+1,y=pos.y+1, z=pos.z}) then return true end else for i = 1, #depots do if depots[i] == getTileItemById(topos,depots[i]).itemid or getTileInfo(topos).actionid == 7483 then return true end end end return false end function onMoveItem2(cid, item, count, toContainer, fromContainer, fromPos, toPos) if isPlayer(cid) then local pos = getThingPos(cid) if getPlayerGroupId(cid) > group then return true end if checkIfThrow({x=pos.x,y=pos.y,z=pos.z,stackpos=0},toPos) then doPlayerSendCancel(cid,"Não jogue item ai!!") doSendMagicEffect(getThingPos(cid),CONST_ME_POFF) return false end end return true end ajudei?? REP+ CRÉDITOS: @L3K0T Fir3element Summ Wise GOD Wille Yan Lima
-
não contar frag em determinada area (Source otx)
SALVE rapaziada do TK, esses dias vim pensando em novos scripts pro meu OT, e em um deles eu precisava que determinada area não contasse frag pro player que matasse outros, PORÉM eu precisava que os players que morressem nessa area ainda assim tivessem as penalidades da sua morte, procurei por ai, achei alguns scripts que apenas tiravam o SKULL e não realmente o FRAG do player. **script atualizado 22/10/2023** - melhorado e otimizado, levei o script pra puxar as infos por .lua / creatureScripts vou disponibilizar o code aqui, e o que fazer pra determinada area não contar frag. SOURCE OTX 2 / TFS 0.x, Funciona em TFS 1.x mudando as tags e ajeitando as sintaxes. vá em creatureevent.cpp procure por: else if(type == "preparedeath") _type = CREATURE_EVENT_PREPAREDEATH; Adiciona abaixo: else if(type == "nocountfrag") _type = CREATURE_EVENT_NOCOUNTFRAG; procure por: case CREATURE_EVENT_PREPAREDEATH: return "onPrepareDeath"; Adicione abaixo: case CREATURE_EVENT_NOCOUNTFRAG: return "noCountFragArea"; procure por: case CREATURE_EVENT_PREPAREDEATH: return "cid, deathList"; Adicione abaixo: case CREATURE_EVENT_NOCOUNTFRAG: return "cid, target"; agora no mesmo arquivo, vá até o final do arquivo e adicione essa função: uint32_t CreatureEvent::executeNoCountFragArea(Creature* creature, Creature* target) { //noCountFragArea(cid, target) if(m_interface->reserveEnv()) { ScriptEnviroment* env = m_interface->getEnv(); if(m_scripted == EVENT_SCRIPT_BUFFER) { env->setRealPos(creature->getPosition()); std::ostringstream scriptstream; scriptstream << "local cid = " << env->addThing(creature) << std::endl; scriptstream << "local target = " << env->addThing(target) << std::endl; if(m_scriptData) scriptstream << *m_scriptData; bool result = true; if(m_interface->loadBuffer(scriptstream.str())) { lua_State* L = m_interface->getState(); result = m_interface->getGlobalBool(L, "_result", true); } m_interface->releaseEnv(); return result; } else { #ifdef __DEBUG_LUASCRIPTS__ std::ostringstream desc; desc << creature->getName(); env->setEvent(desc.str()); #endif env->setScriptId(m_scriptId, m_interface); env->setRealPos(creature->getPosition()); lua_State* L = m_interface->getState(); m_interface->pushFunction(m_scriptId); lua_pushnumber(L, env->addThing(creature)); lua_pushnumber(L, env->addThing(target)); bool result = m_interface->callFunction(2); m_interface->releaseEnv(); return result; } } else { std::clog << "[Error - CreatureEvent::noCountFragArea] Call stack overflow." << std::endl; return 0; } } agora vá em creatureevent.h procure por: CREATURE_EVENT_PREPAREDEATH adicione abaixo: CREATURE_EVENT_NOCOUNTFRAG procure por: uint32_t executePrepareDeath(Creature* creature, DeathList deathList); Adicione abaixo: uint32_t executeNoCountFragArea(Creature* creature, Creature* target); agora vá em player.cpp procure por: bool Player::onKilledCreature(Creature* target, DeathEntry& entry) abaixo de: War_t enemy; if(targetPlayer->getEnemy(this, enemy)) { if(entry.isLast()) IOGuild::getInstance()->updateWar(enemy); entry.setWar(enemy); } Adicione o seguinte código: if (targetPlayer){ CreatureEventList killEvents = getCreatureEvents(CREATURE_EVENT_NOCOUNTFRAG); for (const auto &event : killEvents) { if (!event->executeNoCountFragArea(this, target)) { return true; } } } // Feito isso, tudo completo na sua source, agora é necessário adicionar o creaturescript dentro do servidor vá até creaturescripts/scripts crie um arquivo chamado, "noCountFragInArea.lua" e dentro dele cole o código: --[[ script feito por feetads / TibiaKing ]]-- --[[ discord: feetads / FeeTads#0246 ]]-- -- Add positions here for which you do not want to count frags local areas = { [1] = {from = {x = 91, y = 122, z = 7}, to = {x = 98, y = 127, z = 7}}, -- from = area superior esquerda / to = area inferior direita (formando um quadrado) } local onlyKillerInArea = false -- only killer need to be in area? function noCountFragArea(cid, target) if not isCreature(cid) or not isCreature(target) then return true end local posKiller = getPlayerPosition(cid) local posTarget = getPlayerPosition(target) for i = 1, #areas do local area = areas[i] if isInArea(posKiller, area.from, area.to) then if onlyKillerInArea then return false elseif isInArea(posTarget, area.from, area.to) then return false end end end return true end agora em creaturescripts.xml <event type="nocountfrag" name="fragarea" event="script" value="noCountFragInArea.lua"/> agora em creaturescripts/scripts/login.lua procure por OU semelhante a esse: registerCreatureEvent(cid, "AdvanceSave") e abaixo adicione: registerCreatureEvent(cid, "fragarea") // Agora tudo certo, quando quiser adiciona uma area que não pega frag, vá até o script e apenas coloque a area, igual o demonstrado no script Exemplo: local areas = { [1] = {from = {x = 91, y = 122, z = 7}, to = {x = 98, y = 127, z = 7}}, [2] = {from = {x = 1000, y = 1000, z = 7}, to = {x = 1100, y = 1100, z = 7}}, } assim somente colocando a area no script e abrindo o server ou dando /reload, já funcionará a area como não pegar frag. Esse sistema pode ser bom pra areas de pvp ativo, onde você ainda quer que o player que morrer perca os atributos, como se fosse uma morte normal, porém não conta frag pra quem matar. Bom pra sistemas tipo castle 48h (guild war), onde há diversas mortes e risco de pegar red, atrapalhando a war. Façam bom proveito dos scripts, e deixem os créditos no script rsrs **Eu fiz as alterações e o simples código por isso vim disponibilizar, créditos meus**
-
[Source] Trocar nome das Skills
Hoje vim trazer como trocar o nome das skills na source!! eu estava procurando como fazer isso mas nunca tinha achado então resolvi eu mesmo mexer e consegui achar e estou trazendo para vocês! já que eu consegui e vi vários querendo e não conseguem vim trazer a solução do tal You advanced to ou You advanced from Versão para TFS 0.4 Abra seu Dev C++ source e vá em Player.cpp e procure por! char advMsg[50]; sprintf(advMsg, "You advanced to magic level %d.", magLevel); sendTextMessage(MSG_EVENT_ADVANCE, advMsg); substitua por: ou do jeito que vocês preferirem com o nome do tal! char advMsg[50]; sprintf(advMsg, "You advanced to taijutsu %d.", magLevel); sendTextMessage(MSG_EVENT_ADVANCE, advMsg); Agora vá em Tools.cpp e procure por! std::string getSkillName(uint16_t skillId, bool suffix/* = true*/) logo abaixo terá isso! switch(skillId) { case SKILL_FIST: { std::string tmp = "fist"; if(suffix) tmp += " fighting"; return tmp; } case SKILL_CLUB: { std::string tmp = "club"; if(suffix) tmp += " fighting"; return tmp; } case SKILL_SWORD: { std::string tmp = "sword"; if(suffix) tmp += " taijutsu"; return tmp; } case SKILL_AXE: { std::string tmp = "axe"; if(suffix) tmp += " fighting"; return tmp; } case SKILL_DIST: { std::string tmp = "distance"; if(suffix) tmp += " fighting"; return tmp; } case SKILL_SHIELD: return "shielding"; case SKILL_FISH: return "fishing"; case SKILL__MAGLEVEL: return "magic level"; case SKILL__LEVEL: return "level"; default: break; } substitua por: ou do jeito que vocês preferirem com o nome do tal! switch(skillId) { case SKILL_FIST: { std::string tmp = ""; if(suffix) tmp += " taijutsu"; return tmp; } case SKILL_CLUB: { std::string tmp = "glove"; if(suffix) tmp += " taijutsu"; return tmp; } case SKILL_SWORD: { std::string tmp = "sword"; if(suffix) tmp += " taijutsu"; return tmp; } case SKILL_AXE: { std::string tmp = "axe"; if(suffix) tmp += " taijutsu"; return tmp; } case SKILL_DIST: { std::string tmp = "distance"; if(suffix) tmp += " taijutsu"; return tmp; } case SKILL_SHIELD: return "defense"; case SKILL_FISH: return "fishing"; case SKILL__MAGLEVEL: return "taijutsu"; case SKILL__LEVEL: return "level"; default: break; } se quiserem mudar o nome das skills na client e só pedir que mostro como faz com prints !!!
-
Push enquanto Targeta
Preciso de ajuda. Na minha base o player não pode empurrar enquanto targeta. Eu quero que ele continue targetando, e empurre o alvo. Agradeço desde já. game.cpp
-
Deixando as Sprites mais Rápida no OTclient
Bom galera hoje vou ensinar a vocês como acelerar as Sprites do seu jogo pra ficar como do Tíbia Global, assista o vídeo abaixo que você vai entender. Primeiramente vá na source do seu OTClient 0.6.6 "otclient-0.6.6\src\client"ou superior e abre o game.cpp depois de aberto procure. if(version >= 1050) mude para if(version <= 1050) Salve e Agora vá em const.h e procure. INVISIBLE_TICKS_PER_FRAME = 500, ITEM_TICKS_PER_FRAME = 500, mude para INVISIBLE_TICKS_PER_FRAME = 100, ITEM_TICKS_PER_FRAME = 100, Feito isso salve e da Rebuild no compilador do otcliente que voce usa "recompilação limpa" Depois de compilar vá no seu Object Builder e compile seu cliente pra "Improved Animations" pronto, agora só usar ATENÇÃO USE SOMENTE EM CASO DE ERROS DO OTCLIENT, CASO NÃO LER SEU CLIENTE; ache function load() local version = g_game.getClientVersion() em baixo coloca g_game.enableFeature(GameSpritesU32)
-
Setas do Cast [OTX 2.x]
E aí guys, venho a um tempo vendo as datapacks que lançam diariamente e resolvi postar o sistema de setas no cast (não sei se alguém já postou algo relativo o_o) para fazer esse sistema eu estou usando a última versão do OTX do github... LINK PARA BAIXAR (otx do mattyx) maioria das pessoas já sabem o que é esse sistema, mas pra quem não sabe, é a possibilidade de alternar o cast apertando CTRL + QUALQUER ARROW, mesmo que já tenha em vários servers, algums ainda não tem então toma aí. em protocolgame.cpp procure por void ProtocolGame::spectate(const std::string& name, const std::string& password) dentro das chaves após player->client->addSpectator(this); coloque: std::ostringstream ss; std::string value = "-1"; player->getStorage("broadcast_start", value); time_t ctime = time(NULL); uint32_t total = ctime - std::stoi(value); uint32_t hours, minutes, seconds; minutes = total / 60; seconds = total % 60; hours = minutes / 60; minutes = minutes % 60; ss << "Players watching " << player->getName() << ": " << (player->client->list().size()) << "\nBroadcast time: "; if (hours > 0) { ss << hours << " hour(s)"; } if (minutes > 0) { ss << minutes << " minute(s)"; } if (seconds > 0) { ss << seconds << " second(s)"; } ss << "\nYou can Switch channels by using CTRL + arrow keys!"; std::string msg = ss.str(); this->m_lastSwitch = time(NULL); this->sendTextMessage(MSG_INFO_DESCR, msg); no final desse escopo após a }, adicione: void ProtocolGame::spectatorTurn(uint8_t direction) { std::vector<Player*> candidates; int index = 0; for (AutoList<Player>::iterator it = Player::autoList.begin(); it != Player::autoList.end(); ++it) { if (!it->second->client->isBroadcasting()) { continue; } if (it->second->client->getPassword() != "") { continue; } if (it->second->client->banned(getIP())) { continue; } if (it->second == player) { index = candidates.size(); } candidates.push_back(it->second); } if (candidates.size() < 2) { return; } if (direction == 0 || direction == 1) { direction = uint8_t(1); } if (direction == 2 || direction == 3) { direction = uint8_t(-1); } if (index == 0 && direction == -1) { direction = uint8_t(0); } Player* _player = candidates[(index + direction) % candidates.size()]; if (!_player || player == _player) { return; } if (!_player || _player->isRemoved() || !_player->client->getOwner()) { return; } if ((time(NULL) - this->m_lastSwitch) < 1) { return; } this->m_lastSwitch = time (NULL); player->client->removeSpectator(this); player->unRef(); player = _player; player->addRef(); knownCreatureSet.clear(); player->sendCreatureAppear(player, this); player->sendContainers(this); if (PrivateChatChannel * channel = g_chat.getPrivateChannel(player)) chat(channel->getId()); player->client->addSpectator(this); } agora em: void ProtocolGame::parsePacket(NetworkMessage &msg) procure por: case 0xA1: parseCancelTarget(msg); break; tenha certeza de estar dentro do escopo: if(m_spectator) { switch(recvbyte) { então abaixo de "case 0xA1: parseCancelTarget(msg); break;" coloque: // Ctrl + Arrow case 0x6F: case 0x70: case 0x71: case 0x72: Dispatcher::getInstance().addTask( createTask(boost::bind(&ProtocolGame::spectatorTurn, this, recvbyte - 0x6F))); break; feito, mas agora precisamos adicionar algumas coisas em protocolgame.h procure por: void chat(uint16_t channelId); adicione abaixo: void spectatorTurn(uint8_t direction); agora, lá no final do arquivo, procure por: Player* player; coloque abaixo: time_t m_lastSwitch; agora só compilar e testar apenas umas imagem mostrando que deu certo abaixo... caso der algum tipo de erro, fico a disposição para ajudar Opcional: mensagem MOTD (mensagem do dia) modificada igual abaixo: 2: informações de level / specs ativos na janela de login igual a imagem abaixo: é isso. quem fez esse sistema eu não sei, apenas peguei de uma source que tinha e coloquei explicado aqui, então de créditos eu coloco 1% eu @stackcheese por ter explicado como por hihi