All pastes #47116 Raw Edit

Stuff

public text v1 · immutable
#47116 ·published 2006-03-27 03:20 UTC
rendered paste body
Index: C:/workspace/c4/java/net/sf/l2j/gameserver/RecipeController.java
===================================================================
--- C:/workspace/c4/java/net/sf/l2j/gameserver/RecipeController.java	(revision 1680)
+++ C:/workspace/c4/java/net/sf/l2j/gameserver/RecipeController.java	(working copy)
@@ -35,6 +35,7 @@
 import net.sf.l2j.Config;
 import net.sf.l2j.gameserver.ClientScheduler;
 import net.sf.l2j.gameserver.GameTimeController;
+import net.sf.l2j.gameserver.idfactory.IdFactory;
 import net.sf.l2j.gameserver.lib.Rnd;
 import net.sf.l2j.gameserver.model.Inventory;
 import net.sf.l2j.gameserver.model.L2ItemInstance;
@@ -42,6 +43,7 @@
 import net.sf.l2j.gameserver.model.L2RecipeInstance;
 import net.sf.l2j.gameserver.model.L2RecipeList;
 import net.sf.l2j.gameserver.model.L2Skill;
+import net.sf.l2j.gameserver.model.L2World;
 import net.sf.l2j.gameserver.model.actor.instance.L2PcInstance;
 import net.sf.l2j.gameserver.serverpackets.ActionFailed;
 import net.sf.l2j.gameserver.serverpackets.ItemList;
@@ -159,7 +161,8 @@
 
 	public synchronized void requestMakeItemAbort(L2PcInstance player)
 	{
-		activeMakers.remove(player);
+		if(activeMakers!=null) activeMakers.remove(player);
+		player.isInCraftMode(false);
 	}
 
 	public synchronized void requestManufactureItem(L2PcInstance manufacturer, int recipeListId,
@@ -170,21 +173,23 @@
 		if (recipeList == null) return;
 		manufacturer.isInCraftMode(true);
 
-		if (!Config.ALT_GAME_CREATION)
+		RecipeItemMaker maker;
+
+		if (Config.ALT_GAME_CREATION && (maker = activeMakers.get(manufacturer)) != null) // check if busy
 		{
-			new RecipeItemMaker(manufacturer, recipeList, player);
+			player.sendMessage("Manufacturer is busy, please try later.");
+			manufacturer.isInCraftMode(false);
+			return;
 		}
-		else
+		
+		maker = new RecipeItemMaker(manufacturer, recipeList, player);
+		if(maker.isValid)
 		{
-			RecipeItemMaker maker = activeMakers.get(manufacturer);
-			if (maker != null)
+			if (Config.ALT_GAME_CREATION) 
 			{
-				player.sendMessage("Manufacturer is busy creating.");
-				manufacturer.isInCraftMode(false);
-				return;
-			}
-			maker = new RecipeItemMaker(manufacturer, recipeList, player);
-			if (maker.isValid) activeMakers.put(manufacturer, maker);
+				activeMakers.put(manufacturer, maker);
+				ClientScheduler.scheduleEvent(maker, 100);
+			}	else maker.run(); 
 		}
 	}
 
@@ -192,28 +197,34 @@
 	{
 		L2RecipeList recipeList = getValidRecipeList(player, recipeListId);
 
-		if (recipeList == null) return;
-
+		if (recipeList == null)
+		{
+			_log.warning("recipelist == null");
+			return;
+		}
+		
 		player.isInCraftMode(true);
 
-		if (!Config.ALT_GAME_CREATION)
+		RecipeItemMaker maker;
+
+		if (Config.ALT_GAME_CREATION && ((maker = activeMakers.get(player)) != null)) // already have something cooking (possible in alt mode only)
 		{
-			new RecipeItemMaker(player, recipeList, player);
+			SystemMessage sm = new SystemMessage(614);
+			sm.addString("You are busy creating ");
+			sm.addItemName(recipeList.getItemId());
+			player.sendPacket(sm);
+			player.isInCraftMode(false);
+			return;
 		}
-		else
+
+		maker = new RecipeItemMaker(player, recipeList, player);
+		if(maker.isValid)
 		{
-			RecipeItemMaker maker = activeMakers.get(player);
-			if (maker != null)
+			if (Config.ALT_GAME_CREATION)
 			{
-				SystemMessage sm = new SystemMessage(614);
-				sm.addString("You are busy creating ");
-				sm.addItemName(recipeList.getItemId());
-				player.sendPacket(sm);
-				player.isInCraftMode(false);
-				return;
-			}
-			maker = new RecipeItemMaker(player, recipeList, player);
-			if (maker.isValid) activeMakers.put(player, maker);
+				activeMakers.put(player, maker);
+				ClientScheduler.scheduleEvent(maker, 100);
+			} else maker.run();
 		}
 	}
 
