Bladeren bron

NPC Walkers core support (patch v4.5).
Thanks to at least: bigbro, Rayan/L2Emu, theonn, Nickolbas, Droppo, Kilkenny

Sami 17 jaren geleden
bovenliggende
commit
8b8bd1d2a2

+ 4 - 0
L2_GameServer_It/java/config/options.properties

@@ -130,6 +130,7 @@ MaxNPCAnimation = 20
 MinMonsterAnimation = 5
 MaxMonsterAnimation = 20
 
+
 # Show "data/html/servnews.htm" when a character enters world.
 ShowServerNews = False
 
@@ -171,6 +172,9 @@ AllowBoat        = False
 AllowCursedWeapons = True
 #Allow Manor
 AllowManor		 = False
+#Allow WalkerNPC
+AllowNpcWalkers = False
+
 
 # Allow L2Walker client (Default False)
 # Can be True, False, GM

+ 8 - 1
L2_GameServer_It/java/net/sf/l2j/Config.java

@@ -618,6 +618,9 @@ public final class Config
     /** Allow cursed weapons ? */
     public static boolean           ALLOW_CURSED_WEAPONS;
 
+    //WALKER NPC
+    public static boolean          ALLOW_NPC_WALKERS;
+    
     /** Time after which a packet is considered as lost */
     public static int             PACKET_LIFETIME;
 
@@ -645,6 +648,8 @@ public final class Config
     /** Maximal time between animations of a MONSTER */
     public static int MAX_MONSTER_ANIMATION;
 
+
+
     /** Activate position recorder ? */
     public static boolean ACTIVATE_POSITION_RECORDER;
     /** Use 3D Map ? */
@@ -1270,6 +1275,7 @@ public final class Config
                 ALLOWFISHING                    = Boolean.valueOf(optionsSettings.getProperty("AllowFishing", "False"));
                 ALLOW_MANOR                     = Boolean.parseBoolean(optionsSettings.getProperty("AllowManor", "False"));
                 ALLOW_BOAT                      = Boolean.valueOf(optionsSettings.getProperty("AllowBoat", "False"));
+                ALLOW_NPC_WALKERS               = Boolean.valueOf(optionsSettings.getProperty("AllowNpcWalkers", "true"));
                 ALLOW_CURSED_WEAPONS            = Boolean.valueOf(optionsSettings.getProperty("AllowCursedWeapons", "False"));
 
                 ALLOW_L2WALKER_CLIENT           = L2WalkerAllowed.valueOf(optionsSettings.getProperty("AllowL2Walker", "False"));
@@ -1301,7 +1307,8 @@ public final class Config
                 MAX_NPC_ANIMATION               = Integer.parseInt(optionsSettings.getProperty("MaxNPCAnimation", "20"));
                 MIN_MONSTER_ANIMATION           = Integer.parseInt(optionsSettings.getProperty("MinMonsterAnimation", "5"));
                 MAX_MONSTER_ANIMATION           = Integer.parseInt(optionsSettings.getProperty("MaxMonsterAnimation", "20"));
-
+ 
+                
                 SERVER_NEWS                     = Boolean.valueOf(optionsSettings.getProperty("ShowServerNews", "False"));
                 SHOW_NPC_LVL                    = Boolean.valueOf(optionsSettings.getProperty("ShowNpcLevel", "False"));
 

+ 10 - 0
L2_GameServer_It/java/net/sf/l2j/gameserver/GameServer.java

@@ -50,6 +50,7 @@ import net.sf.l2j.gameserver.datatables.LevelUpData;
 import net.sf.l2j.gameserver.datatables.MapRegionTable;
 import net.sf.l2j.gameserver.datatables.NobleSkillTable;
 import net.sf.l2j.gameserver.datatables.NpcTable;
+import net.sf.l2j.gameserver.datatables.NpcWalkerRoutesTable;
 import net.sf.l2j.gameserver.datatables.SkillSpellbookTable;
 import net.sf.l2j.gameserver.datatables.SkillTable;
 import net.sf.l2j.gameserver.datatables.SkillTreeTable;
