Ir para conteúdo
  • Cadastre-se

L2JPHP: "One library to rule them all"


Grundor
Message added by Grundor

Este é um trabalho em progresso (WIP: Work in progress).

Isso significa que esse projeto não está pronto para uso ou implementação.

Posts recomendados

  • L2JBr ADM

http://i.imgur.com/bhBwp7U.jpg

 

Olá Papangos e Papangas desse meu Brasil baronil!

 

Vos trago não um servidor de L2 baseado em PHP,  não um site completo cheio de fruru, mas uma biblioteca que vai revolucionar o desenvolvimento web para L2J.

 

Introdução


Muitos projetos paralelos foram desenvolvidos no decorrer dos anos, e o L2J hoje está ramificado em diversas distribuições, cada uma delas com banco de dados diferentes,  seja por nome de tabela, campos, etc. Nunca se foi estabelecido um padrão para que todos os bancos respeitassem uma nomeclatura específica, por vezes foram desenvolvidos por desenvolvedores amadores, que apesar de habilitosos , nunca estudaram e não conhecem boas práticas ou seguem algum tipo de padrão.   

Isso dificulta o desenvolvimento de aplicações comatíveis com tantas revisões(L2jserver, l2jbrasil, dream, sunrise, frozen, etc)  e versões do jogo diferentes(Interlude, Gracia, Classic, etc). 

O Grande desafio disso tudo é,  Como criar aplicações web, em php, que sejam compatíveis com o maior número de revisões possível?

 A resposta para isso é a Harmonização de Dados.

Quote

A Harmonização de Dados é o conceito de que você pode criar aplicações que interpretam diversas fontes de dados diferentes e que atuam com um único padrão de entrada e saída.

E a melhor forma de fazer isso é trabalhar na camada de Model, que passa para os controlers um único formato conhecido de dados. Parece complexo, mas não é tanto assim ?

 

Como funciona?

Primeiro temos que definir um conjunto de constantes:

<?php

//PROJECT DEFAULTS
define('L2JBR_DIST', "L2JSERVER"); //Qual a distribuição?
define('L2JBR_L2VERSION', "Interlude"); //Qual a versão do jogo?

define('L2JBR_SALT', 'change_it_for_something_else'); //Será usado para métodos de segurança no futuro.



//DATABASE
define('L2JBR_DB_DRIVER', "mysql");
define('L2JBR_DB_HOST', "localhost");
define('L2JBR_DB_PORT', 3306);
define('L2JBR_DB_NAME', "l2jdb");
define('L2JBR_DB_USER', "root");
define('L2JBR_DB_PWD', "");

Ao configurar a aplicação será necessário dizer para qual versão os Models derão utilizados. O modelo padrão é o L2JSERVER, por ser o projeto base para 99% dos outros projetos, e Interlude que é a versão mais difundida o que já aumenta a compatibilidade inicial da biblioteca.

Mas ai tá, vc deve estar se perguntando "tá, mas como tudo acontece?",  veja o exemplo à seguir com o Model "Characters" responsável pela manipulação dos dados de um personagem:

<?php

$CharactersModel = \L2jBrasil\L2JPHP\ModelFactory::build('Players/Characters');

$CharactersModel->get('ID'); //Retorna o usuário pelo ID
$CharactersModel->update('ID', ["name"=> "Grundor"]); //Atualiza o Nome do personagem
$CharactersModel->ban('ID'); //Bane o personagem pelo ID
$CharactersModel->all(['name','level'],false,10,'level'); //Retorna os 10 personagens com maior level.

//Exemplo Avançado:
$CharactersModel->select(['character.id','account.name'])
    ->join(\L2jBrasil\L2JPHP\ModelFactory::build('Players/Account'))
    ->orderby('level')
    ->limit(100)
    ->query()
    ->FetchAll();

 

 

O segredo está nessa classe "ModelFactory". O método build retorna a instância do model requisitado, nesse caso, no namespace Player/Character.  Mas como ele faz isso?

Ele monta dinâmicamente a instância da classe com base na configuração da DIST e da  L2JBR_L2VERSION  e irá lançar uma exceção caso não exista, 

Então no exemplo acima a chamada do "Build" faria o mesmo que:

 

<?php

$CharactersModel = \L2jBrasil\L2JPHP\Models\Dist\Interlude\L2JSERVER\Players\Characters();
//...

