• nome: Sistema de Ginásio / GYM para pokemon.
• autor: brun123 (mastercraft)
• versão testada: TFS 0.36 pl 1 (8.54)
Acredito que todos saibam do que estou falando, e como é um script de certo modo "avançado" e muito procurado, decidi fazer um. Enfim: Em servidores de pokemon, líderes de ginásios são NPCs em que os players possam lutar no estilo 1 contra 1, com máximo de 6 pokemons. No sistema, não há limite de pokemons, ou seja, fazer um NPC com 200 pokemons é possível, mas saiba que é trabalhoso e demorado.
Detalhes: Como cada servidor de pokemon é bem diferente, acredito que muitos erros vão aparecer devido a diferença de IDs das pokebolas por exemplo, mas vou ajudar no que posso.
Primeiro passo: crie um arquivo com o nome gymlib.lua na pasta data/lib/, e coloque isso dentro do arquivo:
funcpokemon = {2220, 2222} -- ID das pokebolas (o pokemon tem que estar vivo nessas IDs) bpslot = CONST_SLOT_BACKPACK --em outros servers, pode ser que seja CONST_SLOT_AMMO o lugar onde fica a backpack no inventory ginasios = { ["Brock"] = { storage = 990, msgdefeat = "You lost! You aren't strong enough yet, don't come back until you get stronger!", msgafk = "Go away if you don't have any pokemons!", msgwin = "Congratulations, you were strong enough to win this battle fairly! Take this Earth Badge as reward.", [1] = { msggo = "Lets fight then! I choose you, Geodude!", msgba = "That's enough, Geodude!", pokem = "Brock Geodude", nextp = "Brock Graveler"}, [2] = { msggo = "It's your turn, Graveler!", msgba = "Come back, Graveler!", pokem = "Brock Graveler", nextp = "Brock Golem"}, [3] = { msggo = "Crush'em, Golem!", msgba = "You did well, Golem!", pokem = "Brock Golem", nextp = "finish"} } } function noPokeAtAll(cid, gympoke, npcname, msgafk) if not isCreature(cid) then return false end if not isCreature(gympoke) then return false end if #getCreatureSummons(cid) == 0 and getPlayerStorageValue(cid, 991) == 0 and getPlayerStorageValue(cid, ginasios[npcname].storage) ~= 2 then setPlayerStorageValue(cid, 991, -1) doCreatureSay(getCreatureByName(npcname), msgafk, 1) doSendMagicEffect(getThingPos(gympoke), 10) setPlayerStorageValue(cid, ginasios[npcname].storage, 0) doRemoveCreature(gympoke) return true end end function gymChecker(cid, duel, nb, npcname) if not isCreature(cid) then return true end if not isCreature(duel) then doSendMagicEffect(getThingPos(cid), 10) doRemoveCreature(cid) return true end if getDistanceBetween(getThingPos(getCreatureByName(npcname)), getThingPos(duel)) >= 8 then doSendMagicEffect(getThingPos(cid), 10) doRemoveCreature(cid) setPlayerStorageValue(duel, ginasios[npcname].storage, 0) return true end if isCreature(duel) and getPlayerStorageValue(duel, 991) == 0 then addEvent(noPokeAtAll, 6000, duel, cid, npcname, ginasios[npcname].msgafk) end if isCreature(duel) and getPlayerStorageValue(duel, 991) == -1 then doSendMagicEffect(getThingPos(cid), 10) doRemoveCreature(cid) setPlayerStorageValue(duel, ginasios[npcname].storage, 0) doCreatureSay(getCreatureByName(npcname), ginasios[npcname].msgdefeat, 1) return true end if isCreature(duel) and not hasPokemon(duel) then setPlayerStorageValue(duel, 991, -1) end if not isCreature(getCreatureTarget(cid)) then if nb == 0 then addEvent(gymChecker, 200, cid, duel, 1, npcname) else doSendMagicEffect(getThingPos(cid), 10) doRemoveCreature(cid) if isCreature(duel) then setPlayerStorageValue(duel, ginasios[npcname].storage, 0) end end return true end addEvent(gymChecker, 1000, cid, duel, 0, npcname) end function hasPokemon(cid) if #getCreatureSummons(cid) >= 1 then return true end if isInArray(funcpokemon, getPlayerSlotItem(cid, CONST_SLOT_FEET).itemid) then return true end local bp = getPlayerSlotItem(cid, bpslot) for cc = 1, #funcpokemon do if #getItemsInContainerById(bp.uid, funcpokemon[cc]) >= 1 then return true end end return false end function doGymBattle(npcname, gympoke, cid, turn) doCreatureSay(getCreatureByName(npcname), ginasios[npcname][turn].msggo, 1) local x = doSummonCreature(gympoke, getThingPos(getCreatureByName(npcname))) registerCreatureEvent(x, "Gym1") registerCreatureEvent(x, "Gym2") registerCreatureEvent(x, "Gym3") registerCreatureEvent(x, "Gym4") setPlayerStorageValue(x, 201, ".") setPlayerStorageValue(x, 201, npcname) setPlayerStorageValue(x, 202, turn) doSendMagicEffect(getThingPos(x), 10) setPlayerStorageValue(x, ginasios[npcname].storage, 1) addEvent(gymChecker, 1000, x, cid, 0, npcname) end --ex: doGymBattle("Brock", "Brock Geodude", cid, 1)
Segundo passo: crie um arquivo chamado gym.lua na pasta data/creaturescripts/scripts/ e coloque isso dentro:
function onDeath(cid, corpse) doSendMagicEffect(getThingPos(cid), 10) local npc = ""..getPlayerStorageValue(cid, 201).."" local turn = getPlayerStorageValue(cid, 202) local nturn = ((turn) + 1) local nxt = ginasios[npc][turn].nextp doCreatureSay(getCreatureByName(npc), ginasios[npc][turn].msgba, 1) if nxt == "finish" then local killer = getItemAttribute(corpse.uid, "corpseowner") addEvent(doCreatureSay, 1200, getCreatureByName(npc), ginasios[npc].msgwin, 1) setPlayerStorageValue(getCreatureMaster(killer), ginasios[npc].storage, 2) --local item = getPlayerItemById(killer, true, 2307) --doTransformItem(item.uid, 2294) doRemoveCreature(cid) return false end local function summonNext(poke, pos, gym, msg, cid) local x = doSummonCreature(poke, pos) addEvent(gymChecker, 1000, x, cid, 0) doSendMagicEffect(getThingPos(x), 10) doCreatureSay(getCreatureByName(gym), msg, 1) end local killer = getItemAttribute(corpse.uid, "corpseowner") addEvent(doGymBattle, 1200, npc, ginasios[npc][turn].nextp, killer, nturn) doRemoveCreature(cid) return false end local fightconditionnn = createConditionObject(CONDITION_INFIGHT) setConditionParam(fightconditionnn, CONDITION_PARAM_TICKS, 18 * 1000) function onAttack(cid, target) if getPlayerStorageValue(getCreatureMaster(target), ginasios[getPlayerStorageValue(cid, 201)].storage) ~= 1 then doMonsterChangeTarget(cid) doChangeSpeed(cid, -getCreatureSpeed(cid)) return true end if isPlayer(target) then if #getCreatureSummons(target) == 0 then doMonsterChangeTarget(cid) doChangeSpeed(cid, -getCreatureSpeed(cid)) return true end if #getCreatureSummons(target) >= 1 then doMonsterSetTarget(cid, getCreatureSummons(target)[1]) end end if getCreatureSpeed(cid) == 0 then doChangeSpeed(cid, -getCreatureSpeed(cid)) doChangeSpeed(cid, 200) end doAddCondition(getCreatureMaster(target), fightconditionnn) return true end function onCast(cid, target) if isPlayer(target) then return false end local targete = getCreatureTarget(cid) if isPlayer(getCreatureMaster(targete)) and getPlayerStorageValue(getCreatureMaster(targete), ginasios[getPlayerStorageValue(cid, 201)].storage) ~= 1 then return false end return true end function onDirection(cid, old, current) if isCreature(getCreatureTarget(cid)) and not isPlayer(getCreatureTarget(cid)) and getPlayerStorageValue(getCreatureMaster(getCreatureTarget(cid)), ginasios[getPlayerStorageValue(cid, 201)].storage) == 1 then return true end return false end
Terceiro passo: abra o arquivo creaturescripts.xml (localizado em data/creaturescripts) e adicione essas 4 tags:
<event type="cast" name="Gym1" event="script" value="gym.lua"/> <event type="attack" name="Gym2" event="script" value="gym.lua"/> <event type="direction" name="Gym3" event="script" value="gym.lua"/> <event type="death" name="Gym4" event="script" value="gym.lua"/>
Quarto passo: crie um arquivo chamado brock.xml em data/npc e crie um chamado brock.lua em data/npc/scripts e coloque isso dentro:
local focus = 0 local talk_start = 0 local target = 0 local following = false local attacking = false local talkState = {} function onThingMove(creature, thing, oldpos, oldstackpos) end function onCreatureAppear(creature) end function onCreatureDisappear(cid, pos) if focus == cid then selfSay('Good bye sir!') focus = 0 talk_start = 0 end end function onCreatureTurn(creature) end function msgcontains(txt, str) return (string.find(txt, str) and not string.find(txt, '(%w+)' .. str) and not string.find(txt, str .. '(%w+)')) end function onCreatureSay(cid, type, msg) local msg = string.lower(msg) local talkUser = NPCHANDLER_CONVBEHAVIOR == CONVERSATION_DEFAULT and 0 or cid if getPlayerStorageValue(cid, ginasios["Brock"].storage) >= 1 then return true end if (msgcontains(msg, 'hi') and (focus == 0) and (focus ~= cid) and (getDistanceToCreature(cid) <= 4)) then focus = cid talkState[talkUser] = 1 selfSay("Welcome to the gym of Pewter City. My name is Brock and I am the leader of this gym. Would you like to battle me?") elseif (msgcontains(msg, "no") or msgcontains(msg, "bye")) and focus == cid and talkState[talkUser] ~= 3 then selfSay("Bye then, tell strong friends to come here and try to beat me!") focus = 0 elseif (msgcontains(msg, "yes") or msgcontains(msg, "battle")) and focus == cid and talkState[talkUser] == 1 then if not hasPokemon(cid) then selfSay("You don't have any pokemon to battle me!") focus = 0 return true end talkState[talkUser] = 3 doGymBattle("Brock", "Brock Geodude", cid, 1) setPlayerStorageValue(cid, ginasios["Brock"].storage, 1) setPlayerStorageValue(cid, 991, 0) local function focusCheck(cid) if not isCreature(cid) then focus = 0 return true end if getPlayerStorageValue(cid, 991) == -1 then focus = 0 return true end addEvent(focusCheck, 500, cid) end addEvent(focusCheck, 1000, cid) end end function onThink() if focus ~= 0 then a, b, c = selfGetPosition() if c ~= getThingPos(focus).z then focus = 0 end end if focus ~= 0 then if getDistanceToCreature(focus) > 6 then focus = 0 end end return true end
Quinto passo: se você usar o pokemon dash, vá no arquivo playerattack.lua (data/creatureevents/scripts) e adicione esses códigos embaixo da linha "function onAttack(cid, target)":
if getPlayerStorageValue(target, 201) ~= -1 then for a, b in pairs(ginasios) do if getPlayerStorageValue(target, ginasios[getPlayerStorageValue(target, 201)].storage) == 1 then if getPlayerStorageValue(cid, ginasios[getPlayerStorageValue(target, 201)].storage) ~= 1 then doPlayerSendCancel(cid, "You can't attack this pokemon.") return false end end end end end
Se você não usa o pokemon dash, crie um arquivo chamado playerattack.lua na pasta data/creatureevents/scripts e coloque isso dentro:
function onAttack(cid, target) if getPlayerStorageValue(target, 201) ~= -1 then for a, b in pairs(ginasios) do if getPlayerStorageValue(target, ginasios[getPlayerStorageValue(target, 201)].storage) == 1 then if getPlayerStorageValue(cid, ginasios[getPlayerStorageValue(target, 201)].storage) ~= 1 then doPlayerSendCancel(cid, "You can't attack this pokemon.") return false end end end end return true end
e adicione essa tag no creaturescripts.xml (data/creaturescripts):
<event type="attack" name="PlayerAttack" event="script" value="playerattack.lua"/>
e ainda, abra o arquivo login.lua e embaixo dessa linha: "function onLogin(cid)" adicione esse código:
registerCreatureEvent(cid, "PlayerAttack")
Último passo: vá em data/creaturescripts/scripts e abra o arquivo goback.lua (aqui por exemplo, pode ser que haja uma particulariedade de servidor para servidor), e procure pela linha:
function onDeath(cid, deathList)
e abaixo dela, adicione esse código:
local owner = getCreatureMaster(cid) for x, y in pairs(ginasios) do if getPlayerStorageValue(owner, y.storage) == 1 then if getPlayerStorageValue(owner, 991) == 5 or not hasPokemon(owner) then setPlayerStorageValue(owner, 991, -1) else local number = tonumber(getPlayerStorageValue(owner, 991)) setPlayerStorageValue(owner, 991, number + 1) end end end
Configuração dos NPCs:
Abrindo o arquivo brock.lua na pasta data/npc/scripts, procure por esta linha:
"doGymBattle("Brock", "Brock Geodude", cid, 1)"
o "Brock" precisa ser o nome do NPC e o "Brock Geodude" precisa ser o nome do primeiro pokemon a ser usado.
Agora nessa parte, no script do seu NPC, você também deve editar o nome do NPC no lugar de "brock".
Se quiser que o player possa lutar com o NPC mesmo depois de tê-lo vencido, é só remover a mesma parte:
if getPlayerStorageValue(cid, ginasios["Brock"].storage) >= 1 then return true end
Ao abrir o arquivo gymlib.lua, você encontra essas duas linhas (são as primeiras):
funcpokemon = {2220, 2222} -- ID das pokebolas (o pokemon tem que estar vivo nessas IDs) bpslot = CONST_SLOT_BACKPACK --em outros servers, pode ser que seja CONST_SLOT_AMMO o lugar onde fica a backpack no inventory
Onde está funcpokemon, você coloca o ID de todas as pokebolas do seu server, mas esse ID tem que ser o ID da pokebola com algum pokemon vivo dentro, e esses IDs devem ser separados por vírgula.
Onde está bpslot, você escreve onde fica a BACKPACK do seu server de pokemon. Se ficar no lugar normal de BPs, deixe como está, mas se sua BP fica no slot da munição (ammunition), você deve estar para CONST_SLOT_AMMO. Isso é tudo, agora vocês podem configurar os IDs e localização da BP facilmente, sem precisar conhecimentos mais avançados sobre scripting, e evitar erros no sistema.
Abrindo o arquivo gymlib.lua, localizado em data/lib/ você encontra essa tabela:
ginasios = { ["Brock"] = { storage = 900, msgdefeat = "You lost! You aren't strong enough yet, don't come back until you get stronger!", msgafk = "Go away if you don't have any pokemons!", msgwin = "Congratulations, you were strong enough to win this battle fairly! Take this Earth Badge as reward.", [1] = { msggo = "Lets fight then! I choose you, Geodude!", msgba = "That's enough, Geodude!", pokem = "Brock Geodude", nextp = "Brock Graveler"}, [2] = { msggo = "It's your turn, Graveler!", msgba = "Come back, Graveler!", pokem = "Brock Graveler", nextp = "Brock Golem"}, [3] = { msggo = "Crush'em, Golem!", msgba = "You did well, Golem!", pokem = "Brock Golem", nextp = "finish"} } }
• storage significa a única storage que deve variar de NPC para NPC, tenha certeza de que essa storage não está sendo usada, é você não deve usar a storage 901 aqui, pois a mesma já está em uso pelo próprio sistema.
• msgdefeat significa o que o NPC irá dizer quando o player perder o duelo.
• msgafk significa o que o NPC irá dizer caso o player comece o duelo, mas não faça nada depois.
• msgwin significa o que o NPC irá dizer quando o player vencer o duelo.
• msggo / msgba significam os textos que o NPC irá dizer ao sumonar o pokemon dele, ou quando o mesmo morrer, respectivamente.• pokem / nextp significam o nome do pokemon que será utilizado, e o próximo pokemon a ser sumonado pelo NPC, respectivamente. Caso o nextp seja especificado como "finish", não haverá um próximo pokemon.
• o número entre [] significa a ordem utilizada pelo líder para usar o tal pokemon, sendo 1 o primeiro pokemon, 2 o segundo e assim vai...
Uma tabela detalhada sobre a configuração:
ginasios = { ["nome do npc"] = { storage = xxx (algum número para a storage), msgdefeat = "texto quando o player perder", msgafk = "texto caso o player fique afk", msgwin = "texto caso o player ganhe", [1] = { msggo = "texto quando o npc sumonar o primeiro pokemon", msgba = "texto quando o primeiro pokemon morrer", pokem = "nome do primeiro pokemon a ser sumonado", nextp = "nome do próximo pokemon a ser sumonado"}, [2] = { msggo = "texto quando o npc sumonar o segundo pokemon", msgba = "texto quando o segundo pokemon morrer", pokem = "nome do segundo pokemon a ser sumonado", nextp = "nome do próximo pokemon a ser sumonado"}, [3] = { msggo = "texto quando o npc sumonar o terceiro pokemon", msgba = "texto quando o terceiro pokemon morrer", pokem = "nome do terceiro pokemon a ser sumonado", nextp = "nome do próximo pokemon a ser sumonado"}, [4] = { msggo = "texto quando o npc sumonar o quarto pokemon", msgba = "texto quando o quarto pokemon morrer", pokem = "nome do quarto pokemon a ser sumonado", nextp = "nome do próximo pokemon a ser sumonado"}, [5] = { msggo = "texto quando o npc sumonar o quinto pokemon", msgba = "texto quando o quinto pokemon morrer", pokem = "nome do quinto pokemon a ser sumonado", nextp = "nome do próximo pokemon a ser sumonado"}, [6] = { msggo = "texto quando o npc sumonar o sexto pokemon", msgba = "texto quando o sexto pokemon morrer", pokem = "nome do sexto pokemon a ser sumonado", nextp = "nome do próximo pokemon a ser sumonado (finish, caso queria que só hajam 6"}, }, ["nome de outro npc"] = { storage = xxx (algum número para a storage), msgdefeat = "texto quando o player perder", msgafk = "texto caso o player fique afk", msgwin = "texto caso o player ganhe", [1] = { msggo = "texto quando o npc sumonar o primeiro pokemon", msgba = "texto quando o primeiro pokemon morrer", pokem = "nome do primeiro pokemon a ser sumonado", nextp = "nome do próximo pokemon a ser sumonado"}, [2] = { msggo = "texto quando o npc sumonar o segundo pokemon", msgba = "texto quando o segundo pokemon morrer", pokem = "nome do segundo pokemon a ser sumonado", nextp = "nome do próximo pokemon a ser sumonado"}, [3] = { msggo = "texto quando o npc sumonar o terceiro pokemon", msgba = "texto quando o terceiro pokemon morrer", pokem = "nome do terceiro pokemon a ser sumonado", nextp = "nome do próximo pokemon a ser sumonado"}, [4] = { msggo = "texto quando o npc sumonar o quarto pokemon", msgba = "texto quando o quarto pokemon morrer", pokem = "nome do quarto pokemon a ser sumonado", nextp = "nome do próximo pokemon a ser sumonado"}, [5] = { msggo = "texto quando o npc sumonar o quinto pokemon", msgba = "texto quando o quinto pokemon morrer", pokem = "nome do quinto pokemon a ser sumonado", nextp = "nome do próximo pokemon a ser sumonado"}, [6] = { msggo = "texto quando o npc sumonar o sexto pokemon", msgba = "texto quando o sexto pokemon morrer", pokem = "nome do sexto pokemon a ser sumonado", nextp = "nome do próximo pokemon a ser sumonado"}, } }
Importante: caso for testar o sistema como foi postado aqui, é necessário criar antes os monstros Brock Geodude, Brock Graveler e Brock Golem, assim como adicioná-los no monsters.xml.
Recomendações: é bom você impedir que o pokemon consiga retornar para a pokebola durante duelos (assim como foi feito no SvkE), para isso, basta adicionar esse código no arquivo goback.lua (data/actions) na parte inicial de retornar o pokemon:
for a, b in pairs(ginasios) do if getPlayerStorageValue(cid, b.storage) == 1 then -- GYM doPlayerSendCancel(cid, "You can't return your pokemon during gym battles.") return true end end
Aconselho fazer o mesmo caso seu server tenha script de revive.
Deve-se também checar pelos storages usado em players, somente o 990 e o 991.
No servidor testado, é utilizado apenas pokeballs e ultraballs, com IDs 2220 e 2222 (outra particularidade).
Após ter instalado os arquivos, entre no jogo e use o comando /n Brock para testar o NPC.
Último aviso: o prêmio ainda não foi adicionado (que seria a badge), pois esse sim é bem variado de servidor pra servidor.
Já que nenhum servidor base já vem com os IDs das badges, cada um adicionou elas de modo diferente, por isso não pude fazer, mas existe uma parte sobre um prêmio no arquivo gym.lua (data/creaturescripts/scripts), procure por isso:
--local item = getPlayerItemById(killer, true, 2307) --doTransformItem(item.uid, 2294)
Você pode modificar para adicionar dinheiro ou outra coisa, e pode também desenvolver um sistema de badges.
Eu não vou fazer badge aqui, mas se vocês tentarem, posso ajudar com problemas.
Quaisquer erros, tentarei responder o mais rápido possível.