@@ -58,6 +59,7 @@ import net.sf.l2j.gameserver.datatables.StaticObjects;
 import net.sf.l2j.gameserver.datatables.SummonItemsData;
 import net.sf.l2j.gameserver.datatables.TeleportLocationTable;
 import net.sf.l2j.gameserver.datatables.ZoneData;
+
 import net.sf.l2j.gameserver.geoeditorcon.GeoEditorListener;
 import net.sf.l2j.gameserver.handler.AdminCommandHandler;
 import net.sf.l2j.gameserver.handler.ItemHandler;
@@ -330,6 +332,12 @@ public class GameServer
 		    throw new Exception("Could not initialize the skill table");
 		}
 
+		
+//		L2EMU_ADD by Rayan. L2J - BigBro
+		if(Config.ALLOW_NPC_WALKERS)
+			NpcWalkerRoutesTable.getInstance().load();
+		//L2EMU_ADD by Rayan. L2J - BigBro
+		
 		RecipeController.getInstance();
 
 		SkillTreeTable.getInstance();
@@ -587,6 +595,8 @@ public class GameServer
 
 		_log.config("VoicedCommandHandler: Loaded " + _voicedCommandHandler.size() + " handlers.");
 
+		
+						
 		if(Config.L2JMOD_ALLOW_WEDDING)
 			CoupleManager.getInstance();
 

+ 215 - 0
L2_GameServer_It/java/net/sf/l2j/gameserver/ai/L2NpcWalkerAI.java

