Noticias:

Debes de estar registrado para poder ver el contenido indicado. Registrate o Conectate

Menú Principal

Npc Run Shopper - Tienda Express

Iniciado por Swarlog, Ago 11, 2022, 01:08 AM

Tema anterior - Siguiente tema

Swarlog

Debes de estar registrado para poder ver el contenido indicado. Registrate o Conectate

El escenario de este código reside en un server de rates low (en mi caso x7), donde un PJ está leveando sólo y se le acaban los bufos, y las potas para levear.

En lugar de ir y volver de la villa, he creado un voiceCommand ".shop", en el cual viene corriendo un NPC que es una tienda y tiene un tiempo antes de desaparecer mientras te sigue. Tambien le inclui un limite de spawns (solo 1) y un limite para volver a invocar al RunShopper (300 segundos).

Podeis incluir mas exclusiones editando el archivo "shop.java" para evitar que lo invoquen en un asedio, en unas olimpiadas, etc...

Para implementar este codigo necesitamos:

1) Insertar un nuevo NPC
INSERT INTO custom_npc VALUES ('931362', '31362', 'MacCow', '1', 'Shopper', '1', 'LineageNPC.a_traderA_MDwarf', '8.00', '17.00', '1', 'male', 'L2NpcRunShopper', '40', '2444.468190000000000', '1345.800000000000000', '7.500000000000000', '2.700000000000000', '40', '43', '30', '21', '20', '20', '0', '0', '688.86373', '295.91597', '470.40463', '216.53847', '253', '4', '333', '0', '0', '0', '120.00000', '120.00000', '0', '1');

2) Insertar las habilidades del nuevo NPC
INSERT INTO npcaidata VALUES ('931362', '7', '15', '0', '1000', '1', '1', '1', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', null, '300', null, '0', '0', 'fighter');
Si os da error, tendreis que editar el campo ID, del tipo original que es int a bigint. El server funciona perfectamente.

3) Un HTML para nuestro RunShopper en la carpeta (la teneis que crear) "runShopper".
<head><title>Run Shopper</title></head>
<body>
Oi tu llamada!<br>
He llegado hasta aqui gracias a una pocion magica, y no puedo quedarme mucho tiempo!<br><br>
Rapido, date prisa, los efectos solo duran unos segundos!<br><br>
<a action="bypass -h npc_%objectId%_multisell 931362">Comprar viveres</a><br>

4) Un XML para poder vender:
<?xml version="1.0" encoding="UTF-8"?>
<list xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../xsd/multisell.xsd">
 <item>
 <!-- Adena -->
 <ingredient count="150" id="57" />
 <!-- Healing Potion -->
 <production count="1" id="727" />
 </item>
 <item>
 <!-- Adena -->
 <ingredient count="500" id="57" />
 <!-- Mana Potion -->
 <production count="1" id="728" />
 </item>
 <item>
 <!-- Adena -->
 <ingredient count="150" id="57" />
 <!-- Haste Potion -->
 <production count="1" id="734" />
 </item>
 <item>
 <!-- Adena -->
 <ingredient count="150" id="57" />
 <!-- Alacrity Potion -->
 <production count="1" id="735" />
 </item>
 <item>
 <!-- Adena -->
 <ingredient count="250" id="57" />
 <!-- Lesser Healing Potion -->
 <production count="1" id="1060" />
 </item>
 <item>
 <!-- Adena -->
 <ingredient count="350" id="57" />
 <!-- Greater Healing Potion -->
 <production count="1" id="1061" />
 </item>
 <item>
 <!-- Adena -->
 <ingredient count="300" id="57" />
 <!-- Haste Potion -->
 <production count="1" id="1062" />
 </item>
 <item>
 <!-- Adena -->
 <ingredient count="100" id="57" />
 <!-- Beginner's Potion -->
 <production count="1" id="1073" />
 </item>
 <item>
 <!-- Adena -->
 <ingredient count="300" id="57" />
 <!-- Greater Haste Potion -->
 <production count="1" id="1374" />
 </item>
 <item>
 <!-- Adena -->
 <ingredient count="500" id="57" />
 <!-- Greater Healing Potion -->
 <production count="1" id="1539" />
 </item>
 <item>
 <!-- Adena -->
 <ingredient count="1000" id="57" />
 <!-- Quick Healing Potion -->
 <production count="1" id="1540" />
 </item>