Que pode ser usado diretamente também, sem problemas, já que são todas classes idependentes e instanciáveis.
 

Todo model terá as operações de CRUD(Create, Read, Update e Delete na língua Inglesa)  e "listagem(all)". Além disso, os models implementam interfaces, essas interfaces vão garantir que todo model , por exemplo "Characters", de todas as versões, possuam os mesmos métodos mais padrões (ban, move, changeProfession, changeLevel, cleanPK, etc), além disso será permitido de forma dinâmica.

Para que seja possível manter a compatibilidade, está sendo desenvolvido uma classe que irá montar as condições "where"  (segundo parâmetro do método all (listagem) ) para que a conversão dos nomes das colunas também seja aplicado dinâmicamente.

Como posso ajudar?

Há diversas revisões, todas elas precisarão e poderão ser implementadas, você poderá escrever models compatíveis com as revisões para que seja utilizado expandindo a compatibilidade das aplicações desenvolvidas utilizando esse framework. Detalhes para contribuição serão atualizados no futuro próximo.

Informações técnicas

 

 

 

EJoOSOj.gif

Sua pergunta foi respondida? Certifique-se de marcar a resposta como a solução aceita.
Se existe mais de uma resposta, utilize o "vote up" para destacá-la.
Se você achar uma resposta útil, diga obrigado clicando no botão "Gostei".

Link para o comentário
Compartilhar em outros sites


Show!

Esse projeto vai agilizar muito o desenvolvimento e o melhor de tudo, vai padronizar os projetos, a galera vai entender melhor o código por se acostumar com ele, menos dúvidas e mais projetos;

Linda iniciativa!

Link para o comentário
Compartilhar em outros sites

Caraca,

Posso ser novo na área porem peguei o espirito da coisa, isso ajuda demais, há casos como JSunrise, que stats, e items são com xml, já em outras versões são diretamente na DB, acaba que o determinado script php não funciona e o responsável tem que adaptar isso.

Link para o comentário
Compartilhar em outros sites

  • L2JBr ADM
11 minutos atrás, TurtleLess disse:

Caraca,

Posso ser novo na área porem peguei o espirito da coisa, isso ajuda demais, há casos como JSunrise, que stats, e items são com xml, já em outras versões são diretamente na DB, acaba que o determinado script php não funciona e o responsável tem que adaptar isso.

Então, isso ajudaria pois o acesso pode ser dado diretamente via XML ao invés do SQL, aplicando a regra de negócio diretamente no model.

À principio a intenção é implementar o máximo de métodos com base em L2jServer e depois ir implementando os métodos para as outras revs e versões

A idéia é ,  "faça isso" seja "faça isso" em todas as versões, o máximo compatível possível é claro.

 

EJoOSOj.gif

Sua pergunta foi respondida? Certifique-se de marcar a resposta como a solução aceita.
Se existe mais de uma resposta, utilize o "vote up" para destacá-la.
Se você achar uma resposta útil, diga obrigado clicando no botão "Gostei".

Link para o comentário
Compartilhar em outros sites

Ótimo share!

Uma dúvida/sugestão: o ban em personagens (dei uma olhada no código e vi que não foi implementado ainda) poderia ser da mesma forma que o banimento de contas, não? Pq também tem uma columa "access_level" na tabela "characters"... Caso isso não seja eficaz, um simples "delete" recursivo através do ID (remove atalhos, skills, itens, macros etc) poderia resolver também, não?

--

Entrei de férias da faculdade semana passada e agora posso voltar a me envolver com L2J novamente. Vou contribuir com o que puder nesse meio tempo.

Link para o comentário
Compartilhar em outros sites

Eu não testei ainda, mas tenho uma pergunta...

Muitas revisões novas usam o banco de dados para gravar configurações e gameplay (items de usuário e etc) sendo assim todos os items, NPC e demais informações que são comuns a todos estão em arquivos XML, eu fiz um site recentemente aonde eu linkei os arquivos XML do server e usava DOM para puxar as informações transformando em JSON "on-the-fly" mas é trabalhoso e abre brecha de segurança pra exploit.

Esse projeto tem uma API interna pra puxar informação desses arquivos XML? se não, seria uma sugestão para implementar futuramente, se já tem esqueçam ? 

EM BREVE - GALAGARD.COM.BR

