Ir para conteúdo

Enenra

Membro
  • Registro em

  • Última visita

Histórico de Curtidas

  1. Gostei
    Enenra deu reputação a Tio Rusher em (Action) Double Exp Global   
    Estou com o tempo bem apertado, mas pode usar isso como base


    Servidor/mods/nome.xml
    <?xml version="1.0" encoding="UTF-8"?> <mod name="Double Exp Global" enabled="yes" author="MatheusMkalo/Tio Rusher" forum="Xtibia/Tibia King"> <!-- Configuração --> <config name="PotionExpConfigs"><![CDATA[ ------ CONFIGURE SEU SCRIPT ------ TRUE ou FALSE configs = { time = 720, ---- Tempo em minutos needpa = FALSE, needlvl = {FALSE, level = 1}, costmana = {FALSE, mana = 1}, addrate = 100, -- Exp que vai adicionar em % removeonuse = TRUE } function getTime(s) local n = math.floor(s / 60) s = s - (60 * n) return n, s end CreatureEventChecker = function(event, ...) if isCreature(arg[1]) then event(unpack(arg)) end end creatureEvent = function(event, delay, ...) addEvent(CreatureEventChecker, delay, event, unpack(arg)) end function getPlayerExtraExpRate(cid) return (getPlayerRates(cid)[8]-1)*100 end ]]></config> <!-- exppotion.lua --> <action itemid="7440" event="script"><![CDATA[ -- ID do item domodlib('PotionExpConfigs') if getPlayerStorageValue(cid, 62164) >= 1 then doRemoveItem(item.uid, 1) return broadcastMessage("Um Double Exp foi ativado, e bla bla bla", MESSAGE_STATUS_CONSOLE_BLUE) end if configs.needpa and not isPremium(cid) then return doPlayerSendCancel(cid, "Voce precisa ser uma conta premmium para usar este item.") end if configs.needlvl[1] and getPlayerLevel(cid) < configs.needlvl.level then return doPlayerSendCancel(cid, "Voce precisa do nivel " .. configs.needlvl.level .. " para usar este item") end if configs.costmana[1] then if getCreatureMana(cid) < configs.costmana.mana then return doPlayerSendCancel(cid, "Voce precisa " .. configs.costmana.mana .. " mana para usar este item") else doCreatureAddMana(cid, -configs.costmana.mana) end end if configs.removeonuse then doRemoveItem(item.uid, 1) end for i = configs.time*60, 1, -1 do local a = math.floor(i/60) .. ":" .. i - (60 * math.floor(i/60)) if #a < 4 then a = string.sub(a,1,2) .. "0" .. string.sub(a, 3) end if i == configs.time*60 then creatureEvent(doPlayerSendCancel, configs.time*60*1000, cid, "O efeito do Elexir do Conhecimento se esgotou") end creatureEvent(doPlayerSendCancel, (configs.time*60-i)*1000, cid, "O Efeito do Elixir do conhecimento expira em: "..a.."minutos") end doPlayerSetExperienceRate(cid, (1+(configs.addrate/100))+(getPlayerExtraExpRate(cid)/100)) creatureEvent(doPlayerSetExperienceRate, configs.time *60*1000, cid, 1+(getPlayerExtraExpRate(cid)/100-(configs.addrate/100))) doPlayerSendTextMessage(cid, 22, "Agora voce esta sob o efeito do Elixir do conhecimento e ganhara double exp") setPlayerStorageValue(cid, 62164, os.time()) caixinha = creatureEvent(setPlayerStorageValue, configs.time *60*1000, cid, 62164, 0) return TRUE ]]></action> <creaturescript type="login" name="ExpPotion" event="script"><![CDATA[ domodlib('PotionExpConfigs') local time = configs.time if os.time()-getPlayerStorageValue(cid, 62164) < time *60 then doPlayerSetExperienceRate(cid, (1+(configs.addrate/100))+(getPlayerExtraExpRate(cid)/100)) creatureEvent(doPlayerSetExperienceRate, (time*60-(os.time()-getPlayerStorageValue(cid, 62164))) * 1000, cid, 1+(getPlayerExtraExpRate(cid)/100-(configs.addrate/100))) creatureEvent(setPlayerStorageValue, (time*60-(os.time()-getPlayerStorageValue(cid, 62164))) * 1000 , cid, 62164, 0) for i = (time*60-(os.time()-getPlayerStorageValue(cid, 62164))), 1, -1 do local a = math.floor(i/60) .. ":" .. i - (60 * math.floor(i/60)) if #a < 4 then a = string.sub(a,1,2) .. "0" .. string.sub(a, 3) end if i == (time*60-(os.time()-getPlayerStorageValue(cid, 62164))) then creatureEvent(doPlayerSendCancel, (time*60-(os.time()-getPlayerStorageValue(cid, 62164)))*1000, cid, "O Efeito do Elixir do conhecimento chegou ao fim") end creatureEvent(doPlayerSendCancel, ((time*60-(os.time()-getPlayerStorageValue(cid, 62164)))-i)*1000, cid, "O Efeito do Elixir do conhecimento expira em: "..a.." minutos") end end return TRUE ]]></creaturescript> </mod>  
  2. Gostei
    Enenra deu reputação a MatteusDeli em Mudar posição na Action   
    @Enenra Tenta trocar essa linha:
    local atual = {x = getPlayerPosition(cid).x + 1, y = getPlayerPosition(cid).y + 0, z = getPlayerPosition(cid).z} Por essa :
    local atual = {x = getPlayerPosition(cid).x, y = getPlayerPosition(cid).y, z = getPlayerPosition(cid).z}
  3. Gostei
    Enenra deu reputação a luangop em Mudar posição na Action   
    -- CONFIGURAÇÕES aurastr = 25950 -- storage da aura estr = 25951 -- storage para o exhaust porcentagem = 100 -- chance de curar em cada volta da aura, em porcentagem quantheal = 1 -- porcentagem do hp máximo que cada cura irá curar. (No caso, irá curar 10% do hp máximo cada cura) tempo = 5000 -- tempo para dar uma volta no player (este tempo foi o que achei mais agradável visualmente, é recomendável não mudar) tipoaura = 97 -- número do efeito da aura (efeito de distância, pode ser identificado com /x no jogo) efeitocura = 92 -- número do efeito quando a cura chega ao player (efeito de posição fixa, pode ser identificado com /z no jogo) -- Função que chama a aura function efeitosAura(i,tm,cid) if(isCreature(cid)) then local atual = {x = getPlayerPosition(cid).x + 1, y = getPlayerPosition(cid).y + 0, z = getPlayerPosition(cid).z} local chances = math.random(100) if(chances<=porcentagem/8 and getCreatureHealth(cid)<getCreatureMaxHealth(cid)) then doCreatureAddHealth(cid, getCreatureMaxHealth(cid)/quantheal) if(i<=8 and i>1) then doSendMagicEffect(getPlayerPosition(cid), tipoaura) else doSendMagicEffect(getPlayerPosition(cid), tipoaura) end doSendMagicEffect(atual, efeitocura) end if(i==8) then doSendMagicEffect(getPlayerPosition(cid), tipoaura) elseif(i<8) then doSendMagicEffect(getPlayerPosition(cid), tipoaura) end if(i<=8 and getPlayerStorageValue(cid, aurastr)==2) then i = i+1 tm = tempo/8 return addEvent(efeitosAura,tm,i,tm,cid) elseif(i>8 and getPlayerStorageValue(cid, aurastr)==2) then return efeitosAura(1,0,cid) else return TRUE end else return TRUE end end function onUse(cid, item, fromPosition, itemEx, toPosition) if(getPlayerStorageValue(cid, aurastr)==2) then setPlayerStorageValue(cid, estr, os.time()+2) setPlayerStorageValue(cid, aurastr, -1) doPlayerSendCancel(cid,"Aura desligada!") else doPlayerSendCancel(cid,"Aura ligada!") setPlayerStorageValue(cid, aurastr, 2) efeitosAura(1,tempo/8,cid) end return TRUE end  
  4. Gostei
    Enenra deu reputação a Fabi Marzan em (SPELL) Spell com Cooldown   
    Tambem esse,
    local combat1 = createCombatObject() setCombatParam(combat1, COMBAT_PARAM_TYPE, COMBAT_DEATHDAMAGE) setCombatParam(combat1, COMBAT_PARAM_EFFECT, CONST_ME_MORTAREA) setCombatParam(combat1, COMBAT_PARAM_DISTANCEEFFECT, 27) function onGetFormulaValues(cid, level, skill, attack, factor) local k = getPlayerStorageValue(cid, 378378) local skillTotal, levelTotal = skill + attack, level / 5 return -(skillTotal * 2.5 + levelTotal)-(skillTotal * 2.5 + levelTotal)*(k*1.0), -(skillTotal * 2.8 + levelTotal)-(skillTotal * 2.9 + levelTotal)*(k*1.0) end setCombatCallback(combat1, CALLBACK_PARAM_SKILLVALUE, "onGetFormulaValues") local combat2 = createCombatObject() setCombatParam(combat2, COMBAT_PARAM_TYPE, COMBAT_DEATHDAMAGE) setCombatParam(combat2, COMBAT_PARAM_EFFECT, CONST_ME_MORTAREA) setCombatParam(combat2, COMBAT_PARAM_DISTANCEEFFECT, 27) setCombatFormula(combat2, COMBAT_FORMULA_LEVELMAGIC, -10, -10, -1, -20, 5, 5, 2.4, 10.1) local combat3 = createCombatObject() setCombatParam(combat3, COMBAT_PARAM_TYPE, COMBAT_DEATHDAMAGE) setCombatParam(combat3, COMBAT_PARAM_EFFECT, CONST_ME_MORTAREA) setCombatParam(combat3, COMBAT_PARAM_DISTANCEEFFECT, 27) setCombatFormula(combat3, COMBAT_FORMULA_LEVELMAGIC, -10, -10, -1, -20, 5, 5, 2.4, 10.1) local function onCastSpell1(parameters) doCombat(parameters.cid, parameters.combat1, parameters.var) end local function onCastSpell2(parameters) doCombat(parameters.cid, parameters.combat2, parameters.var) end local function onCastSpell3(parameters) doCombat(parameters.cid, parameters.combat3, parameters.var) end function onCastSpell(cid, var) local waittime = 30 -- Tempo de exhaustion local storage = 3 -- não mecha if exhaustion.check(cid, storage) then doPlayerSendCancel(cid, "You are Exhausted.") doSendMagicEffect(getCreaturePosition(cid), 2) return false end local parameters = {cid = cid, var = var, combat1 = combat1, combat2 = combat2, combat3 = combat3} addEvent(onCastSpell1, 1000, parameters) addEvent(onCastSpell2, 1500, parameters) addEvent(onCastSpell3, 2000, parameters) exhaustion.set(cid, storage, waittime) return true end  
  5. Obrigado
    Enenra deu reputação a Kyle Bellini em (SPELL) Spell com Cooldown   
    Olá,
     
    Testa aí e me fala se está como quer. Coloquei uma mensagem tbm avisando os segundos pro jogador saber.
     
     
     
  6. Gostei
    Enenra deu reputação a vine96 em Baiak-PvP [8.60] - Watch System + Cast Look   
    Bem simples de resolver ao criar o banco de dados para o server coloca como: utf8mb4_unicode_ci
    Galera vou fazer a minha boa ação de fim de ano.
     
    Explicando: essa base do Baiak PVP é realmente muito boa e bem estável eu estou a quase 2 meses ininterruptos com o server online e sem problemas de crash, rollback ou qualquer coisa do tipo, já testei diversos bugs conhecidos de derrubar servidor e nada acontece com essas sources, é uma base realmente muito sólida e ótima para um servidor 8.6.
     
    Porém recentemente descobri um bug que é o seguinte, ao player utilizar !leavehouse ou perder a house e afins, os seus itens não vão para o depot, eles simplesmente somem.
     
    O distro quem compilou foi eu com base nas sources disponibilizadas aqui mesmo, no caso compilei para ubuntu 14.04.
     
    Ao testar no testserver que possuo, e utilizando outra distro com sources acredito ser de outra revisão do TFS 0.4 esse bug não acontece, os itens são entregue normalmente ao depot do jogador.
     
    Descobri então que se trata de um bug nas sources, no caso a house.cpp
     
    Fiz uma alteração no arquivo e após isso ficou 100%, para tal vou disponibilizar o arquivo editado aqui para vocês.
     
    Troque todo o código do arquivo house.cpp por esse:
     
    //////////////////////////////////////////////////////////////////////// // OpenTibia - an opensource roleplaying game //////////////////////////////////////////////////////////////////////// // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. //////////////////////////////////////////////////////////////////////// #include "otpch.h" #include "house.h" #include "tools.h" #include "database.h" #include "beds.h" #include "town.h" #include "iologindata.h" #include "ioguild.h" #include "iomapserialize.h" #include "configmanager.h" #include "game.h" extern ConfigManager g_config; extern Game g_game; House::House(uint32_t houseId) { guild = pendingTransfer = isprotected = false; name = "OTX headquarter (Flat 1, Area 42)"; entry = Position(); id = houseId; rent = price = townId = paidUntil = owner = rentWarnings = lastWarning = 0; syncFlags = HOUSE_SYNC_NAME | HOUSE_SYNC_TOWN | HOUSE_SYNC_SIZE | HOUSE_SYNC_PRICE | HOUSE_SYNC_RENT | HOUSE_SYNC_GUILD; } void House::addTile(HouseTile* tile) { tile->setFlag(TILESTATE_PROTECTIONZONE); houseTiles.push_back(tile); } void House::addBed(BedItem* bed) { bedsList.push_back(bed); bed->setHouse(this); } void House::addDoor(Door* door) { door->addRef(); doorList.push_back(door); door->setHouse(this); updateDoorDescription("", door); } void House::removeDoor(Door* door) { HouseDoorList::iterator it = std::find(doorList.begin(), doorList.end(), door); if(it != doorList.end()) { (*it)->unRef(); doorList.erase(it); } } Door* House::getDoorByNumber(uint8_t doorId) const { for(HouseDoorList::const_iterator it = doorList.begin(); it != doorList.end(); ++it) { if((*it)->getDoorId() == doorId) return (*it); } return NULL; } Door* House::getDoorByPosition(const Position& pos) { for(HouseDoorList::iterator it = doorList.begin(); it != doorList.end(); ++it) { if((*it)->getPosition() == pos) return (*it); } return NULL; } void House::setOwner(uint32_t guid) { owner = guid; updateDoorDescription(); } bool House::setOwnerEx(uint32_t guid, bool transfer) { if(owner == guid) return true; if(isGuild() && guid) { Player* player = g_game.getPlayerByGuidEx(guid); if(!player) return false; guid = player->getGuildId(); if(player->isVirtual()) delete player; } if(owner) { rentWarnings = paidUntil = 0; if(transfer) clean(); setAccessList(SUBOWNER_LIST, "", !transfer); setAccessList(GUEST_LIST, "", !transfer); for(HouseDoorList::iterator it = doorList.begin(); it != doorList.end(); ++it) (*it)->setAccessList(""); } setOwner(guid); lastWarning = guid ? time(NULL) : 0; Database* db = Database::getInstance(); DBTransaction trans; if(!trans.begin()) return false; IOMapSerialize::getInstance()->saveHouse(db, this); return trans.commit(); } bool House::isGuild() const { return g_config.getBool(ConfigManager::GUILD_HALLS) && guild; } bool House::isBidded() const { Database* db = Database::getInstance(); DBResult_ptr result; DBQuery query; query << "SELECT `house_id` FROM `house_auctions` WHERE `house_id` = " << id << " LIMIT 1"; if(!(result = db->storeQuery(query.str()))) return false; return true; } void House::updateDoorDescription(std::string _name/* = ""*/, Door* door/* = NULL*/) { std::string tmp = "house"; if(isGuild()) tmp = "hall"; char houseDescription[200]; if(owner) { if(isGuild()) IOGuild::getInstance()->getGuildById(_name, owner); else if(_name.empty()) IOLoginData::getInstance()->getNameByGuid(owner, _name); sprintf(houseDescription, "It belongs to %s '%s'. %s owns this %s.", tmp.c_str(), name.c_str(), _name.c_str(), tmp.c_str()); } else sprintf(houseDescription, "It belongs to %s '%s'. Nobody owns this %s. It costs %d gold coins.", tmp.c_str(), name.c_str(), tmp.c_str(), price); if(!door) { for(HouseDoorList::iterator it = doorList.begin(); it != doorList.end(); ++it) (*it)->setSpecialDescription(houseDescription); } else door->setSpecialDescription(houseDescription); } void House::removePlayer(Player* player, bool ignoreRights) { if(!ignoreRights && player->hasFlag(PlayerFlag_CanEditHouses)) return; Position curPos = player->getPosition(), newPos = g_game.getClosestFreeTile(player, entry, false, false); if(g_game.internalTeleport(player, newPos, false) == RET_NOERROR && !player->isGhost()) { g_game.addMagicEffect(curPos, MAGIC_EFFECT_POFF); g_game.addMagicEffect(newPos, MAGIC_EFFECT_TELEPORT); } } void House::removePlayers(bool ignoreInvites) { PlayerVector kickList; for(HouseTileList::iterator it = houseTiles.begin(); it != houseTiles.end(); ++it) { CreatureVector* creatures = (*it)->getCreatures(); if(!creatures) continue; Player* player = NULL; for(CreatureVector::iterator cit = creatures->begin(); cit != creatures->end(); ++cit) { if((player = (*cit)->getPlayer()) && !player->isRemoved() && (ignoreInvites || !isInvited(player))) kickList.push_back(player); } } if(kickList.empty()) return; for(PlayerVector::iterator it = kickList.begin(); it != kickList.end(); ++it) removePlayer((*it), false); } bool House::kickPlayer(Player* player, Player* target) { if(!target || target->isRemoved()) return false; HouseTile* houseTile = target->getTile()->getHouseTile(); if(!houseTile || houseTile->getHouse() != this) return false; bool self = player == target; if(getHouseAccessLevel(player) < getHouseAccessLevel(target) && !self) return false; removePlayer(target, self); return true; } void House::clean() { for(HouseBedList::iterator bit = bedsList.begin(); bit != bedsList.end(); ++bit) { if((*bit)->getSleeper()) (*bit)->wakeUp(); } removePlayers(true); transferToDepot(); } bool House::transferToDepot() { if(!townId) return false; Player* player = NULL; if(owner) { uint32_t tmp = owner; if(isGuild() && !IOGuild::getInstance()->swapGuildIdToOwner(tmp)) tmp = 0; if(tmp) player = g_game.getPlayerByGuidEx(tmp); } Item* item = NULL; Container* tmpContainer = NULL; ItemList moveList; for(HouseTileList::iterator it = houseTiles.begin(); it != houseTiles.end(); ++it) { for(uint32_t i = 0; i < (*it)->getThingCount(); ++i) { if(!(item = (*it)->__getThing(i)->getItem())) continue; if(item->isPickupable()) moveList.push_back(item); else if((tmpContainer = item->getContainer())) { for(ItemList::const_iterator it = tmpContainer->getItems(); it != tmpContainer->getEnd(); ++it) moveList.push_back(*it); } } } if(player) { Depot* depot = player->getDepot(townId, true); for(ItemList::iterator it = moveList.begin(); it != moveList.end(); ++it) g_game.internalMoveItem(NULL, (*it)->getParent(), depot, INDEX_WHEREEVER, (*it), (*it)->getItemCount(), NULL, FLAG_NOLIMIT); if(player->isVirtual()) { IOLoginData::getInstance()->savePlayer(player); delete player; } } else { for(ItemList::iterator it = moveList.begin(); it != moveList.end(); ++it) g_game.internalRemoveItem(NULL, (*it), (*it)->getItemCount(), false, FLAG_NOLIMIT); } return true; } bool House::isInvited(const Player* player) { return getHouseAccessLevel(player) != HOUSE_NO_INVITED; } AccessHouseLevel_t House::getHouseAccessLevel(const Player* player) { if(!player) return HOUSE_NO_INVITED; if(player->hasFlag(PlayerFlag_CanEditHouses)) return HOUSE_OWNER; if(!owner) return HOUSE_NO_INVITED; AccessHouseLevel_t tmp = HOUSE_NO_INVITED; if(isGuild()) { if(player->getGuildId() == owner) { switch(player->getGuildLevel()) { case GUILDLEVEL_LEADER: return HOUSE_OWNER; case GUILDLEVEL_VICE: return HOUSE_SUBOWNER; default: tmp = HOUSE_GUEST; } } } else if(player->getGUID() == owner/* || player->marriage == owner*/) return HOUSE_OWNER; if(subOwnerList.isInList(player)) return HOUSE_SUBOWNER; if(guestList.isInList(player)) return HOUSE_GUEST; return tmp; } bool House::canEditAccessList(uint32_t listId, const Player* player) { switch(getHouseAccessLevel(player)) { case HOUSE_OWNER: return true; case HOUSE_SUBOWNER: return listId == GUEST_LIST; default: break; } return false; } bool House::getAccessList(uint32_t listId, std::string& list) const { if(listId == GUEST_LIST) { guestList.getList(list); return true; } if(listId == SUBOWNER_LIST) { subOwnerList.getList(list); return true; } if(Door* door = getDoorByNumber(listId)) return door->getAccessList(list); #ifdef __DEBUG_HOUSES__ std::clog << "[Failure - House::getAccessList] door == NULL, listId = " << listId <<std::endl; #endif return false; } void House::setAccessList(uint32_t listId, const std::string& textlist, bool teleport/* = true*/) { if(listId == GUEST_LIST) guestList.parseList(textlist); else if(listId == SUBOWNER_LIST) subOwnerList.parseList(textlist); else { if(Door* door = getDoorByNumber(listId)) door->setAccessList(textlist); #ifdef __DEBUG_HOUSES__ else std::clog << "[Failure - House::setAccessList] door == NULL, listId = " << listId <<std::endl; #endif return; } if(teleport) removePlayers(false); } TransferItem* TransferItem::createTransferItem(House* house) { TransferItem* transferItem = new TransferItem(house); transferItem->addRef(); transferItem->setID(ITEM_HOUSE_TRANSFER); char buffer[150]; sprintf(buffer, "It is a %s transfer document for '%s'.", house->isGuild() ? "guild hall" : "house", house->getName().c_str()); transferItem->setSpecialDescription(buffer); transferItem->setSubType(1); return transferItem; } bool TransferItem::onTradeEvent(TradeEvents_t event, Player* owner, Player* seller) { switch(event) { case ON_TRADE_TRANSFER: { if(house) house->setOwnerEx(owner->getGUID(), true); g_game.internalRemoveItem(NULL, this, getItemCount()); seller->transferContainer.setParent(NULL); break; } case ON_TRADE_CANCEL: { owner->transferContainer.setParent(NULL); owner->transferContainer.__removeThing(this, getItemCount()); g_game.freeThing(this); break; } default: return false; } return true; } void AccessList::getList(std::string& _list) const { _list = list; } bool AccessList::parseList(const std::string& _list) { playerList.clear(); guildList.clear(); expressionList.clear(); regexList.clear(); list = _list; if(_list.empty()) return true; std::stringstream listStream(_list); std::string line; while(getline(listStream, line)) { trimString(line); trim_left(line, "\t"); trim_right(line, "\t"); trimString(line); toLowerCaseString(line); if(line.empty()) break; if(line.substr(0, 1) == "#" || line.length() > 100) continue; if(line.find("@") != std::string::npos) { std::string::size_type pos = line.find("@"); addGuild(line.substr(pos + 1), line.substr(0, pos)); } else if(line.find("!") != std::string::npos || line.find("*") != std::string::npos || line.find("?") != std::string::npos) addExpression(line); else addPlayer(line); } return true; } bool AccessList::isInList(const Player* player) { std::string name = player->getName(); boost::cmatch what; try { toLowerCaseString(name); for(RegexList::iterator it = regexList.begin(); it != regexList.end(); ++it) { if(boost::regex_match(name.c_str(), what, it->first)) return it->second; } } catch(...) {} if(playerList.find(player->getGUID()) != playerList.end()) return true; for(GuildList::iterator git = guildList.begin(); git != guildList.end(); ++git) { if(git->first == player->getGuildId() && ((uint32_t)git->second == player->getRankId() || git->second == -1)) return true; } return false; } bool AccessList::addPlayer(std::string& name) { std::string tmp = name; uint32_t guid; if(!IOLoginData::getInstance()->getGuidByName(guid, tmp) || playerList.find(guid) != playerList.end()) return false; playerList.insert(guid); return true; } bool AccessList::addGuild(const std::string& guildName, const std::string& rankName) { uint32_t guildId; if(!IOGuild::getInstance()->getGuildId(guildId, guildName)) return false; std::string tmp = rankName; int32_t rankId = IOGuild::getInstance()->getRankIdByName(guildId, tmp); if(!rankId && (tmp.find("?") == std::string::npos || tmp.find("!") == std::string::npos || tmp.find("*") == std::string::npos)) rankId = -1; if(!rankId) return false; for(GuildList::iterator git = guildList.begin(); git != guildList.end(); ++git) { if(git->first == guildId && git->second == rankId) return true; } guildList.push_back(std::make_pair(guildId, rankId)); return true; } bool AccessList::addExpression(const std::string& expression) { for(ExpressionList::iterator it = expressionList.begin(); it != expressionList.end(); ++it) { if((*it) == expression) return false; } std::string out, meta = ".[{}()\\+|^$"; for(std::string::const_iterator it = expression.begin(); it != expression.end(); ++it) { if(meta.find(*it) != std::string::npos) out += "\\"; out += (*it); } replaceString(out, "**", ""); replaceString(out, "*", ".*"); replaceString(out, "?", ".?"); try { if(out.length() > 0) { expressionList.push_back(out); if(out.substr(0, 1) == "!") { if(out.length() > 1) regexList.push_front(std::make_pair(boost::regex(out.substr(1)), false)); } else regexList.push_back(std::make_pair(boost::regex(out), true)); } } catch(...) {} return true; } Door::~Door() { delete accessList; } Attr_ReadValue Door::readAttr(AttrTypes_t attr, PropStream& propStream) { if(attr != ATTR_HOUSEDOORID) return Item::readAttr(attr, propStream); uint8_t _doorId = 0; if(!propStream.getByte(_doorId)) return ATTR_READ_ERROR; doorId = _doorId; return ATTR_READ_CONTINUE; } void Door::copyAttributes(Item* item) { Item::copyAttributes(item); if(Door* door = item->getDoor()) { doorId = door->getDoorId(); std::string list; if(door->getAccessList(list)) setAccessList(list); } } void Door::onRemoved() { Item::onRemoved(); if(house) house->removeDoor(this); } bool Door::canUse(const Player* player) { if(!house || house->getHouseAccessLevel(player) >= HOUSE_SUBOWNER) return true; return accessList->isInList(player); } void Door::setHouse(House* _house) { if(house) return; house = _house; if(!accessList) accessList = new AccessList(); } bool Door::getAccessList(std::string& list) const { if(!house) return false; accessList->getList(list); return true; } void Door::setAccessList(const std::string& textlist) { if(!accessList) accessList = new AccessList(); accessList->parseList(textlist); } Houses::Houses() { rentPeriod = RENTPERIOD_NEVER; std::string strValue = asLowerCaseString(g_config.getString(ConfigManager::HOUSE_RENT_PERIOD)); if(strValue == "yearly") rentPeriod = RENTPERIOD_YEARLY; else if(strValue == "monthly") rentPeriod = RENTPERIOD_MONTHLY; else if(strValue == "weekly") rentPeriod = RENTPERIOD_WEEKLY; else if(strValue == "daily") rentPeriod = RENTPERIOD_DAILY; } bool Houses::loadFromXml(std::string filename) { xmlDocPtr doc = xmlParseFile(filename.c_str()); if(!doc) { std::clog << "[Warning - Houses::loadFromXml] Cannot load houses file." << std::endl; std::clog << getLastXMLError() << std::endl; return false; } xmlNodePtr houseNode, root = xmlDocGetRootElement(doc); if(xmlStrcmp(root->name,(const xmlChar*)"houses")) { std::clog << "[Error - Houses::loadFromXml] Malformed houses file." << std::endl; xmlFreeDoc(doc); return false; } int32_t intValue; std::string strValue; houseNode = root->children; while(houseNode) { if(xmlStrcmp(houseNode->name,(const xmlChar*)"house")) { houseNode = houseNode->next; continue; } int32_t houseId = 0; if(!readXMLInteger(houseNode, "houseid", houseId)) { std::clog << "[Error - Houses::loadFromXml] Could not read houseId" << std::endl; xmlFreeDoc(doc); return false; } House* house = Houses::getInstance()->getHouse(houseId); if(!house) { std::clog << "[Error - Houses::loadFromXml] Unknown house with id: " << houseId << std::endl; xmlFreeDoc(doc); return false; } Position entry(0, 0, 0); if(readXMLInteger(houseNode, "entryx", intValue)) entry.x = intValue; if(readXMLInteger(houseNode, "entryy", intValue)) entry.y = intValue; if(readXMLInteger(houseNode, "entryz", intValue)) entry.z = intValue; if(readXMLString(houseNode, "name", strValue)) house->setName(strValue); else house->resetSyncFlag(House::HOUSE_SYNC_NAME); house->setEntry(entry); if(!entry.x || !entry.y) { std::clog << "[Warning - Houses::loadFromXml] House entry not set for: " << house->getName() << " (" << houseId << ")" << std::endl; } if(readXMLInteger(houseNode, "townid", intValue)) house->setTownId(intValue); else house->resetSyncFlag(House::HOUSE_SYNC_TOWN); if(readXMLInteger(houseNode, "size", intValue)) house->setSize(intValue); else house->resetSyncFlag(House::HOUSE_SYNC_SIZE); if(readXMLString(houseNode, "guildhall", strValue)) house->setGuild(booleanString(strValue)); else house->resetSyncFlag(House::HOUSE_SYNC_GUILD); uint32_t rent = 0; if(readXMLInteger(houseNode, "rent", intValue)) rent = intValue; uint32_t price = (house->getSize() + house->getBedsCount()) * g_config.getNumber(ConfigManager::HOUSE_PRICE); // we should let players to pay only for walkable tiles + beds as single units not two items. if(g_config.getBool(ConfigManager::HOUSE_RENTASPRICE) && rent) price = rent; house->setPrice(price); if(g_config.getBool(ConfigManager::HOUSE_PRICEASRENT)) house->setRent(price); else house->setRent(rent); house->setOwner(0); houseNode = houseNode->next; } xmlFreeDoc(doc); return true; } void Houses::check() { uint64_t start = OTSYS_TIME(); std::clog << "> Checking houses..." << std::endl; time_t currentTime = time(NULL); for(HouseMap::iterator it = houseMap.begin(); it != houseMap.end(); ++it) payHouse(it->second, currentTime, 0); std::clog << "Houses checked in " << (OTSYS_TIME() - start) / (1000.) << " seconds." << std::endl; } bool Houses::payRent(Player* player, House* house, uint32_t bid, time_t _time/* = 0*/) { if(rentPeriod == RENTPERIOD_NEVER || !house->getOwner() || house->getPaidUntil() > _time || !house->getRent() || player->hasCustomFlag(PlayerCustomFlag_IgnoreHouseRent)) return true; Town* town = Towns::getInstance()->getTown(house->getTownId()); if(!town) return false; bool paid = false; uint32_t amount = house->getRent() + bid; if(g_config.getBool(ConfigManager::BANK_SYSTEM) && player->balance >= amount) { player->balance -= amount; paid = true; } else if(Depot* depot = player->getDepot(town->getID(), true)) paid = g_game.removeMoney(depot, amount, FLAG_NOLIMIT); if(!paid) return false; if(!_time) _time = time(NULL); uint32_t paidUntil = _time; switch(rentPeriod) { case RENTPERIOD_DAILY: paidUntil += 86400; break; case RENTPERIOD_WEEKLY: paidUntil += 7 * 86400; break; case RENTPERIOD_MONTHLY: paidUntil += 30 * 86400; break; case RENTPERIOD_YEARLY: paidUntil += 365 * 86400; break; default: break; } house->setLastWarning(0); house->setRentWarnings(0); house->setPaidUntil(paidUntil); return true; } bool Houses::payHouse(House* house, time_t _time, uint32_t bid) { if(rentPeriod == RENTPERIOD_NEVER || !house->getOwner() || house->getPaidUntil() > _time || !house->getRent()) return true; Town* town = Towns::getInstance()->getTown(house->getTownId()); if(!town) return false; uint32_t owner = house->getOwner(); if(house->isGuild() && !IOGuild::getInstance()->swapGuildIdToOwner(owner)) { house->setOwnerEx(0, true); return false; } std::string name; if(!IOLoginData::getInstance()->getNameByGuid(owner, name)) { house->setOwnerEx(0, true); return false; } Player* player = g_game.getPlayerByNameEx(name); if(!player) return false; if(!player->isPremium() && g_config.getBool(ConfigManager::HOUSE_NEED_PREMIUM)) { house->setOwnerEx(0, true); if(player->isVirtual()) delete player; return false; } int32_t loginClean = g_config.getNumber(ConfigManager::HOUSE_CLEAN_OLD); if(loginClean && _time >= (player->getLastLogin() + loginClean)) { house->setOwnerEx(0, true); if(player->isVirtual()) delete player; return false; } if(payRent(player, house, bid, _time) || _time < (house->getLastWarning() + 86400)) { if(player->isVirtual()) { IOLoginData::getInstance()->savePlayer(player); delete player; } return true; } uint32_t warningsLimit = 7; switch(rentPeriod) { case RENTPERIOD_DAILY: warningsLimit = 1; break; case RENTPERIOD_WEEKLY: warningsLimit = 3; break; case RENTPERIOD_YEARLY: warningsLimit = 14; break; default: break; } uint32_t warnings = house->getRentWarnings(); if(warnings >= warningsLimit) { house->setOwnerEx(0, true); if(player->isVirtual()) delete player; return false; } if(Depot* depot = player->getDepot(town->getID(), true)) { if(Item* letter = Item::CreateItem(ITEM_LETTER_STAMPED)) { if(g_game.internalAddItem(NULL, depot, letter, INDEX_WHEREEVER, FLAG_NOLIMIT) == RET_NOERROR) { letter->setWriter(g_config.getString(ConfigManager::SERVER_NAME)); letter->setDate(std::time(NULL)); std::stringstream s; s << "Warning!\nThe "; switch(rentPeriod) { case RENTPERIOD_DAILY: s << "daily"; break; case RENTPERIOD_WEEKLY: s << "weekly"; break; case RENTPERIOD_MONTHLY: s << "monthly"; break; case RENTPERIOD_YEARLY: s << "annual"; break; default: break; } s << " rent of " << house->getRent() << " gold for your " << (house->isGuild() ? "guild hall" : "house") << " \"" << house->getName() << "\" has to be paid. Have it within " << (warningsLimit - warnings) << " days or you will lose your " << (house->isGuild() ? "guild hall" : "house") << "."; letter->setText(s.str().c_str()); if(player->isVirtual()) IOLoginData::getInstance()->savePlayer(player); } else g_game.freeThing(letter); } } house->setLastWarning(_time); house->setRentWarnings(++warnings); if(player->isVirtual()) delete player; return false; } House* Houses::getHouse(uint32_t houseId, bool add/*= false*/) { HouseMap::iterator it = houseMap.find(houseId); if(it != houseMap.end()) return it->second; if(!add) return NULL; houseMap[houseId] = new House(houseId); return houseMap[houseId]; } House* Houses::getHouseByPlayer(Player* player) { if(!player || player->isRemoved()) return NULL; HouseTile* houseTile = player->getTile()->getHouseTile(); if(!houseTile) return NULL; if(House* house = houseTile->getHouse()) return house; return NULL; } House* Houses::getHouseByPlayerId(uint32_t playerId) { for(HouseMap::iterator it = houseMap.begin(); it != houseMap.end(); ++it) { if(!it->second->isGuild() && it->second->getOwner() == playerId) return it->second; } return NULL; } House* Houses::getHouseByGuildId(uint32_t guildId) { for(HouseMap::iterator it = houseMap.begin(); it != houseMap.end(); ++it) { if(it->second->isGuild() && it->second->getOwner() == guildId) return it->second; } return NULL; } uint32_t Houses::getHousesCount(uint32_t accId) { Account account = IOLoginData::getInstance()->loadAccount(accId); uint32_t guid, count = 0; for(Characters::iterator it = account.charList.begin(); it != account.charList.end(); ++it) { #ifndef __LOGIN_SERVER__ if(IOLoginData::getInstance()->getGuidByName(guid, (*it)) && getHouseByPlayerId(guid)) #else if(IOLoginData::getInstance()->getGuidByName(guid, (std::string&)it->first) && getHouseByPlayerId(guid)) #endif count++; } return count; } Após recompile e o server não vai mais possuir este bug xD, feliz ano novo a todos!
  7. Gostei
    Enenra deu reputação a Apache em Um ''inteligentao'' derrubando a maquina da google cloud   
    aconselho pegar direto na ovh pois a maioria das empresas que vc vai encontrar revendem deles
  8. Gostei
    Enenra deu reputação a 139 em Spell dando index a nil value   
    O Dr. Frankestein ficaria orgulhoso desse código.
    Pelo que eu entendi deveria ser isso:

     


     
  9. Curtir
    Enenra deu reputação a vine96 em Classic-Yurots [8.60] - Push Cruzado + Cast com Setas   
    Galera seguinte, eu na época em 2019 que foi lançado este servidor Classic Yurots estava terminando a minha faculdade (ou seja TCC, projetos, apresentações e afins) e trabalhando na área, então era praticamente impossível acompanhar a comunidade de otservers.
     
    Eu mexo com servidores de tibia desde os meus 13 anos, tenho 24 atualmente, a paixão por jogar o game não me é mais suficiente, sou programador web e desktop e a paixão por administrar um servidor de sucesso sempre bateu a porta, mas agora ela falou mais alto do que nunca. Eu possuo bastante experiência em todos os ramos de ter um servidor de tibia, seja hospedagem, alteração de sprites, clientes, códigos, sites e afins.
     
    Meu primeiro server foi um X-DREAM WAR 8.40, CARAAAA quem lembra daquela epóca sabe como era muito bom estes servidores. Nunca tive sucesso com eles, mas igual eu gostava muito... heheh
     
    Em 2018 abri um global full custom com sprites editadas, ele fez um bom sucesso na época, pois como tenho habilidades com sprites, eu atualizava o conteúdo da cipsoft antes de todos os outros servers kkk lançava o update da cip e no mesmo dia eu já colocava no client e os arquivos no server. Esse server me dava muuuuito trabalho pois o mapa era global full 11.57, então era gigantesco para 1 pessoa só cuidar, tinha muita coisa com bugs e para ajustar, ainda estava na faculdade e trabalhando ao mesmo tempo, então não tinha como conciliar isto, acabei até tendo que repetir uma cadeira por isso KKKK.
     
    Mesmo assim vocês sabem que o dedicado para um server global geralmente tem que ser mais robusto por conta do map e diversos arquivos, funções e etc. Na época era 150 reais o dedicado mais barato para aguentar um servidor global deste nível, coloquei do meu bolso mesmo, e em 5 dias +/- já tive esse dinheiro de volta com doações (a galera doava de mais nesse server), uma vez recebi uma doação de 150 libras esterlinas neste server, lembro que pirei demais, era um cara australiano kkkk. Foi o meu primeiro server que realmente deu "lucro" digamos assim, mesmo que esse não fosse o objetivo.
     
    Continuando, o server conseguiu se manter online, totalmente por doações por 6 meses sem eu investir nada, acabou que eu não tinha tempo para cuidar dele, tinham muito bugs propositais que os caras colocavam na base justamente para tirar vantagem e subornar os OT admins depois, sofri muito com isso. Usava a base OTX3 do malucooo a mais atualizada na época. Quem for montar um servidor global full dos mais atuais peço que cuidem muito com isso, quem distribui a base FREE coloca diversas condições nas sources, arquivos e database do server que derrubam ele facilmente, entre outras coisas para tirar vantagem, realmente acontece.
     
    Bom seguindo, na metade de 2020 eu me formei como Analista de Sistemas e no finalzinho do ano fui demitido da empresa que eu estava, pois eu trabalhava como consultor de implantação, implantava os sistemas legados da empresa nos clientes e tinham muitas viagens a diversas cidades, a cada semana a gente ia em uma, era bem bacana, por causa da pandemia isso parou, então foram cortados vários servidores, eu fui um deles kkk. 
     
    Perante a isso eu pensei: cara essa é a hora perfeita para eu investir em um ou vários servidores de tibia, não ter que ficar dependendo de empresa contratante e ser um empregado. Estou com o tempo 100% livre, o meu PC é totalmente montado para programação e performance, só falta minha dedicação.
     
    Decidi então me dedicar aos servidores custom/baiaks e derivados, pois tenho mais controle de tudo, mapas, scripts, clients e afins e os bugs das versões e distros 8.60 já são bem conhecidos, e portanto não fica um trabalho tão maçante como em um global.
     
    Já abri um servidor que se encontra online atualmente é o http://baiakgaming.online/ que utilizei a base do BAIAK PVP, aqui doTK mesmo e fiz algumas alterações nas sources, scripts em geral e constatei que ele é bem estável, nunca caiu, nesses quase 3 meses.
     
    Ele fez um "relativo" sucesso, mas o mais importante disso tudo que o dedicado mais barato que aguenta manter ele online com bastante players, custa na faixa de R$70 + R$20 de proteção DDOS 500gb(sim isto foi necessário pois recebi 40gb de ataques a alguns dias), totalizando: R$90 por mês, este valor que eu investi, em menos de 1 mês recebi doações que conseguem manter o servidor online por mais 6 meses. Agora fechando os seus quase 3 meses de server online, ele consegue se manter online por aproximadamente mais 3 anos, somente com estas doações que já foram obtidas.
     
    Bom, porque eu escrevi esse textão de facebook? Então, de qualquer forma estava olhando este projeto do Classic Yurots e me interessei muito, vi todos os videos, imagens, sistemas e show offs até o lançamento. E pensei o seguinte: vou abrir este servidor heheh, vou levar este projeto adiante, mas com modificações minhas claro heheh.
     
    Acho que o grande erro do @KOLISAO para o servidor não ter feito sucesso, foi não ter investido em uma campanha de marketing para o lançamento (não sei se ele realmente fez isso), mas notei este ponto, isso é muito importante para chamar jogadores e abrir o server com uma grande quantidade e assim aumentar cada vez mais o número de players, mais jogadores chamam mais jogadores. 
     
    Outra coisa foi o fato de ele ter investido em um dedicado caríssimo, 75 euros, convertendo hoje da quase R$500 reais por mês, teria que ter um nível de doações muito alto para manter isso kkk, esta configuração é muito acima do necessário para rodar este servidor com perfeição.
     
    Mais um ponto é o fato de que ele teria que ter lançado bem antes do previsto este server, e com o feedback dos jogadores ir mudando e atualizando o mesmo, dia a dia. Pois nós como OT admins não temos a visão de jogador, é muito diferente a percepção para a do player. As vezes você pensa que lançou uma feature muito loca no game e é algo super top para você, mas para os jogadores talvez não faça nenhuma diferença e nunca utilizem tal feature, isso de certa forma vai frustrando para quem administra, por isso a ideia de lançar antes e ir verificando o feedback da galera.
     
    Veja bem não estou criticando o membro do fórum @KOLISAO ele fez um excelente trabalho com este datapack, merece todos os aplausos possíveis, o mapa está impecável, as quests com muito RPG, alterações no site que deixam ele muito bonito e com várias features, e também tem toda uma história por trás de cada coisa. É um trabalho digno de um profissional de mais alto nível.
     
    O datapack do Classic Yurots é bem superior a vários outros disponibilizados por aqui no fórum e realmente merece uma devida homenagem a quem desenvolveu isto.
     
    Pois bem chega de papo, montei todo este texto para no final dizer que o meu real objetivo é seguir este projeto adiante, acho que vale a pena investir e tem todo o potencial possível, na verdade tornar um servidor online em si, vou abrir este server e realizar o possível para ele fazer sucesso. Em 30 dias ou menos vai estar online.
     
    Principais mudanças que vou realizar já logo de cara: clients próprios, como: stand alone, OTC e mobile, todos com launcher, proxy, auto updater, mc e sprites únicas.
     
    O client stand alone suportando todos os bots do 8.60, e o OTC e mobile com bots incluídos no client.
     
    Pensei em utilizar as sprites do client 8.60 original, mas elas são muito limitadas a quantidade de opções, é difícil montar um set donate para cada vocação por exemplo, teria que ficar escolhendo itens a dedo e/ou tirando ele de drop de monstros, o que não é muito legal. Por isso vou utilizar de sprites editadas.
     
    Principalmente para aumentar a gama de opções de itens adquiríveis dentro e fora do game, bem como os sprites dos sistemas mais atuais: exercise trainer com os dummys e armas de treino e estatuas de treino, também nas versões mais atuais. Itens de house, armas destruction e tudo mais.
     
    Faço aqui então um convite para quem queira participar deste projeto junto comigo, sendo mapper, scripter C++, lua, web. Spriter, divulgador e afins.
     
    Tendo ajuda ou não vou abrir este servidor, vou também estar ajudando qualquer pessoa que tiver problemas para estar colocando ele online, dentro do meu alcance claro. 
     
    Obrigado para quem leu até aqui +)
     
     
     
     
     
     
     
     
     
  10. Curtir
    Enenra recebeu reputação de Toruk em (Resolvido)Experience Scroll e Caixa com recompensa radomica   
    De primeira meu mano kkk
    Muito obrigado msm ! me ajudou nos dois scripts.
  11. Obrigado
    Enenra deu reputação a Toruk em (Resolvido)Experience Scroll e Caixa com recompensa radomica   
    Tente isso:
     
    function onUse(cid, item, fromPos, itemEx, toPos) local exp = 500000 doPlayerAddExp(cid, exp) doSendPlayerTextMessage(cid, 22, "You have gained ".. exp .." experience points.") doRemoveItem(item.uid) return false end  
  12. Obrigado
    Enenra deu reputação a Toruk em (Resolvido)Experience Scroll e Caixa com recompensa radomica   
    Cara, da pra vc apenas mudar o valor do "local r1 = math.random(1,4)" Pois o math.random é usado para dar um número aleatório no intervalo pedido.
     
    Por exemplo, se vc deseja que a chance seja 30%, 50% e 20%, respectivamente.
     
    function onUse(cid, item, frompos, item2, topos) r1 = math.random(1,100) if getPlayerLevel(cid) >= 60 then if doPlayerRemoveItem(cid,6497,1) == TRUE then if r1 <= 30 then doPlayerSendTextMessage(cid,22,"Você ganhou uma Magic Plate Armor.") doPlayerAddItem(cid,2472,1) elseif r1 > 30 and r1 <= 80 then doPlayerSendTextMessage(cid,22,"Você ganhou um Golden Helmet.") doPlayerAddItem(cid,2471,1) elseif r1 > 80 then doPlayerSendTextMessage(cid,22,"Você ganhou uma Golden Armor.") doPlayerAddItem(cid,2466,1) end end end end  
  13. Gostei
    Enenra recebeu reputação de Cat em (Resolvido)Training Spell - Spell usada X vezes vira outra spell   
    Nossa irmão ficou perfeito !!
    Eu achei o script bem intuitivo, ainda consigo mudar as coisas do jeito que eu quero.
    Muito obrigado
  14. Curtir
    Enenra deu reputação a Fabi Marzan em (Resolvido)Training Spell - Spell usada X vezes vira outra spell   
    algo assim?
     
    local config = { effects_per_level = { [1] = 25, -- efeito no [level 1] [2] = 177 -- efeito no [level 2] }, casts_to_up = 300, -- quantas vezes tem que soltar para alcançar o level 2 storages = {level = 46890, cast = 46891} -- só modifique se necessário } local function getPlayerRasenganLevel(cid) return getPlayerStorageValue(cid, config.storages.level) end local function getPlayerRasenganCasts(cid) return getPlayerStorageValue(cid, config.storages.cast) > 0 and getPlayerStorageValue(cid, config.storages.cast) or 0 end local function doPlayerRasenganUp(cid, round) return setPlayerStorageValue(cid, config.storages.cast, getPlayerRasenganCasts(cid) + round) end local combat = {} for i = 1, 2 do combat[i] = createCombatObject() setCombatParam(combat[i] , COMBAT_PARAM_TYPE, COMBAT_PHYSICALDAMAGE) setCombatParam(combat[i] , COMBAT_PARAM_EFFECT, config.effects_per_level[i]) function onGetFormulaValues(cid, level, maglevel) min = -(level * 0.28 + maglevel * 1.48) * 1.0 * getPlayerRasenganLevel(cid) max = -(level * 0.34 + maglevel * 2.34) * 1.0 * getPlayerRasenganLevel(cid) return min, max end setCombatCallback(combat[i], CALLBACK_PARAM_LEVELMAGICVALUE, "onGetFormulaValues") end function onCastSpell(cid, var) if getPlayerRasenganLevel(cid) == -1 then setPlayerStorageValue(cid, config.storages.level, 1) end if getPlayerRasenganLevel(cid) ~= 2 then doPlayerRasenganUp(cid, 1) local casts, str = getPlayerRasenganCasts(cid), '' if casts == 1 then str = "First time casting Rasengan! You need "..config.casts_to_up.." casts to advance to level 2 and improve its power!" elseif casts == (config.casts_to_up / 2) then str = "You reached 50% on Rasengan level 1. Now you need "..(config.casts_to_up/2).. " casts to advance to level 2." elseif casts == config.casts_to_up then str = "Congratulations! You advanced the Rasengan jutsu from level 1 to level 2." setPlayerStorageValue(cid, config.storages.level, 2) end if str ~= '' then doPlayerSendTextMessage(cid, MESSAGE_EVENT_ADVANCE, str) end end return doCombat(cid, combat[getPlayerRasenganLevel(cid)], var) end  

Informação Importante

Confirmação de Termo