@@ -234,7 +245,7 @@
 			else if (recipeTypeString.equalsIgnoreCase("common")) isDwarvenRecipe = false;
 			else
 			{ //prints a helpfull message 
-				_log.warning("Error parsing recipes.csv, unknow recipe type " + recipeTypeString);
+				_log.warning("Error parsing recipes.csv, unknown recipe type " + recipeTypeString);
 				return;
 			}
 
@@ -281,118 +292,174 @@
 	public class RecipeItemMaker implements Runnable
 	{
 		public boolean isValid;
-		final List<L2ItemInstance> items;
-		int materialsRefPrice;
-		final L2PcInstance player;
+		List<L2ItemInstance> items = null;
+		final L2RecipeList recipeList;
+		final L2PcInstance player;		// "crafter"
+		final L2PcInstance target;		// "customer"		
+		final L2Skill skill;
+		final int skillId;
+		final int skillLevel;
+		int creationPasses;
 		int price;
-		final L2RecipeList recipeList;
-		final L2PcInstance target;
-		int timeReady;
 		int delay;
-		int gameTicks;
 		int totalItems;
-		int usedItems;
-		int skillLevel;
-		
+		int materialsRefPrice;
+		double manaRequired;
+
 		public RecipeItemMaker(L2PcInstance player, L2RecipeList recipeList, L2PcInstance target)
 		{
 			this.player = player;
 			this.target = target;
 			this.recipeList = recipeList;
-			this.items = new FastList<L2ItemInstance>();
 			this.isValid = false;
+			this.skillId = recipeList.isDwarvenRecipe()	? L2Skill.SKILL_CREATE_DWARVEN
+														: L2Skill.SKILL_CREATE_COMMON;
+			this.skillLevel = player.getSkillLevel(skillId);
+			this.skill = player.getKnownSkill(skillId);
 
-			skillLevel = recipeList.isDwarvenRecipe()	? player.getSkillLevel(L2Skill.SKILL_CREATE_DWARVEN)
-														: player.getSkillLevel(L2Skill.SKILL_CREATE_COMMON);
+			// validate recipe list
+			if ((recipeList == null) || (recipeList.getRecipes().length == 0))
+			{
+				player.sendMessage("No such recipe");
+				player.sendPacket(new ActionFailed());
+				abort(false);
+				return;
+			}
 
-			if (!validateSkillLevel()) return;
-			if (!validateRecipeList()) return;
-			if (!validateCustomerCanAfford(target,player)) return; 
-			if (!validateMaterials()) return;
-			if (!validateMp()) return;
+			this.manaRequired = this.recipeList.getMpCost();
+			
+			// validate skill level
+			if (this.recipeList.getLevel() > skillLevel)
+			{
+				player.sendMessage("Need skill level " + this.recipeList.getLevel());
+				player.sendPacket(new ActionFailed());
+				abort(false);
+				return;
+			}
 
-			isValid = true;
-			updateTime();
-			run();
-		}
-
-		private void finishCrafting()
-		{
-			player.reduceCurrentMp(recipeList.getMpCost());
-
-			if (Rnd.get(100) < recipeList.getSuccessRate())
+			// check that customer can afford to pay for creation services
+			if (player != target)
 			{
-				if (target != player)
-				{
-					if (price > 0)
+				for (L2ManufactureItem temp : player.getCreateList().getList())
+					if (temp.getRecipeId() == this.recipeList.getId()) // find recipe for item we want manufactured
 					{
-						target.reduceAdena("Manufacture", price, player, false);
-						player.addAdena("Manufacture", price, target, false);
+						price=temp.getCost();
+						if (target.getAdena() < price) // check price
+						{
+							target.sendPacket(new SystemMessage(SystemMessage.YOU_NOT_ENOUGH_ADENA));
+							abort(false);
+							return;
+						}
+						break;
 					}
-					updateCurLoad();
-				}
-
-				RewardPlayer();
-				updateMakeInfo(true); // success
 			}
-			else
+			
+			// make temporary items
+			if ((items = getItems(false)) == null) return; 
+			
+			// calculate reference price
+			for (L2ItemInstance i : items)
 			{
-				player.sendMessage("Item(s) failed to create");
-				updateMakeInfo(false);
+				materialsRefPrice += i.getReferencePrice() * i.getCount();
+				totalItems += i.getCount();
 			}
-			activeMakers.remove(player);
-			player.isInCraftMode(false);
+			
+			if(player.getCurrentMp()<manaRequired)
+			{
+				target.sendPacket(new SystemMessage(SystemMessage.NOT_ENOUGH_MP));
+				abort(false); 
+				return;
+			}
 
-			target.sendPacket(new ItemList(target, false));
+			// determine number of creation passes needed
+			// can "equip"  skillLevel items each pass
+			creationPasses = (totalItems / skillLevel ); //+((totalItems % skillLevel)!=0 ? 1 : 0); 
+
+			if (Config.ALT_GAME_CREATION) 		// update mana required to "per pass"
+				manaRequired /= (double)creationPasses;	// checks to validateMp() will only need portion of mp for one pass
+			
+			this.isValid = true;
 		}
 
 		public void run()
 		{
 			player.isInCraftMode(true);
-			gameTicks = GameTimeController.getGameTicks();
-			if (!validateCraftingEnabled() || !validateMp()) return;
 
-			updateTime();
+			if (!Config.IS_CRAFTING_ENABLED)
+			{
+				target.sendMessage("Crafting is currently disabled.");
+				abort(false);
+				return;
+			}
+			
+			if(Config.ALT_GAME_CREATION && activeMakers.get(player)==null) 
+			{ 
+				player.sendMessage("Crafting aborted"); 
+				abort(true); 
+			};
 
+			// check mana
+			if (!validateMp()) return;
+			
+			// divided by RATE_CONSUMABLES_COST to remove craft time increase on higher consumable rates (more mats consumed -> more time)
+			delay = (int) (Config.ALT_GAME_CREATION_SPEED * player.getMReuseRate(skill)
+				* GameTimeController.TICKS_PER_SECOND / Config.RATE_CONSUMABLE_COST)
+				* GameTimeController.MILLIS_IN_TICK;
+
 			if (Config.ALT_GAME_CREATION && !items.isEmpty())
 			{
-				player.reduceCurrentMp(recipeList.getMpCost()); // use some mp
+				player.reduceCurrentMp(manaRequired); // use some mp
 				grabSomeItems(); // grab (equip) some more items with a nice msg to player
 
-				MagicSkillUser msk = new MagicSkillUser(
-														player,
-														recipeList.isDwarvenRecipe() ? L2Skill.SKILL_CREATE_DWARVEN
-																					: L2Skill.SKILL_CREATE_COMMON,
-														skillLevel, delay, 0);
-
+				// FIXME: this is not working ...  this should be animation of crafting skill ?? or what ?
+				MagicSkillUser msk = new MagicSkillUser(player, skillId, skillLevel, delay, 0);
 				player.broadcastPacket(msk);
 
 				player.sendPacket(new SetupGauge(0, delay));
 
-				ClientScheduler.getInstance().scheduleLow(this, 100 + delay);
+				ClientScheduler.scheduleEvent(this, 100 + delay);
 			}
-			else finishCrafting();
+			else  // finish crafting 
+			{
+				player.reduceCurrentMp(manaRequired);
 
+				if (Rnd.get(100) < recipeList.getSuccessRate())
+				{
+					if (target != player) // customer must pay for services
+					{
+						if (price > 0)
+						{
+							target.transferItem("PayForCrafting",
+												target.getInventory().getAdenaInstance().getObjectId(),
+												price, player.getInventory(), null);
+						}
+						updateCurLoad();
+					}
+
+					if ((items = getItems(true)) != null) // this line actually takes materials from inventory
+					RewardPlayer(); // and this one immediately puts created item in its place
+					else updateMakeInfo(false);
+				}
+				else
+				{
+					player.sendMessage("Item(s) failed to create");
+					updateMakeInfo(false);
+				}
+				if (Config.ALT_GAME_CREATION) activeMakers.remove(player);
+				player.isInCraftMode(false);
+				target.sendPacket(new ItemList(target, false));
+			}
+
 			// update load and mana bar of craft window
 			updateMakeInfo(true);
 			updateCurMp();
 			updateCurLoad();
 		}
 
-		private void updateTime()
-		{
-			// divided by RATE_CONSUMABLES_COST to remove craft time increase on higher consumable rates (more mats consumed -> more time)
-			timeReady = gameTicks
-				+ (int) (Config.ALT_GAME_CREATION_SPEED
-					* player.getMReuseRate(player.getKnownSkill(172))
-					* GameTimeController.TICKS_PER_SECOND / Config.RATE_CONSUMABLE_COST);
-
-			delay = (timeReady - gameTicks) * GameTimeController.MILLIS_IN_TICK;
-		}
-
 		private void updateMakeInfo(boolean success)
 		{
-			if (target == player) target.sendPacket(new RecipeItemMakeInfo(recipeList.getId(), target, success));
+			if (target == player) target.sendPacket(new RecipeItemMakeInfo(recipeList.getId(), target,
+																			success));
 			else target.sendPacket(new RecipeShopItemInfo(player.getObjectId(), recipeList.getId()));
 		}
 
@@ -413,7 +480,7 @@
 
 		private void grabSomeItems()
 		{
-			int numItems = player.getSkillLevel(172);
+			int numItems = skillLevel;
 			while (numItems > 0 && !items.isEmpty())
 			{
 				L2ItemInstance item = items.get(0);
@@ -423,135 +490,99 @@
 
 				item.setCount(item.getCount() - count);
 				if (item.getCount() <= 0) items.remove(0);
+				else items.set(0, item);
 
 				numItems -= count;
-				usedItems += count;
 
 				SystemMessage sm = new SystemMessage(368); // you equipped ...
 				sm.addNumber(count);
 				sm.addItemName(item.getItemId());
-				if (target == null) player.sendPacket(sm);
-				else target.sendPacket(sm);
+				// FIXME: should not send "you equipped ... " message to customer so I improvised
+				if (target == player) player.sendPacket(sm);
+				else target.sendMessage("Manufacturer " + player.getName() + " used " + count + " "
+					+ item.getItem().getName());
 			}
 		}
 
-		private boolean validateCraftingEnabled()
+		private boolean validateMp()
 		{
-			if (!Config.IS_CRAFTING_ENABLED)
+			if (player.getCurrentMp() < manaRequired)
 			{
-				target.sendMessage("Crafting is currently disabled.");
-				player.isInCraftMode(false);
-				activeMakers.remove(player);
-				updateMakeInfo(false);
-				return false;
-			}
-			return true;
-		}
-
-		private boolean validateCustomerCanAfford(L2PcInstance customer, L2PcInstance manufacturer)
-		{
-			if(customer==manufacturer) return true;
-			for (L2ManufactureItem temp : manufacturer.getCreateList().getList())
-				if (temp.getRecipeId() == recipeList.getId()) // find recipe for item we want manufactured
+				// rest (wait for MP)
+				if (Config.ALT_GAME_CREATION)
 				{
-					int price = temp.getCost();
-					if (customer.getAdena() < price) // check price
-					{
-						customer.sendPacket(new SystemMessage(SystemMessage.YOU_NOT_ENOUGH_ADENA));
-						manufacturer.isInCraftMode(false);
-						activeMakers.remove(manufacturer);
-						return false;
-					}
-					break;
+					player.sendPacket(new SetupGauge(0, delay));
+					ClientScheduler.scheduleEvent(this, 100 + delay);
+					abort(true); // true == can resume
 				}
-			return true;
-		}		
-		private boolean validateMp()
-		{
-			if (player.getCurrentMp() < recipeList.getMpCost())
-			{
-				target.sendPacket(new SystemMessage(SystemMessage.NOT_ENOUGH_MP));
-				player.isInCraftMode(false);
-				activeMakers.remove(player);
-				updateMakeInfo(false);
+				else
+				// no rest  - report no mana
+				{
+					target.sendPacket(new SystemMessage(SystemMessage.NOT_ENOUGH_MP));
+					abort(false); // false == don't resume
+				}
 				return false;
 			}
 			return true;
 		}
 
-		private boolean validateMaterials()
+		private List<L2ItemInstance> getItems(boolean remove)
 		{
 			L2RecipeInstance[] recipes = recipeList.getRecipes();
-
 			Inventory inv = target.getInventory();
+			List<L2ItemInstance> materials = new FastList<L2ItemInstance>();
 
 			for (L2RecipeInstance recipe : recipes)
 			{
-				if (recipe.getQuantity() != 0)
+				int quantity = (int) recipe.getQuantity();
+				if (recipeList.isConsumable())
+					quantity = (int) (quantity * Config.RATE_CONSUMABLE_COST);
+
+				if (quantity > 0)
 				{
-					L2ItemInstance invItem = inv.getItemByItemId(recipe.getItemId());
-					if ((invItem == null)
-						|| ((recipeList.isConsumable()	? (int) (recipe.getQuantity() * Config.RATE_CONSUMABLE_COST)
-														: recipe.getQuantity()) > invItem.getCount()))
+					L2ItemInstance item = inv.getItemByItemId(recipe.getItemId());
+					int old_quantity = item.getCount(); // remember old quantity
+
+					// now we verify that we have enough materials
+					if (item.getCount() < quantity)
 					{
 						target.sendMessage("You dont have the right elements for making this item"
 							+ ((recipeList.isConsumable() && Config.RATE_CONSUMABLE_COST != 1)	? ".\nDue to server rates you need "
 																									+ Config.RATE_CONSUMABLE_COST
 																									+ "x more material than listed in recipe"
 																								: ""));
-						player.isInCraftMode(false);
-						activeMakers.remove(player);
-						updateMakeInfo(false);
-						return false;
+						abort(false);
+						return null;
 					}
-				}
-			}
 
-			for (L2RecipeInstance recipe : recipes)
-			{
-				if (recipe.getQuantity() != 0)
-				{
-					L2ItemInstance invItem = inv.getItemByItemId(recipe.getItemId());
-					invItem = inv.dropItem(
-											"Manufacture",
-											invItem.getObjectId(),
-											recipeList.isConsumable()	? (int) (recipe.getQuantity() * Config.RATE_CONSUMABLE_COST)
-																		: recipe.getQuantity(), target,
-											player);
-					items.add(invItem);
-					materialsRefPrice += invItem.getReferencePrice() * invItem.getCount();
-					totalItems += invItem.getCount();
+					// make new temporary object, just for counting puroses, real item modification only
+					// on finishCrafting() so we minimize risk of losing items
+
+					L2ItemInstance temp = new L2ItemInstance(IdFactory.getInstance().getNextId(),
+															item.getItem().getItemId());
+					temp.setCount("TemporaryCraftObject", quantity, target, player);
+					materials.add(temp); 
+
+					if (remove) item.setCount("Manufacture", old_quantity - quantity, target, player);
 				}
 			}
-
-			return true;
+			return materials;
 		}
 
-		private boolean validateSkillLevel()
+		private void abort(boolean resume)
 		{
-			if (recipeList.getLevel() > skillLevel)
+
+			if (resume)
 			{
-				player.sendMessage("Need skill level " + recipeList.getLevel());
-				player.sendPacket(new ActionFailed());
-				player.isInCraftMode(false);
-				activeMakers.remove(player);
-				updateMakeInfo(false);
-				return false;
+				// will continue later, run() is scheduled to run in 100+delay miliseconds so no worry
+				// about blocking other things ...  requestMakeItemAbort will interrupt this properly ?
 			}
-			return true;
-		}
-
-		private boolean validateRecipeList()
-		{
-			if ((recipeList == null) || (recipeList.getRecipes().length == 0))
+			else
 			{
-				player.sendMessage("No such recipe");
+				updateMakeInfo(false);
+				activeMakers.remove(player);
 				player.isInCraftMode(false);
-				activeMakers.remove(player);
-				updateMakeInfo(false);
-				return false;
 			}
-			return true;
 		}
 
 		private void RewardPlayer()
@@ -561,35 +592,48 @@
 
 			L2ItemInstance createdItem = target.getInventory().addItem("Manufacture", itemId, itemCount,
 																		target, player);
+
+			// inform customer of earned item
 			SystemMessage sm = new SystemMessage(SystemMessage.EARNED_ITEM);
 			sm.addItemName(itemId);
 			sm.addNumber(itemCount);
 			target.sendPacket(sm);
 
+			// inform manufacturer of earned profit
+			sm = new SystemMessage(SystemMessage.EARNED_ADENA);
+			sm.addNumber(price);
+			player.sendPacket(sm);
+			
 			if (Config.ALT_GAME_CREATION)
 			{
 				int recipeLevel = recipeList.getLevel();
 				int exp = createdItem.getReferencePrice() * itemCount;
 				// one variation
 				// exp -= materialsRefPrice;
-				
+
 				if (exp < 0) exp = 0;
-				
+
 				// another variation
 				exp /= recipeLevel;
-				for (int i = skillLevel; i > recipeLevel; i--) exp /= 4;
-				
+				for (int i = skillLevel; i > recipeLevel; i--)
+					exp /= 4;
+
 				int sp = exp / 10;
-				player.addExpAndSp((int) player.calcStat(Stats.EXPSP_RATE, exp * Config.RATE_XP, null,
-															null),
-									(int) player.calcStat(Stats.EXPSP_RATE, sp * Config.RATE_SP, null,
-															null));
 
-				sm = new SystemMessage(SystemMessage.YOU_EARNED_S1_EXP_AND_S2_SP);
-				sm.addNumber((int) player.calcStat(Stats.EXPSP_RATE, exp * Config.RATE_XP, null, null));
-				sm.addNumber((int) player.calcStat(Stats.EXPSP_RATE, sp * Config.RATE_SP, null, null));
-				player.sendPacket(sm);
+				// FIXME: Added multiplication of Creation speed with XP/SP gained ...
+				// so slower crafting -> more XP,  faster crafting -> less XP 
+				// admins:  find balance you like ... you can use ALT_GAME_CREATION_RATE_XP/SP to
+				// compensate XP/SP (default = 1)   my goal is to make ALT_GAME_CREATION
+				// a *bit* slower,  a bit more interesting, and give some xp/sp but not too much
+				// comments on this would be welcome.
+
+				player.addExpAndSp((int) player.calcStat(Stats.EXPSP_RATE, exp * Config.RATE_XP
+					* Config.ALT_GAME_CREATION_XP_RATE * Config.ALT_GAME_CREATION_SPEED, null, null),
+									(int) player.calcStat(Stats.EXPSP_RATE, sp * Config.RATE_SP
+										* Config.ALT_GAME_CREATION_SP_RATE
+										* Config.ALT_GAME_CREATION_SPEED, null, null));
 			}
+			updateMakeInfo(true); // success
 		}
 	}
 
@@ -605,7 +649,5 @@
 		}
 		return recipeList;
 	};
-	
 
 }
-