SERVIDOR Grand Crusade - 50x

SERVIDOR Classic - 8x

Link para o comentário
Compartilhar em outros sites

  • L2JBr ADM
Em 21/07/2018 at 13:11, Enkel disse:

Ótimo share!

Uma dúvida/sugestão: o ban em personagens (dei uma olhada no código e vi que não foi implementado ainda) poderia ser da mesma forma que o banimento de contas, não? Pq também tem uma columa "access_level" na tabela "characters"... Caso isso não seja eficaz, um simples "delete" recursivo através do ID (remove atalhos, skills, itens, macros etc) poderia resolver também, não?

--

Entrei de férias da faculdade semana passada e agora posso voltar a me envolver com L2J novamente. Vou contribuir com o que puder nesse meio tempo.

Na verdade estou só divulgando o começo do projeto, ainda não está em release, é um WIP(work-in-progress), a demonstração do código do model Character é apenas uma POC (Proof of Concept), a ideia é que exista um método ban, o que o método ban faz para cada revisão é customisável. O importante nesse caso é que o ban seja executado da mesma forma, passando o mesmo parâmetro. Quando não houver a possibilidade se implementar com apenas um parametro um array será usado e cada model decidirá a regra de negócio a ser implementad.

Seria ótimo ter ajuda, principalmente , no primeiro roadmap (não publicado) que pretende cobrir a gestão total de contas, personagens e items. Inclua dentro de personagem os ranks padrões de pvp, pk, level, adena, etc.

Como falei o projeto base será o L2jServer interlude, ele será o norte para a implementação de outras versões.

 

Em 21/07/2018 at 14:07, Seifer Almasy® disse:

Eu não testei ainda, mas tenho uma pergunta...

Muitas revisões novas usam o banco de dados para gravar configurações e gameplay (items de usuário e etc) sendo assim todos os items, NPC e demais informações que são comuns a todos estão em arquivos XML, eu fiz um site recentemente aonde eu linkei os arquivos XML do server e usava DOM para puxar as informações transformando em JSON "on-the-fly" mas é trabalhoso e abre brecha de segurança pra exploit.

Esse projeto tem uma API interna pra puxar informação desses arquivos XML? se não, seria uma sugestão para implementar futuramente, se já tem esqueçam ? 

 

Sim, já trabalhei com ótimas classes de leitura e manipulação de XML, ainda não está no roadmap a inclusão de uma versão que use XML, como disse acima é intenção fazer a cobertura de pelo menos accounts, characters e items (inventorio e warehouse).

 

O Objetivo principal é que essa lib seja a base de outros projetos como o https://github.com/L2jBrasil/vote-for-reward que comecei porém parei devido a barreira das revisões. 
O mais importante à se destacar nesse projeto é que o escopo dele é apenas de manipulação de dados no mais baixo nível das aplicações que utilizarem ele garantindo ao desenvolvedor uma API de models que seja suficiente para desenvolver aplicações mais complexas sem se preocupar com essa parte, focando na funcionalidade em sí.

 

Editado por Wallace Carvalho

EJoOSOj.gif

Sua pergunta foi respondida? Certifique-se de marcar a resposta como a solução aceita.
Se existe mais de uma resposta, utilize o "vote up" para destacá-la.
Se você achar uma resposta útil, diga obrigado clicando no botão "Gostei".

Link para o comentário
Compartilhar em outros sites

Uma configuração com os nomes de tableas usados, em teses, não resolveria uma boa parte do problema?

Algo tipo:

$account = 'accounts';

$characters = 'characters';

$items = 'items';

Como a estrutura é bastante comum de uma versão pra outra (ao menos o básico é, o que costuma mudar é access_level pra accessLevel, e sinceramente não sei qual das duas é o padrão atual) uma sessão de configuração do script aonde se colocasse os dados de forma manual, ao meu ver, seria uma solução bem mais prática do ponto de vista técnico.

Ou, existe algum modo de listar as tabelas de um banco de dados e procurar por elas (via PHP) guardando o retorno num array e comparando os possíveis nomes de tabela dos databases das diferentes versões de L2j que existem...? (Se existe como fazer isso desconheço, senão já teria usado nos meus sites)

 

EM BREVE - GALAGARD.COM.BR

SERVIDOR Grand Crusade - 50x

SERVIDOR Classic - 8x

