All pastes #44883 Raw Edit

Untitled

public text v1 · immutable
#44883 ·published 2006-03-08 15:02 UTC
rendered paste body
/*
 * 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;
/////
////
///
//     This file is now overcomented to explain all the changes
//     in detail. Feel free to remove them later
///
////
/////
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.LineNumberReader;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.StringTokenizer;
import java.util.WeakHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;

import javolution.util.FastList;
import javolution.util.FastMap;
import net.sf.l2j.Config;
import net.sf.l2j.gameserver.model.Inventory;
import net.sf.l2j.gameserver.model.L2ItemInstance;
import net.sf.l2j.gameserver.model.L2ManufactureItem;
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.actor.instance.L2PcInstance;
import net.sf.l2j.gameserver.serverpackets.ActionFailed;
import net.sf.l2j.gameserver.serverpackets.InventoryUpdate;
import net.sf.l2j.gameserver.serverpackets.ItemList;
import net.sf.l2j.gameserver.serverpackets.MagicSkillUser;
import net.sf.l2j.gameserver.serverpackets.RecipeBookItemList;
import net.sf.l2j.gameserver.serverpackets.RecipeItemMakeInfo;
import net.sf.l2j.gameserver.serverpackets.RecipeShopItemInfo;
import net.sf.l2j.gameserver.serverpackets.SetupGauge;
import net.sf.l2j.gameserver.serverpackets.StatusUpdate;
import net.sf.l2j.gameserver.serverpackets.SystemMessage;
import net.sf.l2j.gameserver.skills.Stats;


public class RecipeController
{
    protected static Logger _log = Logger.getLogger(RecipeController.class.getName());
    private static RecipeController _instance;
    
    private Map<Integer, L2RecipeList> _lists;
    
	protected static Map<L2PcInstance, RecipeItemMaker> activeMakers = Collections.synchronizedMap(new WeakHashMap<L2PcInstance,RecipeItemMaker>());
	
    protected static final Random _rnd = new Random();
    
    public static RecipeController getInstance()
    {
        if (_instance == null)
        {
            _instance = new RecipeController();
        }
        return _instance;
    }
    
    public RecipeController()
    {
        _lists = new FastMap<Integer, L2RecipeList>();
        String line = null;
        LineNumberReader lnr = null;
        
        try
        {
            File recipesData = new File(Config.DATAPACK_ROOT, "data/recipes.csv");
            lnr = new LineNumberReader(new BufferedReader( new FileReader(recipesData)));
            
            while ((line=lnr.readLine()) != null)
            {
                if (line.trim().length() == 0 || line.startsWith("#"))
                {
                    continue;
                }
                
                parseList(line);
                
            }
            _log.config("RecipeController: Loaded "+_lists.size()+" Recipes.");
        }
        catch (Exception e)
        {
            if (lnr != null)
            {
                _log.log(Level.WARNING, "error while creating recipe controller in linenr: "+lnr.getLineNumber(), e);
            }
            else
            {
                _log.warning("No recipes were found in data folder");
            }
        }
        finally
        {
            try { lnr.close(); } catch (Exception e) { }
        }
    }
    
    public int getRecipesCount()
    {
        return _lists.size();
    }
    public L2RecipeList getRecipeList(int listId)
    {
        return _lists.get(listId);
    }
    public L2RecipeList getRecipeByItemId(int itemId)
    {
        for (int i = 0; i < _lists.size(); i++)
        {
            L2RecipeList find = _lists.get(new Integer(i));
            if (find.getRecipeId() == itemId)
            {
                return find;
            }
        }
        return null;
    }
 
   
    public synchronized void requestBookOpen(L2PcInstance player, boolean isDwarvenCraft)
    {                       
        RecipeItemMaker maker=null;

        // @@todo: this must be removed, but also made sure it will happen only if realy crafting 
        if (Config.ALT_GAME_CREATION) 
        {
            maker = activeMakers.get(player);
            if(!player.isInCraftMode() && maker!=null) 
            {
                activeMakers.remove(player);
                if (Config.DEBUG) 
                    _log.fine("removed "+player.getName()+" from activeMakers by force, trust only isInCraftMode for acurate state");
            }
        }
        
        if (maker == null) {
            RecipeBookItemList response = new RecipeBookItemList(isDwarvenCraft,player.getMaxMp());
		 	if (isDwarvenCraft) 
		 		response.addRecipes(player.getDwarvenRecipeBook()); 
		 	else 
		 		response.addRecipes(player.getCommonRecipeBook()); 
            player.sendPacket(response);
            return;
        }
        
        if (Config.DEBUG) _log.fine("requestBookOpen:  Busy creating");
        SystemMessage sm = new SystemMessage(SystemMessage.CANT_ALTER_RECIPEBOOK_WHILE_CRAFTING);
        sm.addItemName(maker.recipeList.getItemId());
        player.sendPacket(sm);
        return;
    }
    
    public synchronized void requestManufactureItem(L2PcInstance manufacturer, int recipeListId, L2PcInstance customer)
    {
        L2RecipeList recipeList = getRecipeList(recipeListId-1);
        SystemMessage sm = new SystemMessage(614);
        
        if(manufacturer.isInCraftMode()) 
            {
                if(Config.DEBUG) _log.fine("requestManufactureItem called but manufacturer is in craft mode");
                sm = new SystemMessage(614); 
                sm.addString("Manufacturer is busy.");
                sm.addItemName(recipeList.getItemId());
                customer.sendPacket(sm);
                return;
            }
        
        if ((recipeList == null) || (recipeList.getRecipes().length == 0))
        {
            _log.warning("I bet this can never happen normally, "+
                         "cause customer can order only listed recipes, which are 100% in manufacturer's recipe book");
            
            sm = new SystemMessage(614); 
            sm.addString("No recipe for: " + recipeListId);
            customer.sendPacket(sm);
            manufacturer.isInCraftMode(false);
            return;
        }

        if (Config.ALT_GAME_CREATION) 
        {
            RecipeItemMaker maker = activeMakers.get(manufacturer);
            if (maker != null)
            {
                if(!manufacturer.isInCraftMode())
                {
                    if(Config.DEBUG) 
                    {
                        _log.fine(maker.manufacturer.getName() + " is in activeMakers but not IsInCraftmode");
                        _log.fine("decision: isincraftmode() is right, activeMakers wrong, removing from activeMakers");
                    }
                    activeMakers.remove(manufacturer);
                } else
                {
                    sm = new SystemMessage(614); 
                    sm.addString("Manufacturer is busy.");
                    sm.addItemName(recipeList.getItemId());
                    customer.sendPacket(sm);
                    manufacturer.isInCraftMode(false);
                    return;
                }
            }
            
            maker = new RecipeItemMaker(manufacturer, recipeList, customer);
            
            if (maker!=null && manufacturer!=null && maker.isValid)
                activeMakers.put(manufacturer, maker);
        } else // for regular crafting we don't use activeMakers
            new RecipeItemMaker(manufacturer, recipeList, customer);
    }
    public synchronized void requestMakeItem(L2PcInstance manufacturer, int recipeListId)
    {
        L2RecipeList recipeList = getRecipeList(recipeListId-1);
        
        SystemMessage sm = new SystemMessage(614);
        
        if ((recipeList == null) || (recipeList.getRecipes().length == 0))
        {
            sm = new SystemMessage(614); 
            sm.addString("No recipe for: " + recipeListId);
            manufacturer.sendPacket(sm);
            return;
        }

        if(manufacturer.isInCraftMode()) 
        {
            sm = new SystemMessage(614); 
            sm.addString("You are busy creating ");
            sm.addItemName(recipeList.getItemId());
            manufacturer.sendPacket(sm);
            return;
        } 
        
        if (Config.ALT_GAME_CREATION) 
        {
            RecipeItemMaker maker = activeMakers.get(manufacturer);
            if (maker != null)
                activeMakers.remove(manufacturer);
            
            maker = new RecipeItemMaker(manufacturer, recipeList, null);
            
            if (manufacturer!=null && maker!=null && maker.isValid)
                activeMakers.put(manufacturer, maker);
        } else
        {
            new RecipeItemMaker(manufacturer, recipeList, null);
        }
    }
    public synchronized void requestMakeItemAbort(L2PcInstance manufacturer)
    {
        manufacturer.isInCraftMode(false);
        if (Config.DEBUG) _log.fine(manufacturer.getName()+" IsInCraftMode(false)");

        if(Config.ALT_GAME_CREATION && activeMakers!=null) 
        {
            if(activeMakers.get(manufacturer)!=null) activeMakers.remove(manufacturer);
            if (Config.DEBUG) 
                _log.fine("requestMakeItemAbort called - removing " +manufacturer.getName() +" from activeMakers ");
        }
    }
    
	private void parseList(String line)
	{
		StringTokenizer st = new StringTokenizer(line,";");
		List<L2RecipeInstance> recipePartList = new FastList<L2RecipeInstance>();
		
        //we use common/dwarf for easy reading of the recipes.csv file 
	 	String recipeTypeString = st.nextToken(); 
	 	
	 	//now we parse the string into a boolean 
	 	boolean isDwarvenRecipe; 
	 	if (recipeTypeString.equalsIgnoreCase("dwarven")) 
	 		isDwarvenRecipe = true; 
	 	else if (recipeTypeString.equalsIgnoreCase("common")) 
	 		isDwarvenRecipe = false; 
	 	else 
	 	{
	 		//prints a helpfull message 
	 		_log.warning("Error parsing recipes.csv, unknow recipe type "+recipeTypeString); 
	 		return; 
	 	}
		
		String recipeName = st.nextToken();
		int id = Integer.parseInt(st.nextToken());
		int recipeId = Integer.parseInt(st.nextToken());
		int level = Integer.parseInt(st.nextToken());
		
		//material
		StringTokenizer st2 = new StringTokenizer(st.nextToken(), "[],");
		while (st2.hasMoreTokens())
		{
			StringTokenizer st3 = new StringTokenizer(st2.nextToken(), "()");
			int rpItemId = Integer.parseInt(st3.nextToken());
			int quantity = Integer.parseInt(st3.nextToken());
			L2RecipeInstance rp = new L2RecipeInstance(rpItemId, quantity);
			recipePartList.add(rp);
			//recipe.addRecipe(rp);
		}
		
		int itemId = Integer.parseInt(st.nextToken());
		int count = Integer.parseInt(st.nextToken());
		
		//npc fee
		/*String notdoneyet = */ st.nextToken();
		
		int mpCost = Integer.parseInt(st.nextToken());
		int successRate = Integer.parseInt(st.nextToken());
		
		L2RecipeList recipeList = new L2RecipeList(id,level,recipeId,recipeName,successRate,mpCost,itemId,count,isDwarvenRecipe);
		for (L2RecipeInstance recipePart : recipePartList)
		{
			recipeList.addRecipe(recipePart);
		}
		_lists.put(new Integer(_lists.size()), recipeList);
	}
    
    class RecipeItemMaker implements Runnable
    {
        int   timeReady;
        final L2PcInstance manufacturer;
        final L2PcInstance customer;
        final L2RecipeList recipeList;
        final List<L2ItemInstance> items;
        int   skillLevel;
        int   creationPasses;
        int   materialsRefPrice;
        int   totalItems;
        int   usedItems;
        int   price;
        boolean isValid;
        
        
        RecipeItemMaker(L2PcInstance manufacturer, L2RecipeList recipeList, L2PcInstance customer )
        {
            this.manufacturer = manufacturer;
            this.customer = customer;
            this.recipeList = recipeList;
            this.items = new FastList<L2ItemInstance>();
            if(recipeList.isDwarvenRecipe()) 
            	this.skillLevel = manufacturer.getSkillLevel(L2Skill.SKILL_CREATE_DWARVEN);
            else
            	this.skillLevel = manufacturer.getSkillLevel(L2Skill.SKILL_CREATE_COMMON);
            
            // crafting is disabled...
            if (!Config.IS_CRAFTING_ENABLED)
            {
                SystemMessage sm = new SystemMessage(614);
                sm.addString("Crafting is currently disabled.");
                if (customer == null)
                    manufacturer.sendPacket(sm);
                else
                    customer.sendPacket(sm);
                manufacturer.isInCraftMode(false);
                return;
            }
            
            if (manufacturer.isInCraftMode())
            {
                if(Config.DEBUG) _log.fine("manufacturer.isInCraftMode()==true, in RecipeItemMaker constructor");
                SystemMessage sm = new SystemMessage(614);
                
                if (customer == null)
                {
                    sm.addString("You are busy crafting");
                    manufacturer.sendPacket(sm);
                }
                else
                {
                    sm.addString("Manufacturer is busy crafting");
                    customer.sendPacket(sm);
                };
                return;
            }
            
            if (skillLevel <= 0)
            {
                manufacturer.sendPacket(new ActionFailed());
                manufacturer.isInCraftMode(false);
                
                if(customer!=null) 
                {
                    customer.sendPacket(new ActionFailed());
                    if(Config.DEBUG) 
                        _log.warning("skillLevel <= 0 and customer!=null, so why can manufacturer open manufacture shop if he has no skill?");
                }
                return;
            }
            
            if ((recipeList == null) || (recipeList.getRecipes().length == 0))
            {
                SystemMessage sm = new SystemMessage(614); 
                sm.addString("No such recipe");
                manufacturer.sendPacket(sm);

                if(customer!=null) 
                {
                    customer.sendPacket(sm);
                    if(Config.DEBUG) 
                        _log.warning("customer!=null, so why can manufacturer offer manufacture for item if he has no recipe ?");
                }

                return;
            }
            
            if (customer != null)
            {
                for (L2ManufactureItem temp : manufacturer.getCreateList().getList())
                    if (temp.getRecipeId() == recipeList.getId())
                    {
                        price = temp.getCost();
                        if (customer.getAdena() < price)
                            {
                                customer.sendPacket(new SystemMessage(SystemMessage.YOU_NOT_ENOUGH_ADENA));
                                return;
                            }
                        break;
                }
            }
            
            if (recipeList.getLevel()>skillLevel)
            {
                SystemMessage sm = new SystemMessage(614); 
                sm.addString("Need skill level "+recipeList.getLevel());
                manufacturer.sendPacket(sm);
                return;
            }
            
            L2RecipeInstance[] recipes = recipeList.getRecipes();
            
            Inventory inv = null;
            
            if (customer == null)
                inv = manufacturer.getInventory();
            else
                inv = customer.getInventory();
            
            for (L2RecipeInstance recipe : recipes)
            {
                if (recipe.getQuantity() != 0)
                {
                    L2ItemInstance invItem = inv.getItemByItemId(recipe.getItemId());
                    if ((invItem == null) || (recipe.getQuantity()>invItem.getCount()))
                    {
                        SystemMessage sm = new SystemMessage(614); 
                        sm.addString("You dont have the right elements for making this item");
                        
                        if (customer == null) manufacturer.sendPacket(sm);
                            else customer.sendPacket(sm);
                        
                        return;
                    }
                }
            }
            
            if (manufacturer.getCurrentMp() < recipeList.getMpCost())
            {
                if (Config.DEBUG) _log.fine("not enough mana in RecipeItemMaker constructor");
                SystemMessage sm = new SystemMessage(SystemMessage.NOT_ENOUGH_MP); 
                if (customer == null)
                    manufacturer.sendPacket(sm);
                else
                    customer.sendPacket(sm);

                return;
            }
            
            creationPasses=0;
            for (L2RecipeInstance recipe : recipes)
            {
                if (recipe.getQuantity() != 0)
                {
                    L2ItemInstance invItem = inv.getItemByItemId(recipe.getItemId());
                    invItem = inv.dropItem(invItem, recipe.getQuantity());
                    items.add(invItem);
                    materialsRefPrice += invItem.getReferencePrice()*invItem.getCount();
                    totalItems += invItem.getCount();
                    creationPasses+=invItem.getCount()/skillLevel;
                    if((invItem.getCount()%skillLevel)>0) creationPasses++;
                }
            }

            if(Config.DEBUG)
            {
                //_log.warning("calculated creation passes: "+creationPasses);
            
                if(creationPasses==0) 
                {
                    creationPasses++;
                    _log.warning("creationPasses was ZERO so creationPasses++  and it is now " + creationPasses);
                }
            }
            isValid = true; 
            
            manufacturer.isInCraftMode(true); // here we say craf mode started, no reason to do it sooner, just makes problems
            
            //ClientScheduler.scheduleCraftAction(this, 100);
            run();  // for not-alternative crafting -> no reason why not craft immediately, cause it uses really little cpu
                    // and will never make lag ... scheduled things tend to make lag, things that are immediately done don't if you 
                    // have sufficient cpu anyway ...  how long can craft take ? few nanoseconds ?
                    // and for ALT_CRAFTING=true,  first run is innitial, and takes no time also,  and on end, if needed, another run
                    // is scheduled
            
        }
        public void run()
        {
            if (!Config.ALT_GAME_CREATION || items.isEmpty())   // this method does the actual creation of item,
            {                                                   // it is called immediately for normal crafting,  
                completeCreation();                             // cause normal crafting does not take time, bug gives no XP/SP
                manufacturer.isInCraftMode(false);              // for ALT_CRAFTING it is called only when all items are "equiped"
                                                                // thru multiple scheduled runs using 
                                                                // ClientScheduler.ScheduleCraftAction()  - separate thread pool
                                                                // there should be no thread launching at all 

                if(Config.DEBUG) 
                {
                    //_log.warning("MP_Cost:  "+recipeList.getMpCost()+" ");
                    //_log.warning("passes:   "+creationPasses+" ");
                    //_log.warning("mana/pass:"+recipeList.getMpCost()/creationPasses+" ");
                    //_log.warning("error:    "+(recipeList.getMpCost()-((recipeList.getMpCost()/creationPasses)*creationPasses)));
                }
                
                return;
            }
            
            
            // all this code is for ALT_GAME_CREATION only
            // previous code was very confusing, which led
            // to "craft lag" due to misunderstanding of 
            // "crafting" in normal mode.
            // normal mode takes no time but gives no XP/SP
            // ALT_MODE takes time but gives XP/SP as reward
            // ---------------------------------------------
            // run() is scheduled multiple times, 
            // (equiping) skillLevel items on every run, until 
            // all items are "equiped", and then on next run,
            // completeCreation() will produce the result
            // and remove equiped  materials from inventory
            //  
            // on every run() part of mana is deducted from
            // manufacturer, amount is calculated as sum of 
            // all item counts from recipe.

            double currentManufacturerMp = manufacturer.getCurrentMp();          // we need player to have only mana needed 
            if (currentManufacturerMp < ((double)recipeList.getMpCost()/(double)creationPasses))                                                                     
            {                                                                     // for this pass, next pass is in 1 second
                if (Config.DEBUG) _log.warning("not enough mana in run()");      // so we can allow mp regen to help manufacturer
                SystemMessage sm = new SystemMessage(SystemMessage.NOT_ENOUGH_MP); 
                if (customer == null)
                    manufacturer.sendPacket(sm);
                else
                    customer.sendPacket(sm);
                
                manufacturer.isInCraftMode(false);
                activeMakers.remove(manufacturer);
                return;
            }
                
                
            manufacturer.reduceCurrentMp((double)recipeList.getMpCost()/(double)creationPasses);
            
            int numItems = skillLevel;
            
            while (numItems > 0 && !items.isEmpty())  // equip # of items we can in one run()  
            {                                         // if players complain they lose items during craft on server crashes, try
                L2ItemInstance item = items.get(0);   // not to item.remove(), but instead copy it somehow and return if crafting 
                int count = item.getCount();          // did not finish for some reason ?? umh don't know how it works really
                                                      // when is sql db updated ? all the time ? sometimes ?
                
                if (count >= numItems) count = numItems; 
                item.setCount(item.getCount() - count);
                if (item.getCount() <= 0) items.remove(0);
                numItems -= count;
                usedItems += count;
                        
                SystemMessage sm = new SystemMessage(368);
                sm.addNumber(count);
                sm.addItemName(item.getItemId());
                
                if (customer == null) manufacturer.sendPacket(sm);
                    else customer.sendPacket(sm);
            }
                    
            int delay = ((int)(manufacturer.getMReuseRate(manufacturer.getKnownSkill(172)) * GameTimeController.TICKS_PER_SECOND)) * GameTimeController.MILLIS_IN_TICK;

            MagicSkillUser msk = new MagicSkillUser(manufacturer, 1050, 1, delay, 0);
            manufacturer.broadcastPacket(msk);

            SetupGauge sg = new SetupGauge(0, delay);
            manufacturer.sendPacket(sg);                

            StatusUpdate su = new StatusUpdate(manufacturer.getObjectId());
            su.addAttribute(StatusUpdate.CUR_MP, (int) manufacturer.getCurrentMp());
            manufacturer.sendPacket(su);
//          // this works but it makes craft of level1 items very fast for alt mode ... uhm 
//          // I'd allow that, cause crafting "braided hemp" is not very fun and you need to do it
//          // in huge quantities, even if you are high level, so you get no XP/SP
//          // but it's not my call        
            if (items.isEmpty()) 
            {
                if(Config.DEBUG) _log.fine("saved "+delay+" miliseconds");
                completeCreation();
                manufacturer.isInCraftMode(false);
                activeMakers.remove(manufacturer);                
            } else
            ClientScheduler.scheduleEvent(this, 1+delay);  // Craft thread pool has nothing to do with other pools, so
                                                                 // only way this can make any kind of lag is when CPU usage is 100%
                                                                 // when nobody is "in craft mode"  scheduled tasks for 
                                                                 // craft thread pool must be 0 or we have a bug again :/
        }
        
        private void completeCreation()        // this is where real item production is done
        {  
            int createdItemCount=0;
            if(Config.DEBUG) 
                _log.fine(manufacturer.getName()+" : completeCreation() ");
            
            if(!Config.ALT_GAME_CREATION)       // we reduce Mp in run() for ALT_GAME_CREATION 
                manufacturer.reduceCurrentMp(recipeList.getMpCost());
            
            StatusUpdate su = new StatusUpdate(manufacturer.getObjectId());
            
            L2ItemInstance createdItem = ItemTable.getInstance().createItem(recipeList.getItemId());
            createdItem.setCount(recipeList.getCount());
            
            int randCheck = _rnd.nextInt(100);
            
            if(recipeList.getSuccessRate() <= randCheck ) 
            {
                SystemMessage sm = new SystemMessage(614);
                sm.addString("Item(s) failed to create");
                manufacturer.sendPacket(sm);
                manufacturer.isInCraftMode(false);
                if(Config.ALT_GAME_CREATION && activeMakers.get(manufacturer)!=null) activeMakers.remove(manufacturer);
            } else 
            {   // item succesfully created    
                createdItemCount = createdItem.getCount();
                SystemMessage sm = new SystemMessage(SystemMessage.EARNED_ITEM);
                sm.addItemName(createdItem.getItemId());
                sm.addNumber(createdItemCount);
                
                if (customer == null)
                {
                    manufacturer.getInventory().addItem(createdItem);
                    manufacturer.sendPacket(sm);
                    su.addAttribute(StatusUpdate.CUR_LOAD, manufacturer.getCurrentLoad());
                } else 
                {
                    if (price > 0) 
                    {
                        customer.reduceAdena(price);
                        manufacturer.addAdena(price);
                        L2ItemInstance adenaupdate = manufacturer.getInventory().findItemByItemId(57);
                        InventoryUpdate iu = new InventoryUpdate();
                        iu.addModifiedItem(adenaupdate);
                        manufacturer.sendPacket(iu);
                        SystemMessage sm2 = new SystemMessage(SystemMessage.DISSAPEARED_ADENA);
                        sm2.addNumber(price);
                        customer.sendPacket(sm2);
                        sm2 = new SystemMessage(SystemMessage.EARNED_ADENA);
                        sm2.addNumber(price);
                        manufacturer.sendPacket(sm2);
                    }
                
                    customer.getInventory().addItem(createdItem);
                    customer.sendPacket(sm);
                    StatusUpdate su2 = new StatusUpdate(customer.getObjectId());
                    su2.addAttribute(StatusUpdate.CUR_LOAD, customer.getCurrentLoad());
                    customer.sendPacket(su2);
                }
            }
                
            if (Config.ALT_GAME_CREATION)              
            {
                int RecipeLevel = recipeList.getLevel();
                int exp;
                int sp;
                
                    exp = createdItem.getReferencePrice()*createdItemCount;
                    //   ensure at least some profit
                    materialsRefPrice=(int)((double)materialsRefPrice*(double)0.95);
                    
                    // one variation
                    if(Config.DEBUG) 
                    {
                      _log.fine("BASED ON ITEM PRICE - MATERIALS PRICE");
                      _log.fine("createdItemCount: "+createdItemCount);
                      _log.fine("exp:              "+exp);
                      _log.fine("referent price:   "+createdItem.getReferencePrice());
                      _log.fine("referent total:   "+createdItem.getReferencePrice()*createdItemCount);
                      _log.fine("materials price:  "+materialsRefPrice);
                      _log.fine("exp:              "+(exp-materialsRefPrice));
                    }
                
                    exp -= materialsRefPrice;  

                    if (exp < 0) exp = 0;
    
                    exp/=RecipeLevel;
                    
                    for (int i=skillLevel; i > RecipeLevel; i--) exp /= 4;

                    sp = exp / 10;
                    
                    if(Config.DEBUG) _log.fine("exp with mods:    "+exp);
                    
                
                manufacturer.addExpAndSp((int)manufacturer.calcStat(Stats.EXPSP_RATE, exp * Config.RATE_XP, null, null), 
                                  (int)manufacturer.calcStat(Stats.EXPSP_RATE, sp * Config.RATE_SP, null, null));
            
                if(activeMakers.get(manufacturer)!=null) activeMakers.remove(manufacturer);
            }
            
            manufacturer.isInCraftMode(false);

            su.addAttribute(StatusUpdate.CUR_MP, (int) manufacturer.getCurrentMp());
            manufacturer.sendPacket(su);
            if (customer == null){
                manufacturer.sendPacket(new ItemList(manufacturer, false));
                manufacturer.sendPacket(new RecipeItemMakeInfo(recipeList.getId(),manufacturer));
            } else
            {
                customer.sendPacket(new ItemList(customer, false));
                customer.sendPacket(new RecipeShopItemInfo(manufacturer.getObjectId(),recipeList.getId()));
            }
        }  
    }
}