@@ -0,0 +1,215 @@
+/* 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 2, 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+package net.sf.l2j.gameserver.ai;
+
+import javolution.util.FastList;
+import net.sf.l2j.Config;
+import net.sf.l2j.gameserver.ThreadPoolManager;
+import net.sf.l2j.gameserver.datatables.NpcWalkerRoutesTable;
+import net.sf.l2j.gameserver.model.L2CharPosition;
+import net.sf.l2j.gameserver.model.L2Character;
+import net.sf.l2j.gameserver.model.L2NpcWalkerNode;
+import net.sf.l2j.gameserver.model.actor.instance.L2NpcWalkerInstance;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+public class L2NpcWalkerAI extends L2CharacterAI implements Runnable
+{
+	private static final Log _log = LogFactory.getLog(L2NpcWalkerAI.class);
+	private static final int DEFAULT_MOVE_DELAY = 0;
+
+	private long _nextMoveTime;
+
+	private boolean _walkingToNextPoint = false;
+
+	/**
+	 * home points for xyz
+	 */
+	int _homeX, _homeY, _homeZ;
+
+	/**
+	 * route of the current npc
+	 */
+	private final FastList<L2NpcWalkerNode> _route = NpcWalkerRoutesTable.getInstance().getRouteForNpc(getActor().getNpcId());
+
+	/**
+	 * current node
+	 */
+	private int _currentPos;
+
+
+	/**
+	 * Constructor of L2CharacterAI.<BR><BR>
+	 *
+	 * @param accessor The AI accessor of the L2Character
+	 */
+	public L2NpcWalkerAI(L2Character.AIAccessor accessor)
+	{
+		super(accessor);
+		// Do we really need 2 minutes delay before start?
+		// no we dont... :)
+		ThreadPoolManager.getInstance().scheduleAiAtFixedRate(this, 0, 1000);
+	}
+
+	public void run()
+	{
+		onEvtThink();
+	}
+
+	protected void onEvtThink()
+	{
+		if(!Config.ALLOW_NPC_WALKERS)
+			return;
+
+		if(isWalkingToNextPoint())
+		{
+			checkArrived();
+			return;
+		}
+
+		if(_nextMoveTime < System.currentTimeMillis())
+			walkToLocation();
+	}
+
+	/**
+	 * If npc can't walk to it's target then just teleport to next point
+	 * @param blocked_at_pos ignoring it
+	 */
+	protected void onEvtArrivedBlocked(L2CharPosition blocked_at_pos)
+	{
+		_log.error("NpcWalker ID: " + getActor().getNpcId() + ": Blocked at rote position [" + _currentPos + "], coords: " + blocked_at_pos.x + ", " + blocked_at_pos.y + ", " + blocked_at_pos.z + ". Teleporting to next point");
+
+		int destinationX = _route.get(_currentPos).getMoveX();
+		int destinationY = _route.get(_currentPos).getMoveY();
+		int destinationZ = _route.get(_currentPos).getMoveZ();
+
+		getActor().teleToLocation(destinationX, destinationY, destinationZ, false);
+		super.onEvtArrivedBlocked(blocked_at_pos);
+	}
+
+	private void checkArrived()
+	{
+		int destinationX = _route.get(_currentPos).getMoveX();
+		int destinationY = _route.get(_currentPos).getMoveY();
+		int destinationZ = _route.get(_currentPos).getMoveZ();
+
+		if(getActor().getX() == destinationX && getActor().getY() == destinationY && getActor().getZ() == destinationZ)
+		{
+			String chat = _route.get(_currentPos).getChatText();
+			if(chat != null && !chat.equals(""))
+			{
+				try
+				{
+					getActor().broadcastChat(chat);
+				}
+				catch(ArrayIndexOutOfBoundsException e)
+				{
+					_log.info("L2NpcWalkerInstance: Error, " + e);
+				}
+			}
+
+			//time in millis
+			long delay = _route.get(_currentPos).getDelay()*1000;
+
+			//sleeps between each move
+			if(delay <= 0)
+			{
+				delay = DEFAULT_MOVE_DELAY;
+				if(Config.DEVELOPER)
+					_log.warn("Wrong Delay Set in Npc Walker Functions = " + delay + " secs, using default delay: " + DEFAULT_MOVE_DELAY + " secs instead.");
+			}
+
+			_nextMoveTime = System.currentTimeMillis() + delay;
+			setWalkingToNextPoint(false);
+		}
+	}
+
+	private void walkToLocation()
+	{
+		if(_currentPos < (_route.size() - 1))
+			_currentPos++;
+		else
+			_currentPos = 0;
+
+		boolean moveType = _route.get(_currentPos).getRunning();
+
+		/**
+		 * false - walking
+		 * true - Running
+		 */
+		if(moveType)
+			getActor().setRunning();
+		else
+			getActor().setWalking();
+
+		//now we define destination
+		int destinationX = _route.get(_currentPos).getMoveX();
+		int destinationY = _route.get(_currentPos).getMoveY();
+		int destinationZ = _route.get(_currentPos).getMoveZ();
+
+		//notify AI of MOVE_TO
+		setWalkingToNextPoint(true);
+	
+		setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, new L2CharPosition(destinationX, destinationY, destinationZ, 0));
+	}
+
+	public L2NpcWalkerInstance getActor()
+	{
+		return (L2NpcWalkerInstance) super.getActor();
+	}
+
+	public int getHomeX()
+	{
+		return _homeX;
+	}
+
+	public int getHomeY()
+	{
+		return _homeY;
+	}
+
+	public int getHomeZ()
+	{
+		return _homeZ;
+	}
+
+	public void setHomeX(int homeX)
+	{
+		_homeX = homeX;
+	}
+
+	public void setHomeY(int homeY)
+	{
+		_homeY = homeY;
+	}
+
+	public void setHomeZ(int homeZ)
+	{
+		_homeZ = homeZ;
+	}
+
+	public boolean isWalkingToNextPoint()
+	{
+		return _walkingToNextPoint;
+	}
+
+	public void setWalkingToNextPoint(boolean value)
+	{
+		_walkingToNextPoint = value;
+	}
+}

+ 1 - 0
L2_GameServer_It/java/net/sf/l2j/gameserver/datatables/CharTemplateTable.java

@@ -116,6 +116,7 @@ public class CharTemplateTable
 				set.set("baseMAtkSpd", /*classId.isMage()? 166 : 333*/ rset.getInt("char_templates.m_spd"));
 				set.set("baseCritRate", rset.getInt("char_templates.critical")/10);
 				set.set("baseRunSpd", rset.getInt("move_spd"));