Link para o comentário
Compartilhar em outros sites

  • L2JBr ADM
21 horas atrás, Seifer Almasy® disse:

Uma configuração com os nomes de tableas usados, em teses, não resolveria uma boa parte do problema?

Algo tipo:

$account = 'accounts';

$characters = 'characters';

$items = 'items';

Como a estrutura é bastante comum de uma versão pra outra (ao menos o básico é, o que costuma mudar é access_level pra accessLevel, e sinceramente não sei qual das duas é o padrão atual) uma sessão de configuração do script aonde se colocasse os dados de forma manual, ao meu ver, seria uma solução bem mais prática do ponto de vista técnico.

Ou, existe algum modo de listar as tabelas de um banco de dados e procurar por elas (via PHP) guardando o retorno num array e comparando os possíveis nomes de tabela dos databases das diferentes versões de L2j que existem...? (Se existe como fazer isso desconheço, senão já teria usado nos meus sites)

 

 


2 Problemas nisso:

- O projeto é PHP de alto nível , orientação objeto, blueprints e boas práticas, alinhado com novos paterns e tentando compatibilizar com php 7, já que o php 5 já é considerado deprecated. Usando variáveis para customizar as diferenças vc não está permitindo a criação de múltiplas e diversificadas aplicações, basicamente uma couxa de retálios.

- Compatibilidade limitada, sem possibilidade de colaboração da comunidade, explico:

Fazendo dessa forma, vc  sempre considera que todos os projetos tenham uma única tabela "characters" com colunas que podem variar de a para b, a proposta do projeto é abastrair a  camada de acesso aos dados.

Como prova de conceito eu declarei o método "ban", o que é necessário para fazer o BAN em cada revisão ou versão eu não sei, mas eu padronizei a entrada do método que é o identificador de characters. 

Isso significa que idependente do projeto aplicado, o método ban recebe sempre a mesma entrada e terá também a mesma saida, isso é a garantia da interoperabilidade com multiplas formas.

 

 

Outro exemplo que eu posso aplicar é o Iventorio, imagine que num projeto foi otimizado para que os itens fiquem em tabelas distintas e é necessário um JOIN para regatar os itens do usuário, suponhamos que tem uma tabela para consumíveis, armors, weapons e questitens.  Eu preciso retornar o iventário do usuário, num eu preciso fazer um join, noutro é um select simples.  Só um array de variáveis resolveria? 

A complexidade impediria que algo assim fosse feito.

 

A proposta é que aplicações sejam construidas e que sejam capazes de executar exatamente a mesma coisa idependente da revisão ou da versão com o mínimo de configuração, que no caso do projeto 2 linhas basta:

 

<?php

define('L2JBR_DIST', "L2JSERVER"); //Qual a distribuição?
define('L2JBR_L2VERSION', "Interlude"); //Qual a versão do jogo?

 

 

 

EJoOSOj.gif

Sua pergunta foi respondida? Certifique-se de marcar a resposta como a solução aceita.
Se existe mais de uma resposta, utilize o "vote up" para destacá-la.
Se você achar uma resposta útil, diga obrigado clicando no botão "Gostei".

Link para o comentário
Compartilhar em outros sites

  • 1 year later...
  • L2JBr ADM

Pré-release liberada:

 

https://github.com/L2jBrasil/L2JPHP/releases/tag/v1.0-rc, a instalação recomendada ainda é via composer.

 

Versão inicial com suporte básico à funções de criação de conta e listagens de personagens, mínimo para o website.

Espero que mais projetos pessoas se interessem em transformar essa lib. em algo mais robusto a ponto de se tornar a base do desenvolvimento de todos as distribuições do mercado.

 

 

 

 

EJoOSOj.gif

Sua pergunta foi respondida? Certifique-se de marcar a resposta como a solução aceita.
Se existe mais de uma resposta, utilize o "vote up" para destacá-la.
Se você achar uma resposta útil, diga obrigado clicando no botão "Gostei".

Link para o comentário
Compartilhar em outros sites

  • Grundor pinned e featured this Tópico
  • L2JBr ADM

Adicionei pequeno guia de como implementar outras versões do jogo e distribuições https://github.com/L2jBrasil/L2JPHP/wiki/How-to-create-a-new-distribuition 

EJoOSOj.gif