</list>

5) Código para el GameServer:
Index: java/com/l2jserver/gameserver/model/L2Object.java
===================================================================
--- java/com/l2jserver/gameserver/model/L2Object.java (revision 5685)
+++ java/com/l2jserver/gameserver/model/L2Object.java (working copy)
@@ -178,7 +178,8 @@
 L2TvTEventNpcInstance(L2Npc),
 L2WeddingManagerInstance(L2Npc),
 L2EventMobInstance(L2Npc),
- L2BirthdayCakeInstance(L2Npc);
+ L2BirthdayCakeInstance(L2Npc),
+ L2NpcRunShopperInstance(L2Npc); // By Kimeraweb
 
private final InstanceType _parent;
 private final long _typeL;
Index: java/com/l2jserver/gameserver/model/actor/instance/L2NpcRunShopperInstance.java
===================================================================
--- java/com/l2jserver/gameserver/model/actor/instance/L2NpcRunShopperInstance.java (revision 0)
+++ java/com/l2jserver/gameserver/model/actor/instance/L2NpcRunShopperInstance.java (revision 0)
@@ -0,0 +1,77 @@
+/*
+ * 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/>.
+ */
+package com.l2jserver.gameserver.model.actor.instance;
+
+import com.l2jserver.gameserver.ai.CtrlIntention;
+import com.l2jserver.gameserver.ai.L2CharacterAI;
+import com.l2jserver.gameserver.ai.L2NpcRunShopperAI;
+import com.l2jserver.gameserver.model.actor.L2Character;
+import com.l2jserver.gameserver.model.actor.templates.L2NpcTemplate;
+
+/**
+ * @author Kimera
+ * Para usar con el voicecommand .shop
+ */
+public class L2NpcRunShopperInstance extends L2NpcInstance
+{
+
+ // Constructor de L2NpcInstace, se sobrescribe el InstanceType
+ public L2NpcRunShopperInstance(int objectId, L2NpcTemplate template)
+ {
+ super(objectId, template);
+ setInstanceType(InstanceType.L2NpcRunShopperInstance);
+ setAI(new L2NpcRunShopperAI(new L2NpcRunShopperAIAccessor()));
+ }
+
+ /**
+ * AI can't be deattached, npc must move always with the same AI instance.
+ * @param newAI AI to set for this L2NpcWalkerInstance
+ */
+ @Override
+ public void setAI(L2CharacterAI newAI)
+ {
+ if(!(_ai instanceof L2NpcRunShopperAI))
+ _ai = newAI;
+ }
+
+ @Override
+ public void onSpawn()
+ {
+ super.onSpawn();
+ }
+
+ @Override
+ public String getHtmlPath(int npcId, int val)
+ {
+ String pom = "";
+
+ if (val == 0) pom = "" + npcId;
+ else pom = npcId + "-" + val;
+
+ return "data/html/runShopper/" + pom + ".htm";
+ }
+
+ public class L2NpcRunShopperAIAccessor extends L2Character.AIAccessor
+ {
+ /**
+ * AI can't be deattached.
+ */
+ @Override
+ public void detachAI()
+ {
+ }
+ }
+
+}
Index: java/com/l2jserver/gameserver/ai/L2NpcRunShopperAI.java
===================================================================
--- java/com/l2jserver/gameserver/ai/L2NpcRunShopperAI.java (revision 0)
+++ java/com/l2jserver/gameserver/ai/L2NpcRunShopperAI.java (revision 0)
@@ -0,0 +1,114 @@
+/*
+ * 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/>.
+ */
+package com.l2jserver.gameserver.ai;
+
+import com.l2jserver.Config;
+import com.l2jserver.gameserver.ThreadPoolManager;
+import com.l2jserver.gameserver.model.actor.L2Character;
+import com.l2jserver.gameserver.model.actor.instance.L2NpcRunShopperInstance;
+import com.sun.istack.internal.logging.Logger;
+
+/**
+ * @author Kimera
+ *
+ */
+public class L2NpcRunShopperAI extends L2CharacterAI implements Runnable
+{
+ int _lifetime = 30; // 30 segundos para interactuar con el Shopper, luego se irá
+
+
+ public L2NpcRunShopperAI(L2Character.AIAccessor accessor)
+ {
+ super(accessor);
+ ThreadPoolManager.getInstance().scheduleAiAtFixedRate(this, 1000, 1000);
+ }
+
+ @Override
+ public void run()
+ {
+ if (_followTarget != null)
+ {
+ _lifetime --;
+
+ if (_lifetime==20)
+ _followTarget.sendMessage("En 20 segundos me voy, me estan esperando!");
+ else if (_lifetime==10)
+ _followTarget.sendMessage("En 10 segundos me voy, asegurate de tener de todo!");
+ else if (_lifetime==0)
+ {
+ _followTarget.sendMessage("Adios! Avisame cuando necesites mas!");
+ goAway();
+ }
+ else if (_lifetime<0 && _lifetime>-10)
+ goAway();
+
+ else if (_lifetime<-10)
+ goForEver();
+
+ goTo();
+ }
+ return;
+ }
+
+ private void goForEver()
+ {
+ _accessor.detachAI();
+ _actor.decayMe();
+ _actor.deleteMe();
+ }
+
+ private void goAway()
+ {
+ _accessor.moveTo(_followTarget.getX()*-600, _followTarget.getY(), _followTarget.getZ(),100);
+ }
+
+ private void goTo()
+ {
+ if (_followTarget != null)
+ {
+ if (_actor.isMovementDisabled())
+ {
+ Logger.getLogger(getClass()).warning("No has añadido la habilidad de moverse al NPC " + _actor.getName() + " ID:" + _actor.getInstanceId());
+ return;
+ }
+
+ L2CharacterAI ai = _actor.getAI();
+ _intention = CtrlIntention.AI_INTENTION_FOLLOW;
+
+ _actor.setRunning();
+ _actor.setTarget(_followTarget);
+ _actor.setAI(new L2NpcRunShopperAI(_accessor));
+ _accessor.moveTo(_followTarget.getX(), _followTarget.getY(), _followTarget.getZ(),100);
+
+ if (Config.DEBUG)
+ {
+ System.out.println("AI Intention: " + ai.getIntention().toString());
+ System.out.println("Target en: " +_followTarget.getX()+","+ _followTarget.getY()+","+ _followTarget.getZ());
+ System.out.println("Shop en: " + _actor.getX() + "," + _actor.getY()+","+_actor.getZ());
+ }
+ }
+
+
+
+ }
+
+ @Override
+ public L2NpcRunShopperInstance getActor()
+ {
+ return (L2NpcRunShopperInstance) super.getActor();
+ }
+
+
+}