+				set.set("baseWalkSpd",0);
 				set.set("baseShldDef", 0);
 				set.set("baseShldRate", 0);
 				set.set("baseAtkRange", 40);

+ 127 - 0
L2_GameServer_It/java/net/sf/l2j/gameserver/datatables/NpcWalkerRoutesTable.java

@@ -0,0 +1,127 @@
+/*
+ * 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 2, 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+package net.sf.l2j.gameserver.datatables;
+
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+
+import javolution.util.FastList;
+import javolution.util.FastMap;
+import net.sf.l2j.L2DatabaseFactory;
+import net.sf.l2j.gameserver.model.L2NpcWalkerNode;
+
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Main Table to Load Npc Walkers Routes and Chat SQL Table.<br>
+ * 
+ * @author Rayan RPG for L2Emu Project
+ * 
+ * @since 927 
+ *
+ */
+public class NpcWalkerRoutesTable 
+{
+	private final static Log _log = LogFactory.getLog(SpawnTable.class.getName());
+
+	private static NpcWalkerRoutesTable  _instance;
+
+	private FastList<L2NpcWalkerNode> _routes;
+
+	public static NpcWalkerRoutesTable getInstance()
+	{
+		if(_instance == null)
+		{
+				_instance = new NpcWalkerRoutesTable();
+				_log.info("Initializing Walkers Routes Table.");
+		}
+		
+		return _instance;
+	}
+
+	private NpcWalkerRoutesTable()
+	{
+	}
+	//FIXME: NPE while loading. :S
+	public void load()
+	{
+		 _routes = new FastList<L2NpcWalkerNode>();
+		java.sql.Connection con = null;
+		try
+		{
+			con = L2DatabaseFactory.getInstance().getConnection();
+			PreparedStatement statement = con.prepareStatement("SELECT route_id, npc_id, move_point, chatText, move_x, move_y, move_z, delay, running FROM walker_routes");
+			ResultSet rset = statement.executeQuery();
+			L2NpcWalkerNode  route;
+			while (rset.next())
+			{
+				route = new L2NpcWalkerNode();
+				route.setRouteId(rset.getInt("route_id"));
+				route.setNpcId(rset.getInt("npc_id"));
+				route.setMovePoint(rset.getString("move_point"));
+				route.setChatText(rset.getString("chatText"));
+				
+				route.setMoveX(rset.getInt("move_x"));
+				route.setMoveY(rset.getInt("move_y"));
+				route.setMoveZ(rset.getInt("move_z"));
+				route.setDelay(rset.getInt("delay"));
+				route.setRunning(rset.getBoolean("running"));
+
+			
+				_routes.add(route);
+			}
+
+			rset.close();
+			statement.close();
+
+			_log.info("WalkerRoutesTable: Loaded "+_routes.size()+" Npc Walker Routes.");
+			rset.close();
+			statement.close();
+		}
+		catch (Exception e) 
+		{
+			_log.fatal("WalkerRoutesTable: Error while loading Npc Walkers Routes: "+e.getMessage());
+		}
+		finally
+		{
+			try
+			{
+				con.close();
+			}
+			catch (Exception e) {}
+		}
+	}
+	
+	public FastList<L2NpcWalkerNode> getRouteForNpc(int id)
+	{
+		FastList<L2NpcWalkerNode> _return = new FastList<L2NpcWalkerNode>();
+		
+		 for (FastList.Node<L2NpcWalkerNode> n = _routes.head(), end = _routes.tail(); (n = n.getNext()) != end;) {
+	         if(n.getValue().getNpcId() == id)
+	         {
+	        	 _return.add(n.getValue());
+	         }
+	     }
+		return _return;
+		
+		
+	}
+}

+ 8 - 0
L2_GameServer_It/java/net/sf/l2j/gameserver/handler/admincommandhandlers/AdminAdmin.java