Sua pergunta foi respondida? Certifique-se de marcar a resposta como a solução aceita.
Se existe mais de uma resposta, utilize o "vote up" para destacá-la.
Se você achar uma resposta útil, diga obrigado clicando no botão "Gostei".

Link para o comentário
Compartilhar em outros sites

Boa tarde @Grundor!

Uma sugestão que tenho para o projeto seria a de deixar alguns datasets disponíveis para uso.
Assim evitaria a necessidade de procurar uma database para começar a analisar e desenvolver de acordo com a versão escolhida.

Também seria de muita ajuda adicionar as referências das Versões e Rev's pela qual já é possível declarar como funcional para o package.

Seria bom pra cada dev que resolver colaborar, elaborar previamente um documento ou um diagrama referenciando a versão, a estrutura de dados utilizada e um diagrama de base se possível. Não sei se atualmente os packs que disponibilizam de servidores estão mudando muito as databases originais, mas fica ai a sugestão.

sign.jpg

Link para o comentário
Compartilhar em outros sites

  • 1 month later...
  • L2JBr ADM

Essas informações vc encontra no repositório do projeto, eu tenho desenvolvido sozinho então o desenvolvimento é mais lento, meu objetivo em compartilhar essa biblioteca com a comunidade é que comecem a usá-la para desenvolver paineis e que contribuam com a implementação e melhoria da mesma.

 

EJoOSOj.gif

Sua pergunta foi respondida? Certifique-se de marcar a resposta como a solução aceita.
Se existe mais de uma resposta, utilize o "vote up" para destacá-la.
Se você achar uma resposta útil, diga obrigado clicando no botão "Gostei".

Link para o comentário
Compartilhar em outros sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Visitante
Responder