6) Código para el Datapack
Index: dist/game/data/scripts/handlers/voicedcommandhandlers/Shop.java
===================================================================
--- dist/game/data/scripts/handlers/voicedcommandhandlers/Shop.java (revision 0)
+++ dist/game/data/scripts/handlers/voicedcommandhandlers/Shop.java (revision 0)
@@ -0,0 +1,149 @@
+/*
+ * 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/>.
+ */
+package handlers.voicedcommandhandlers;
+
+import java.util.Date;
+import java.util.LinkedHashMap;
+
+import com.l2jserver.gameserver.ai.CtrlIntention;
+import com.l2jserver.gameserver.ai.L2CharacterAI;
+import com.l2jserver.gameserver.datatables.NpcTable;
+import com.l2jserver.gameserver.datatables.SpawnTable;
+import com.l2jserver.gameserver.handler.IVoicedCommandHandler;
+import com.l2jserver.gameserver.model.L2Spawn;
+import com.l2jserver.gameserver.model.actor.L2Character;
+import com.l2jserver.gameserver.model.actor.instance.L2NpcRunShopperInstance;
+import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
+
+/**
+ * @author Kimera
+ *
+ */
+public class Shop implements IVoicedCommandHandler
+{
+
+ private static final String[] _voicedCommands = { "shop" };
+ private LinkedHashMap<L2PcInstance,LimitTime> _actors = new LinkedHashMap<>();
+
+ @Override
+ public boolean useVoicedCommand(String command, L2PcInstance activeChar, String params)
+ {
+ if (command.equals("shop"))
+ {
+
+ if (
+ (!_actors.containsKey(activeChar)) || // Nunca ha usado un ShopRunner
+ (_actors.containsKey(activeChar) && _actors.get(activeChar).isReady()) // Lo ha usado pero está listo
+ )
+ {
+ if (!_actors.containsKey(activeChar)) // Si no existia, lo añado
+ _actors.put(activeChar, new LimitTime());
+ else
+ {
+ _actors.keySet().remove(activeChar); // Pero si ya existia lo borro y
+ _actors.put(activeChar, new LimitTime()); // lo añado nuevamente
+ }
+
+ L2Spawn spawn = null;
+ try
+ {
+ spawn = new L2Spawn(NpcTable.getInstance().getTemplate(931362));
+ spawn.setLocx(600 + activeChar.getX());
+ spawn.setLocy(activeChar.getY());
+ spawn.setLocz(activeChar.getZ());
+ spawn.setAmount(1);
+ spawn.setHeading(activeChar.getHeading());
+ if (activeChar.getInstanceId() > 0)
+ {
+ spawn.setInstanceId(activeChar.getInstanceId());
+ }
+ else
+ spawn.setInstanceId(0);
+ SpawnTable.getInstance().addNewSpawn(spawn, false);
+ spawn.init();
+ spawn.stopRespawn();
+
+ L2Character actor = spawn.getLastSpawn().getAI().getActor();
+ L2CharacterAI l2AI = actor.getAI();
+
+ l2AI.setIntention(CtrlIntention.AI_INTENTION_FOLLOW, spawn.getLastSpawn(), activeChar);
+ l2AI.startFollow(activeChar);
+
+ L2NpcRunShopperInstance lshopI = new L2NpcRunShopperInstance(931362,spawn.getTemplate());
+ lshopI.setTarget(activeChar);
+ }
+ catch (SecurityException | ClassNotFoundException | NoSuchMethodException e) { e.printStackTrace(); }
+ } else { activeChar.sendMessage("No puedes invocar a un RunShopper en " + getTimeRecall(activeChar) + " segundos."); } // Si no cumple las condiciones, se le hace saber
+ }
+ return true;
+ }
+
+ @Override
+ public String[] getVoicedCommandList()
+ {
+ return _voicedCommands;
+ }
+
+ public void deleteActor(L2PcInstance actor)
+ {
+ _actors.remove(actor);
+ }
+
+ public int getTimeRecall(L2PcInstance activeChar)
+ {
+ LimitTime t = _actors.get(activeChar);
+ return t.getTimeRecall();
+ }
+
+}
+
+/**
+ * Esta clase impide que un jugador invoque a un RunShopper hasta pasado un tiempo determinado
+ * @author Kimera
+ *
+ */
+class LimitTime
+{
+ long myTime;
+ int waitTime = 300000; // 300 segundos
+
+ public LimitTime()
+ {
+ Date d = new Date();
+ myTime = d.getTime();
+ }
+
+ public long getTime()
+ {
+ return myTime;
+ }
+
+ public boolean isReady()
+ {
+ boolean isReady = true;
+ Date d = new Date();
+ if ((myTime + waitTime) > d.getTime()) // confiemos en el autoboxing
+ isReady = false;
+
+ return isReady;
+ }
+
+ public int getTimeRecall()
+ {
+ Date d = new Date();
+ int timeLeft = (int) (((myTime + waitTime) - d.getTime()) / 1000);
+ return timeLeft;
+ }
+}
Index: dist/game/data/scripts/handlers/MasterHandler.java
===================================================================
--- dist/game/data/scripts/handlers/MasterHandler.java (revision 9265)
+++ dist/game/data/scripts/handlers/MasterHandler.java (working copy)
@@ -290,6 +290,7 @@
 import handlers.voicedcommandhandlers.Debug;
 import handlers.voicedcommandhandlers.Hellbound;
 import handlers.voicedcommandhandlers.Lang;
+import handlers.voicedcommandhandlers.Shop;
 import handlers.voicedcommandhandlers.StatsVCmd;
 import handlers.voicedcommandhandlers.TvTVoicedInfo;
 import handlers.voicedcommandhandlers.Wedding;
@@ -592,6 +593,7 @@
 {
 // Voiced Command Handlers
 StatsVCmd.class,
+ Shop.class,
 // TODO: Add configuration options for this voiced commands:
 // CastleVCmd.class,
 // SetVCmd.class,

By Kimeraweb