@@ -26,6 +26,7 @@ import net.sf.l2j.gameserver.Olympiad;
 import net.sf.l2j.gameserver.cache.HtmCache;
 import net.sf.l2j.gameserver.datatables.ItemTable;
 import net.sf.l2j.gameserver.datatables.NpcTable;
+import net.sf.l2j.gameserver.datatables.NpcWalkerRoutesTable;
 import net.sf.l2j.gameserver.datatables.SkillTable;
 import net.sf.l2j.gameserver.datatables.TeleportLocationTable;
 import net.sf.l2j.gameserver.handler.IAdminCommandHandler;
@@ -217,6 +218,13 @@ public class AdminAdmin implements IAdminCommandHandler {
 					Manager.reloadAll();
 					activeChar.sendMessage("All instance manager has been reloaded");
 				}
+				else if(type.startsWith("npcwalkers"))
+				{
+					NpcWalkerRoutesTable.getInstance().load();
+					activeChar.sendMessage("All NPC walker routes have been reloaded");
+					
+				}
+				
 			}
 			catch(Exception e)
 			{

+ 1 - 1
L2_GameServer_It/java/net/sf/l2j/gameserver/handler/admincommandhandlers/AdminEditNpc.java

@@ -726,7 +726,7 @@ public class AdminEditNpc implements IAdminCommandHandler {
 			adminReply.replace("%rHand%", String.valueOf(npc.rhand));
 			adminReply.replace("%lHand%", String.valueOf(npc.lhand));
 			adminReply.replace("%armor%", String.valueOf(npc.armor));
-			adminReply.replace("%walkSpd%", String.valueOf(Util.roundTo((float)(npc.baseRunSpd*0.7),3)));
+			adminReply.replace("%walkSpd%", String.valueOf(npc.baseWalkSpd));
 			adminReply.replace("%runSpd%", String.valueOf(npc.baseRunSpd));
 			adminReply.replace("%factionId%", npc.factionId == null ? "" : npc.factionId);
 			adminReply.replace("%factionRange%", String.valueOf(npc.factionRange));

+ 161 - 0
L2_GameServer_It/java/net/sf/l2j/gameserver/model/L2NpcWalkerNode.java

@@ -0,0 +1,161 @@
+/*
+ * 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 2, 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+package net.sf.l2j.gameserver.model;
+
+import net.sf.l2j.gameserver.templates.StatsSet;
+
+/**
+ * 
+ * @author Rayan RPG
+ * @since 927
+ *
+ */
+public class L2NpcWalkerNode 
+{
+	private int _routeId;
+	private int _npcId;
+	private String _movePoint;
+	private String _chatText;
+	private int _moveX;
+	private int _moveY;
+	private int _moveZ;;
+	private int _delay;
+	
+	private boolean _running;
+
+	public void setRunning(boolean val)
+	{
+		_running = val;
+	}
+	
+	public void setRouteId(int id)
+	{
+		_routeId = id;
+	}
+
+	public void setNpcId(int id)
+	{
+		_npcId = id;
+	}
+
+	public void setMovePoint(String val)
+	{
+		_movePoint = val;
+	}
+
+	public void setChatText(String val)
+	{
+		_chatText = val;
+	}
+
+	public void setMoveX(int val)
+	{
+		_moveX = val;
+	}
+
+	public void setMoveY(int val)
+	{
+		_moveY = val;
+	}
+
+	public void setMoveZ(int val)
+	{
+		_moveZ = val;
+	}
+
+	public void setDelay(int val)
+	{
+		_delay = val;
+	}
+
+	public int getRouteId()
+	{
+		return _routeId;
+	}
+
+	public int getNpcId()
+	{
+		return _npcId;
+	}
+
+	public String getMovePoint()
+	{
+		return _movePoint;
+	}
+
+	public String getChatText()
+	{
+		return _chatText;
+	}
+
+	public int getMoveX()
+	{
+		return _moveX;
+	}
+
+	public int getMoveY()
+	{
+		return _moveY;
+	}
+
+	public int getMoveZ()
+	{
+		return _moveZ;
+	}
+
+	public int getDelay()
+	{
+		return _delay;
+	}
+	
+	public boolean getRunning()
+	{
+		return _running;
+	}
+	
+	/**
+	 * Constructor of L2NpcWalker.<BR><BR>
+	 * 
+	 * @param set The StatsSet object to transfert data to the method
+	 * 
+	 */
+	
+	public L2NpcWalkerNode()
+	{
+		
+	}
+	
+	/**
+	 * Constructor of L2NpcWalker.<BR><BR>
+	 * 
+	 * @param set The StatsSet object to transfert data to the method
+	 * 
+	 */
+		
+	public L2NpcWalkerNode(StatsSet set)
+	{		
+		_npcId       = set.getInteger("npc_id");
+		_movePoint   = set.getString("move_point");
+		_chatText    = set.getString("chatText");
+		_moveX       = set.getInteger("move_x");
+		_moveX       = set.getInteger("move_y");
+		_moveX       = set.getInteger("move_z");
+		_delay       = set.getInteger("delay");
+	}
+}

+ 129 - 0
L2_GameServer_It/java/net/sf/l2j/gameserver/model/actor/instance/L2NpcWalkerInstance.java

@@ -0,0 +1,129 @@
+/* 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 2, 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+package net.sf.l2j.gameserver.model.actor.instance;
+
+import net.sf.l2j.Config;
+import net.sf.l2j.gameserver.ai.L2NpcWalkerAI;
+import net.sf.l2j.gameserver.ai.L2CharacterAI;
+import net.sf.l2j.gameserver.serverpackets.CreatureSay;
+import net.sf.l2j.gameserver.templates.L2NpcTemplate;
+import net.sf.l2j.gameserver.model.L2Character;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.util.Map;
+
+/**
+ * This class manages some npcs can walk in the city. <br>
+ * It inherits all methods from L2NpcInstance. <br><br>
+ *
+ * @original author Rayan RPG for L2Emu Project
+ * @since 819
+ */
+public class L2NpcWalkerInstance extends L2NpcInstance
+{
+	private static Log _log = LogFactory.getLog(L2NpcWalkerInstance.class.getName());
+
+	/**
+	 * Constructor of L2NpcWalkerInstance (use L2Character and L2NpcInstance constructor).<BR><BR>
+	 */
+	public L2NpcWalkerInstance(int objectId, L2NpcTemplate template)
+	{
+		super(objectId, template);
+		setAI(new L2NpcWalkerAI(new L2NpcWalkerAIAccessor()));
+	}
+
+	/**
+	 * AI can't be deattached, npc must move always with the same AI instance.
+	 * @param newAI AI to set for this L2NpcWalkerInstance
+	 */
+	public void setAI(L2CharacterAI newAI)
+	{
+		if(_ai == null)
+			super.setAI(newAI);
+	}
+
+	public void onSpawn()
+	{
+		
+		((L2NpcWalkerAI) getAI()).setHomeX(getX());
+		((L2NpcWalkerAI) getAI()).setHomeY(getY());
+		((L2NpcWalkerAI) getAI()).setHomeZ(getZ());
+	}
+
+	/**
+	 * Sends a chat to all _knowObjects
+	 * @param chat message to say
+	 */
+	public void broadcastChat(String chat)
+	{
+		Map<Integer, L2PcInstance> _knownPlayers = getKnownList().getKnownPlayers();
+
+		if(_knownPlayers == null)
+		{
+			if(Config.DEVELOPER)
+				_log.info("broadcastChat _players == null");
+			return;
+		}
+
+		//we send message to known players only!
+		if(_knownPlayers.size() > 0)
+		{
+			CreatureSay cs = new CreatureSay(getObjectId(), 0, getName(), chat);
+
+			//we interact and list players here
+			for(L2PcInstance players : _knownPlayers.values())
+
+				//finally send packet :D
+				players.sendPacket(cs);
+		}
+	}
+
+	/**
+	 * NPCs are immortal
+	 * @param i ignore it
+	 * @param attacker  ignore it
+	 * @param awake  ignore it
+	 */
+	public void reduceCurrentHp(double i, L2Character attacker, boolean awake)
+	{}
+
+	/**
+	 * NPCs are immortal
+	 * @param killer ignore it
+	 * @return false
+	 */
+	public boolean doDie(L2Character killer)
+	{
+		return false;
+	}
+
+	public L2CharacterAI getAI()
+	{
+		return  super.getAI();
+	}
+
+	private class L2NpcWalkerAIAccessor extends L2Character.AIAccessor
+	{
+		/**
+		 * AI can't be deattached.
+		 */
+		public void detachAI()
+		{}
+	}
+}

+ 14 - 3
L2_GameServer_It/java/net/sf/l2j/gameserver/model/actor/stat/CharStat.java

@@ -20,6 +20,8 @@ package net.sf.l2j.gameserver.model.actor.stat;
 import net.sf.l2j.Config;
 import net.sf.l2j.gameserver.model.L2Character;
 import net.sf.l2j.gameserver.model.L2Skill;
+import net.sf.l2j.gameserver.model.actor.instance.L2NpcWalkerInstance;
+import net.sf.l2j.gameserver.model.actor.instance.L2PcInstance;
 import net.sf.l2j.gameserver.skills.Calculator;
 import net.sf.l2j.gameserver.skills.Env;
 import net.sf.l2j.gameserver.skills.Stats;
@@ -540,7 +542,7 @@ public class CharStat
 
 		// err we should be adding TO the persons run speed
 		// not making it a constant
-		int val = (int) calcStat(Stats.RUN_SPEED, _activeChar.getTemplate().baseRunSpd, null, null);
+		int val =(int) calcStat(Stats.RUN_SPEED, _activeChar.getTemplate().baseRunSpd, null, null);
 
 		if (_activeChar.isFlying())
 		{
@@ -584,10 +586,19 @@ public class CharStat
 	/** Return the WalkSpeed (base+modifier) of the L2Character. */
 	public final int getWalkSpeed()
 	{
-    	if (_activeChar == null)
+    	
+		if (_activeChar == null)
     		return 1;
 
-		return (getRunSpeed() * 70) / 100;
+		if(_activeChar instanceof L2PcInstance )
+		{
+			return (getRunSpeed() * 70) / 100;	
+		}
+		else
+		{	
+			return (int) calcStat(Stats.WALK_SPEED, _activeChar.getTemplate().baseWalkSpd, null, null);
+		}
+		
 	}
 
 	/** Return the WIT of the L2Character (base+modifier). */

+ 1 - 0
L2_GameServer_It/java/net/sf/l2j/gameserver/skills/Stats.java

@@ -75,6 +75,7 @@ public enum Stats
 	// walk & escape speed are calculated proportionally,
 	// magic speed is a buff
 	RUN_SPEED 			("runSpd"),
+	WALK_SPEED			("walkSpd"),
 
 	//
 	// Player-only stats

+ 4 - 0
L2_GameServer_It/java/net/sf/l2j/gameserver/templates/L2CharTemplate.java

@@ -53,7 +53,9 @@ public class L2CharTemplate
 	public final int baseAtkRange;
 	public final int baseShldRate;
 	public final int baseCritRate;
+	public final int baseWalkSpd;
 	public final int baseRunSpd;
+
 	// SpecialStats
 	public final int baseBreath;
 	public final int baseAggression;
@@ -119,7 +121,9 @@ public class L2CharTemplate
 		baseAtkRange       = set.getInteger("baseAtkRange");
 		baseShldRate       = set.getInteger("baseShldRate");
 		baseCritRate       = set.getInteger("baseCritRate");
+		baseWalkSpd 	   = set.getInteger("baseWalkSpd");
 		baseRunSpd         = set.getInteger("baseRunSpd");
+		
 		// SpecialStats
 		baseBreath         = set.getInteger("baseBreath",         100);
 		baseAggression     = set.getInteger("baseAggression",     0);