×   Você colou conteúdo com formatação.   Remover formatação

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Processando...
  • Registre-se

    Faça parte da maior e  mais antigas comunidades sobre Lineage2 da América Latina.





  • Patrocinadores

  • Quem Está Navegando

    • Nenhum usuário registrado visualizando esta página.
  • Posts

    • Teria como fazer do dusk shield e do zombie shield dessa maneira?     Teria como fazer do dusk shield e do zombie shield dessa maneira?     Teria como fazer do dusk shield e do zombie shield dessa maneira?     Teria como fazer do dusk shield e do zombie shield dessa maneira?     Teria como fazer do dusk shield e do zombie shield dessa maneira?     Teria como fazer do dusk shield e do zombie shield dessa maneira?    
    • muchas gracias muy lindos NPC 🙂
    • relaxa jovem gafanhoto, testa as quests. e posTa os erros indesejaveis.  
    • Se alguém pudesse me ensinar como codificar as missões, eu ficaria feliz em fazer isso sozinho ou pelo menos ajudar. Eu realmente quero jogar em um servidor onde todas as quests funcionem bem e melhor ainda se você puder fazer quests customizadas!
    • mas no interlude, nem todas as quests de class,  vai mostrar onde tem que ir, ate o reborn nao mostrava quando era interlude, só mostrou depois que eles colocaram client classic pra rodar, e ficou melhor ainda quando virou hellbound em diante, mas ha sim alguma chance de modificar isso direto no script para fazer igualmente, só basta te um pouco de paciencia e persistencia exato
    • 408_PathToElvenwizard dá Orion eu tive que mexer tbm, até modifiquei e consegui deixar ela igual do Classic, com a seta e a marcação no mapa. (não retail IL) Dá pra importar py de várias revs, o foda é que não da regular as quest py através do debug em tempo real, pelo menos eu não consegui rsrs
    • Hasta el momento todas las QUESTS son completables si te guias con un tutorial de youtube. El problema es que tienen bugs de locacion y de subquests que no avanzan o no te marcan correctamente a donde ir en el mapa, cosa que en Retail si se ve como corresponde.
    • estranho, mas pelo menos a galera nunca reclamo das quests quando tinha aberto 5x, geral fez class primeira e segunda job, poucos que compraram a class
    • en RUSaCis-3.5 data pack, las Quests estan en formato .java y son diferentes a como estan redactadas en jOrion y jFrozen 1.5 (ProyectX) package net.sf.l2j.gameserver.scripting.quest; import net.sf.l2j.commons.random.Rnd; import net.sf.l2j.gameserver.enums.Paperdoll; import net.sf.l2j.gameserver.enums.QuestStatus; import net.sf.l2j.gameserver.enums.actors.ClassId; import net.sf.l2j.gameserver.model.actor.Creature; import net.sf.l2j.gameserver.model.actor.Npc; import net.sf.l2j.gameserver.model.actor.Player; import net.sf.l2j.gameserver.network.serverpackets.SocialAction; import net.sf.l2j.gameserver.scripting.QuestState; public class Q224_TestOfSagittarius extends SecondClassQuest { private static final String QUEST_NAME = "Q224_TestOfSagittarius"; // Items private static final int BERNARD_INTRODUCTION = 3294; private static final int HAMIL_LETTER_1 = 3295; private static final int HAMIL_LETTER_2 = 3296; private static final int HAMIL_LETTER_3 = 3297; private static final int HUNTER_RUNE_1 = 3298; private static final int HUNTER_RUNE_2 = 3299; private static final int TALISMAN_OF_KADESH = 3300; private static final int TALISMAN_OF_SNAKE = 3301; private static final int MITHRIL_CLIP = 3302; private static final int STAKATO_CHITIN = 3303; private static final int REINFORCED_BOWSTRING = 3304; private static final int MANASHEN_HORN = 3305; private static final int BLOOD_OF_LIZARDMAN = 3306; private static final int CRESCENT_MOON_BOW = 3028; private static final int WOODEN_ARROW = 17; // Rewards private static final int MARK_OF_SAGITTARIUS = 3293; // NPCs private static final int BERNARD = 30702; private static final int HAMIL = 30626; private static final int SIR_ARON_TANFORD = 30653; private static final int VOKIAN = 30514; private static final int GAUEN = 30717; // Monsters private static final int ANT = 20079; private static final int ANT_CAPTAIN = 20080; private static final int ANT_OVERSEER = 20081; private static final int ANT_RECRUIT = 20082; private static final int ANT_PATROL = 20084; private static final int ANT_GUARD = 20086; private static final int NOBLE_ANT = 20089; private static final int NOBLE_ANT_LEADER = 20090; private static final int BREKA_ORC_SHAMAN = 20269; private static final int BREKA_ORC_OVERLORD = 20270; private static final int MARSH_STAKATO_WORKER = 20230; private static final int MARSH_STAKATO_SOLDIER = 20232; private static final int MARSH_STAKATO_DRONE = 20234; private static final int MARSH_SPIDER = 20233; private static final int ROAD_SCAVENGER = 20551; private static final int MANASHEN_GARGOYLE = 20563; private static final int LETO_LIZARDMAN = 20577; private static final int LETO_LIZARDMAN_ARCHER = 20578; private static final int LETO_LIZARDMAN_SOLDIER = 20579; private static final int LETO_LIZARDMAN_WARRIOR = 20580; private static final int LETO_LIZARDMAN_SHAMAN = 20581; private static final int LETO_LIZARDMAN_OVERLORD = 20582; private static final int SERPENT_DEMON_KADESH = 27090; public Q224_TestOfSagittarius() { super(224, "Test Of Sagittarius"); setItemsIds(BERNARD_INTRODUCTION, HAMIL_LETTER_1, HAMIL_LETTER_2, HAMIL_LETTER_3, HUNTER_RUNE_1, HUNTER_RUNE_2, TALISMAN_OF_KADESH, TALISMAN_OF_SNAKE, MITHRIL_CLIP, STAKATO_CHITIN, REINFORCED_BOWSTRING, MANASHEN_HORN, BLOOD_OF_LIZARDMAN, CRESCENT_MOON_BOW); addQuestStart(BERNARD); addTalkId(BERNARD, HAMIL, SIR_ARON_TANFORD, VOKIAN, GAUEN); addMyDying(ANT, ANT_CAPTAIN, ANT_OVERSEER, ANT_RECRUIT, ANT_PATROL, ANT_GUARD, NOBLE_ANT, NOBLE_ANT_LEADER, BREKA_ORC_SHAMAN, BREKA_ORC_OVERLORD, MARSH_STAKATO_WORKER, MARSH_STAKATO_SOLDIER, MARSH_STAKATO_DRONE, MARSH_SPIDER, ROAD_SCAVENGER, MANASHEN_GARGOYLE, LETO_LIZARDMAN, LETO_LIZARDMAN_ARCHER, LETO_LIZARDMAN_SOLDIER, LETO_LIZARDMAN_WARRIOR, LETO_LIZARDMAN_SHAMAN, LETO_LIZARDMAN_OVERLORD, SERPENT_DEMON_KADESH); } @Override public String onAdvEvent(String event, Npc npc, Player player) { String htmltext = event; QuestState st = player.getQuestList().getQuestState(QUEST_NAME); if (st == null) return htmltext; // BERNARD if (event.equalsIgnoreCase("30702-04.htm")) { st.setState(QuestStatus.STARTED); st.setCond(1); playSound(player, SOUND_ACCEPT); giveItems(player, BERNARD_INTRODUCTION, 1); if (giveDimensionalDiamonds39(player)) htmltext = "30702-04a.htm"; } // HAMIL else if (event.equalsIgnoreCase("30626-03.htm")) { st.setCond(2); playSound(player, SOUND_MIDDLE); takeItems(player, BERNARD_INTRODUCTION, 1); giveItems(player, HAMIL_LETTER_1, 1); } else if (event.equalsIgnoreCase("30626-07.htm")) { st.setCond(5); playSound(player, SOUND_MIDDLE); takeItems(player, HUNTER_RUNE_1, 10); giveItems(player, HAMIL_LETTER_2, 1); } // SIR_ARON_TANFORD else if (event.equalsIgnoreCase("30653-02.htm")) { st.setCond(3); playSound(player, SOUND_MIDDLE); takeItems(player, HAMIL_LETTER_1, 1); } // VOKIAN else if (event.equalsIgnoreCase("30514-02.htm")) { st.setCond(6); playSound(player, SOUND_MIDDLE); takeItems(player, HAMIL_LETTER_2, 1); } return htmltext; } @Override public String onTalk(Npc npc, Player player) { String htmltext = getNoQuestMsg(); QuestState st = player.getQuestList().getQuestState(QUEST_NAME); if (st == null) return htmltext; switch (st.getState()) { case CREATED: if (player.getClassId() != ClassId.ROGUE && player.getClassId() != ClassId.ELVEN_SCOUT && player.getClassId() != ClassId.ASSASSIN) htmltext = "30702-02.htm"; else if (player.getStatus().getLevel() < 39) htmltext = "30702-01.htm"; else htmltext = "30702-03.htm"; break; case STARTED: int cond = st.getCond(); switch (npc.getNpcId()) { case BERNARD: htmltext = "30702-05.htm"; break; case HAMIL: if (cond == 1) htmltext = "30626-01.htm"; else if (cond == 2 || cond == 3) htmltext = "30626-04.htm"; else if (cond == 4) htmltext = "30626-05.htm"; else if (cond > 4 && cond < 8) htmltext = "30626-08.htm"; else if (cond == 8) { htmltext = "30626-09.htm"; st.setCond(9); playSound(player, SOUND_MIDDLE); takeItems(player, HUNTER_RUNE_2, 10); giveItems(player, HAMIL_LETTER_3, 1); } else if (cond > 8 && cond < 12) htmltext = "30626-10.htm"; else if (cond == 12) { htmltext = "30626-11.htm"; st.setCond(13); playSound(player, SOUND_MIDDLE); } else if (cond == 13) htmltext = "30626-12.htm"; else if (cond == 14) { htmltext = "30626-13.htm"; takeItems(player, BLOOD_OF_LIZARDMAN, -1); takeItems(player, CRESCENT_MOON_BOW, 1); takeItems(player, TALISMAN_OF_KADESH, 1); giveItems(player, MARK_OF_SAGITTARIUS, 1); rewardExpAndSp(player, 54726, 20250); player.broadcastPacket(new SocialAction(player, 3)); playSound(player, SOUND_FINISH); st.exitQuest(false); } break; case SIR_ARON_TANFORD: if (cond == 2) htmltext = "30653-01.htm"; else if (cond > 2) htmltext = "30653-03.htm"; break; case VOKIAN: if (cond == 5) htmltext = "30514-01.htm"; else if (cond == 6) htmltext = "30514-03.htm"; else if (cond == 7) { htmltext = "30514-04.htm"; st.setCond(8); playSound(player, SOUND_MIDDLE); takeItems(player, TALISMAN_OF_SNAKE, 1); } else if (cond > 7) htmltext = "30514-05.htm"; break; case GAUEN: if (cond == 9) { htmltext = "30717-01.htm"; st.setCond(10); playSound(player, SOUND_MIDDLE); takeItems(player, HAMIL_LETTER_3, 1); } else if (cond == 10) htmltext = "30717-03.htm"; else if (cond == 11) { htmltext = "30717-02.htm"; st.setCond(12); playSound(player, SOUND_MIDDLE); takeItems(player, MANASHEN_HORN, 1); takeItems(player, MITHRIL_CLIP, 1); takeItems(player, REINFORCED_BOWSTRING, 1); takeItems(player, STAKATO_CHITIN, 1); giveItems(player, CRESCENT_MOON_BOW, 1); giveItems(player, WOODEN_ARROW, 10); } else if (cond > 11) htmltext = "30717-04.htm"; break; } break; case COMPLETED: htmltext = getAlreadyCompletedMsg(); break; } return htmltext; } @Override public void onMyDying(Npc npc, Creature killer) { final Player player = killer.getActingPlayer(); final QuestState st = checkPlayerState(player, npc, QuestStatus.STARTED); if (st == null) return; switch (npc.getNpcId()) { case ANT: case ANT_CAPTAIN: case ANT_OVERSEER: case ANT_RECRUIT: case ANT_PATROL: case ANT_GUARD: case NOBLE_ANT: case NOBLE_ANT_LEADER: if (st.getCond() == 3 && dropItems(player, HUNTER_RUNE_1, 1, 10, 500000)) st.setCond(4); break; case BREKA_ORC_SHAMAN: case BREKA_ORC_OVERLORD: if (st.getCond() == 6 && dropItems(player, HUNTER_RUNE_2, 1, 10, 500000)) { st.setCond(7); giveItems(player, TALISMAN_OF_SNAKE, 1); } break; case MARSH_STAKATO_WORKER: case MARSH_STAKATO_SOLDIER: case MARSH_STAKATO_DRONE: if (st.getCond() == 10 && dropItems(player, STAKATO_CHITIN, 1, 1, 100000) && player.getInventory().hasItems(MANASHEN_HORN, MITHRIL_CLIP, REINFORCED_BOWSTRING)) st.setCond(11); break; case MARSH_SPIDER: if (st.getCond() == 10 && dropItems(player, REINFORCED_BOWSTRING, 1, 1, 100000) && player.getInventory().hasItems(MANASHEN_HORN, MITHRIL_CLIP, STAKATO_CHITIN)) st.setCond(11); break; case ROAD_SCAVENGER: if (st.getCond() == 10 && dropItems(player, MITHRIL_CLIP, 1, 1, 100000) && player.getInventory().hasItems(MANASHEN_HORN, REINFORCED_BOWSTRING, STAKATO_CHITIN)) st.setCond(11); break; case MANASHEN_GARGOYLE: if (st.getCond() == 10 && dropItems(player, MANASHEN_HORN, 1, 1, 100000) && player.getInventory().hasItems(REINFORCED_BOWSTRING, MITHRIL_CLIP, STAKATO_CHITIN)) st.setCond(11); break; case LETO_LIZARDMAN: case LETO_LIZARDMAN_ARCHER: case LETO_LIZARDMAN_SOLDIER: case LETO_LIZARDMAN_WARRIOR: case LETO_LIZARDMAN_SHAMAN: case LETO_LIZARDMAN_OVERLORD: if (st.getCond() == 13) { if (((player.getInventory().getItemCount(BLOOD_OF_LIZARDMAN) - 120) * 5) > Rnd.get(100)) { playSound(player, SOUND_BEFORE_BATTLE); takeItems(player, BLOOD_OF_LIZARDMAN, -1); addSpawn(SERPENT_DEMON_KADESH, player, false, 300000, true); } else dropItemsAlways(player, BLOOD_OF_LIZARDMAN, 1, 0); } break; case SERPENT_DEMON_KADESH: if (st.getCond() == 13) { if (player.getInventory().getItemIdFrom(Paperdoll.RHAND) == CRESCENT_MOON_BOW) { st.setCond(14); playSound(player, SOUND_MIDDLE); giveItems(player, TALISMAN_OF_KADESH, 1); } else addSpawn(SERPENT_DEMON_KADESH, player, false, 300000, true); } break; } } }  
×
×
  • Criar Novo...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.