package com.planet_ink.coffee_mud.Abilities.Common;
import com.planet_ink.coffee_mud.core.interfaces.*;
import com.planet_ink.coffee_mud.core.*;
import com.planet_ink.coffee_mud.core.collections.*;
import com.planet_ink.coffee_mud.Abilities.interfaces.*;
import com.planet_ink.coffee_mud.Areas.interfaces.*;
import com.planet_ink.coffee_mud.Behaviors.interfaces.*;
import com.planet_ink.coffee_mud.CharClasses.interfaces.*;
import com.planet_ink.coffee_mud.Commands.interfaces.*;
import com.planet_ink.coffee_mud.Common.interfaces.*;
import com.planet_ink.coffee_mud.Common.interfaces.TimeClock.TimePeriod;
import com.planet_ink.coffee_mud.Exits.interfaces.*;
import com.planet_ink.coffee_mud.Items.interfaces.*;
import com.planet_ink.coffee_mud.Libraries.interfaces.*;
import com.planet_ink.coffee_mud.Locales.interfaces.*;
import com.planet_ink.coffee_mud.MOBS.interfaces.*;
import com.planet_ink.coffee_mud.Races.interfaces.*;
import java.util.*;
/*
Copyright 2003-2016 Bo Zimmerman
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
public class Merchant extends CommonSkill implements ShopKeeper
{
@Override
public String ID()
{
return "Merchant";
}
private final static String localizedName = CMLib.lang().L("Marketeering");
@Override
public String name()
{
return localizedName;
}
private static final String[] triggerStrings = I(new String[] { "MARKET" });
@Override
public String[] triggerStrings()
{
return triggerStrings;
}
@Override
public int overrideMana()
{
return 5;
}
@Override
public boolean isAutoInvoked()
{
return true;
}
@Override
public boolean canBeUninvoked()
{
return false;
}
@Override
protected ExpertiseLibrary.SkillCostDefinition getRawTrainingCost()
{
return CMProps.getNormalSkillGainCost(ID());
}
@Override
protected int canAffectCode()
{
return Ability.CAN_MOBS | Ability.CAN_ROOMS | Ability.CAN_EXITS | Ability.CAN_AREAS | Ability.CAN_ITEMS;
}
@Override
protected int canTargetCode()
{
return 0;
}
@Override
public int classificationCode()
{
return Ability.ACODE_COMMON_SKILL | Ability.DOMAIN_INFLUENTIAL;
}
protected CoffeeShop shop = ((CoffeeShop) CMClass.getCommon("DefaultCoffeeShop")).build(this);
private double[] devalueRate = null;
private long whatIsSoldMask = ShopKeeper.DEAL_ANYTHING;
private String prejudice = "";
private String ignore = "";
private MOB staticMOB = null;
private String[] pricingAdjustments = new String[0];
private Pair<Long,TimePeriod> budget = new Pair<Long,TimePeriod>(Long.valueOf(100000), TimePeriod.DAY);
public Merchant()
{
super();
displayText="";
isAutoInvoked();
}
@Override
public CoffeeShop getShop()
{
return shop;
}
@Override
public String text()
{
return shop.makeXML();
}
@Override
public String budget()
{
return budget == null ? "" : (budget.first + " " + budget.second.name());
}
@Override
public void setBudget(String factors)
{
budget = CMLib.coffeeShops().parseBudget(factors);
}
@Override
public String devalueRate()
{
return devalueRate == null ? "" : (devalueRate[0] + " "+devalueRate[1]);
}
@Override
public void setDevalueRate(String factors)
{
devalueRate = CMLib.coffeeShops().parseDevalueRate(factors);
}
@Override
public int invResetRate()
{
return 0;
}
@Override
public void setInvResetRate(int ticks)
{
}
@Override
public void setMiscText(String text)
{
synchronized(this)
{
shop.buildShopFromXML(text);
}
}
@Override
public void affectPhyStats(Physical E, PhyStats affectableStats)
{
if(E instanceof MOB)
affectableStats.setWeight(affectableStats.weight()+shop.totalStockWeight());
}
@Override
public boolean isSold(int mask)
{
if(mask==0)
return whatIsSoldMask==0;
if((whatIsSoldMask&255)==mask)
return true;
return CMath.bset(whatIsSoldMask>>8, CMath.pow(2,mask-1));
}
@Override
public void addSoldType(int mask)
{
if(mask==0)
whatIsSoldMask=0;
else
{
if((whatIsSoldMask>0)&&(whatIsSoldMask<256))
whatIsSoldMask=(CMath.pow(2,whatIsSoldMask-1)<<8);
for(int c=0;c<ShopKeeper.DEAL_CONFLICTS.length;c++)
{
for(int c1=0;c1<ShopKeeper.DEAL_CONFLICTS[c].length;c1++)
{
if(ShopKeeper.DEAL_CONFLICTS[c][c1]==mask)
{
for(c1=0;c1<ShopKeeper.DEAL_CONFLICTS[c].length;c1++)
if((ShopKeeper.DEAL_CONFLICTS[c][c1]!=mask)
&&(isSold(ShopKeeper.DEAL_CONFLICTS[c][c1])))
addSoldType(-ShopKeeper.DEAL_CONFLICTS[c][c1]);
break;
}
}
}
if(mask>0)
whatIsSoldMask|=(CMath.pow(2,mask-1)<<8);
else
whatIsSoldMask=CMath.unsetb(whatIsSoldMask,(CMath.pow(2,(-mask)-1)<<8));
}
}
@Override
public long getWhatIsSoldMask()
{
return whatIsSoldMask;
}
@Override
public void setWhatIsSoldMask(long newSellCode)
{
whatIsSoldMask = newSellCode;
}
@Override
public String storeKeeperString()
{
return CMLib.coffeeShops().storeKeeperString(getShop());
}
@Override
public boolean doISellThis(Environmental thisThang)
{
return CMLib.coffeeShops().doISellThis(thisThang, this);
}
@Override
public String prejudiceFactors()
{
return prejudice;
}
@Override
public void setPrejudiceFactors(String factors)
{
prejudice = factors;
}
@Override
public String ignoreMask()
{
return ignore;
}
@Override
public void setIgnoreMask(String factors)
{
ignore = factors;
}
@Override
public String[] itemPricingAdjustments()
{
return pricingAdjustments;
}
@Override
public void setItemPricingAdjustments(String[] factors)
{
if((!(affected instanceof MOB))||(!((MOB)affected).isMonster()))
factors=new String[0];
pricingAdjustments=factors;
}
protected Area getStartArea()
{
Area A=CMLib.map().getStartArea(affected);
if(A==null)
CMLib.map().areaLocation(affected);
if(A==null)
A=CMLib.map().areas().nextElement();
return A;
}
@Override
public int finalInvResetRate()
{
if((invResetRate()!=0)||((affected instanceof MOB)&&(!((MOB)affected).isMonster())))
return invResetRate();
return getStartArea().finalInvResetRate();
}
@Override
public String finalPrejudiceFactors()
{
if((prejudiceFactors().length()>0)||((affected instanceof MOB)&&(!((MOB)affected).isMonster())))
return prejudiceFactors();
return getStartArea().finalPrejudiceFactors();
}
@Override
public String finalIgnoreMask()
{
if((ignoreMask().length()>0)||((affected instanceof MOB)&&(!((MOB)affected).isMonster())))
return ignoreMask();
return getStartArea().finalIgnoreMask();
}
@Override
public String[] finalItemPricingAdjustments()
{
if(((itemPricingAdjustments()!=null)&&(itemPricingAdjustments().length>0))
||((affected instanceof MOB)&&(!((MOB)affected).isMonster())))
return itemPricingAdjustments();
return getStartArea().finalItemPricingAdjustments();
}
@Override
public Pair<Long, TimePeriod> finalBudget()
{
if((budget != null)||((affected instanceof MOB)&&(!((MOB)affected).isMonster())))
return budget;
return getStartArea().finalBudget();
}
@Override
public double[] finalDevalueRate()
{
if ((devalueRate != null)||((affected instanceof MOB)&&(!((MOB)affected).isMonster())))
return devalueRate;
return getStartArea().finalDevalueRate();
}
@Override
public boolean tick(Tickable ticking, int tickID)
{
if((unInvoked)&&(canBeUninvoked())) // override all normal common skill behavior!!
return false;
return true;
}
public MOB deriveMerchant(MOB roomHelper)
{
if(affected ==null)
return null;
if(affected instanceof MOB)
return (MOB)affected;
if(affected instanceof Item)
{
if(((Item)affected).owner() instanceof MOB)
return (MOB)((Item)affected).owner();
if(CMLib.flags().isGettable((Item)affected))
return null;
}
Room room=CMLib.map().roomLocation(affected);
if((affected instanceof Area)&&(roomHelper!=null))
room=roomHelper.location();
if(room==null)
return null;
if(staticMOB==null)
{
staticMOB=CMClass.getMOB("StdMOB");
if((affected instanceof Room)
||(affected instanceof Exit))
staticMOB.setName(L("the shopkeeper"));
else
if(affected instanceof Area)
staticMOB.setName(L("the shop"));
else
staticMOB.setName(affected.Name());
}
staticMOB.setStartRoom(room);
staticMOB.setLocation(room);
if(finalBudget() != null)
{
if( CMLib.beanCounter().getTotalAbsoluteNativeValue( staticMOB ) < finalBudget().first.longValue() )
staticMOB.setMoney(finalBudget().first.intValue());
}
return staticMOB;
}
@Override
public boolean okMessage(final Environmental myHost, final CMMsg msg)
{
final MOB merchantM=deriveMerchant(msg.source());
if(merchantM==null)
return super.okMessage(myHost,msg);
final MOB shopperM=msg.source();
if((msg.source()==merchantM)
&&(msg.targetMinor()==CMMsg.TYP_GET)
&&(msg.target() instanceof Item))
{
final Item newitem=(Item)msg.target();
if((newitem.numberOfItems()>(merchantM.maxItems()-(merchantM.numItems()+shop.totalStockSizeIncludingDuplicates())))
&&(!merchantM.isMine(this)))
{
merchantM.tell(L("You can't carry that many items."));
return false;
}
}
if(msg.amITarget(merchantM)||(msg.amITarget(affected)))
{
switch(msg.targetMinor())
{
case CMMsg.TYP_VALUE:
case CMMsg.TYP_SELL:
{
if(!merchantM.isMonster())
{
shopperM.tell(shopperM,null,null,L("You'll have to talk to <S-NAME> about that."));
return false;
}
if(!CMLib.coffeeShops().ignoreIfNecessary(msg.source(),finalIgnoreMask(),merchantM))
return false;
final double budgetRemaining=CMLib.beanCounter().getTotalAbsoluteValue(merchantM,CMLib.beanCounter().getCurrency(merchantM));
final double budgetMax=budgetRemaining*100;
if(CMLib.coffeeShops().standardSellEvaluation(merchantM,msg.source(),msg.tool(),this,budgetRemaining,budgetMax,msg.targetMinor()==CMMsg.TYP_SELL))
return super.okMessage(myHost,msg);
return false;
}
case CMMsg.TYP_BUY:
case CMMsg.TYP_VIEW:
{
if(!CMLib.coffeeShops().ignoreIfNecessary(msg.source(),finalIgnoreMask(),merchantM))
return false;
if((msg.targetMinor()==CMMsg.TYP_BUY)&&(msg.tool()!=null)&&(!msg.tool().okMessage(myHost,msg)))
return false;
if(CMLib.coffeeShops().standardBuyEvaluation(merchantM,msg.source(),msg.tool(),this,msg.targetMinor()==CMMsg.TYP_BUY))
return super.okMessage(myHost,msg);
return false;
}
case CMMsg.TYP_LIST:
CMLib.coffeeShops().ignoreIfNecessary(msg.source(),finalIgnoreMask(),merchantM);
break;
default:
break;
}
}
else
if(msg.amISource(merchantM)&&(msg.sourceMinor()==CMMsg.TYP_DEATH))
{
Item I=(Item)getShop().removeStock("all",merchantM);
while(I!=null)
{
merchantM.addItem(I);
I=(Item)getShop().removeStock("all",merchantM);
}
merchantM.recoverPhyStats();
}
return super.okMessage(myHost,msg);
}
public boolean putUpForSale(MOB source, MOB merchantM, Environmental tool)
{
if((tool!=null)
&&(!tool.ID().endsWith("ClanApron"))
&&(merchantM.isMonster())
&&((CMSecurity.isAllowed(source,merchantM.location(),CMSecurity.SecFlag.ORDER)
||(CMLib.law().doesHavePriviledgesHere(source,merchantM.getStartRoom()))
||(CMSecurity.isAllowed(source,merchantM.location(),CMSecurity.SecFlag.CMDMOBS)&&(merchantM.isMonster()))
||(CMSecurity.isAllowed(source,merchantM.location(),CMSecurity.SecFlag.CMDROOMS)&&(merchantM.isMonster())))
||((CMLib.law().getLegalBehavior(merchantM.getStartRoom())!=null)
&&(source.getClanRole(CMLib.law().getLegalBehavior(merchantM.getStartRoom()).rulingOrganization())!=null)))
&&((doISellThis(tool))||(isSold(DEAL_INVENTORYONLY))))
{
CMLib.commands().postSay(merchantM,source,L("OK, I will now sell @x1.",tool.name()),false,false);
getShop().addStoreInventory(tool,1,-1);
if(affected instanceof Area)
CMLib.database().DBUpdateArea(affected.Name(),(Area)affected);
else
if(affected instanceof Exit)
CMLib.database().DBUpdateExits(merchantM.location());
else
if(affected instanceof Room)
CMLib.database().DBUpdateRoom(merchantM.location());
return true;
}
return false;
}
public boolean canPossiblyVend(Environmental E, Environmental what)
{
if(!(what instanceof Item))
return false;
final Item whatI=(Item)what;
if((E instanceof Container)
&&(!(((Container)E).owner() instanceof MOB))
&&(((Container)E).canContain(whatI))
&&(((Container)E).capacity()>whatI.phyStats().weight()))
return true;
return false;
}
@Override
public void executeMsg(final Environmental myHost, final CMMsg msg)
{
final MOB merchantM=deriveMerchant(msg.source());
if(merchantM==null)
{
super.executeMsg(myHost,msg);
return;
}
if(msg.amITarget(merchantM)||(msg.amITarget(affected)))
{
final MOB mob=msg.source();
switch(msg.targetMinor())
{
case CMMsg.TYP_GIVE:
if(!putUpForSale(msg.source(),merchantM,msg.tool()))
super.executeMsg(myHost,msg);
break;
case CMMsg.TYP_PUT:
if((canPossiblyVend(affected,msg.tool()))
&&(putUpForSale(msg.source(),merchantM,msg.tool())))
return;
super.executeMsg(myHost,msg);
break;
case CMMsg.TYP_VALUE:
super.executeMsg(myHost,msg);
if(merchantM.isMonster())
CMLib.commands().postSay(merchantM,mob,L("I'll give you @x1 for @x2.",CMLib.beanCounter().nameCurrencyShort(merchantM,CMLib.coffeeShops().pawningPrice(merchantM,mob,msg.tool(),this).absoluteGoldPrice),msg.tool().name()),true,false);
break;
case CMMsg.TYP_VIEW:
super.executeMsg(myHost,msg);
if((msg.tool() instanceof Physical)
&&(getShop().doIHaveThisInStock(msg.tool().Name(),mob)))
{
CMLib.commands().postSay(merchantM,msg.source(),L("Interested in @x1? Here is some information for you:\n\rLevel @x2\n\rDescription: @x3",msg.tool().name(),""+((Physical)msg.tool()).phyStats().level(),msg.tool().description()),true,false);
}
break;
case CMMsg.TYP_SELL: // sell TO -- this is a shopkeeper purchasing from a player
{
super.executeMsg(myHost,msg);
CMLib.coffeeShops().transactPawn(merchantM,msg.source(),this,msg.tool());
break;
}
case CMMsg.TYP_BUY:
{
super.executeMsg(myHost,msg);
final MOB mobFor=CMLib.coffeeShops().parseBuyingFor(msg.source(),msg.targetMessage());
if((msg.tool()!=null)
&&(getShop().doIHaveThisInStock("$"+msg.tool().Name()+"$",mobFor))
&&(merchantM.location()!=null))
{
final Environmental item=getShop().getStock("$"+msg.tool().Name()+"$",mobFor);
if(item!=null)
CMLib.coffeeShops().transactMoneyOnly(merchantM,msg.source(),this,item,!merchantM.isMonster());
final List<Environmental> products=getShop().removeSellableProduct("$"+msg.tool().Name()+"$",mobFor);
if(products.size()==0)
break;
final Environmental product=products.get(0);
if(product instanceof Item)
{
if(!CMLib.coffeeShops().purchaseItems((Item)product,products,merchantM,mobFor))
return;
}
else
if(product instanceof MOB)
{
if(CMLib.coffeeShops().purchaseMOB((MOB)product,merchantM,this,mobFor))
{
msg.modify(msg.source(),msg.target(),product,msg.sourceCode(),msg.sourceMessage(),msg.targetCode(),msg.targetMessage(),msg.othersCode(),msg.othersMessage());
product.executeMsg(myHost,msg);
}
}
else
if(product instanceof Ability)
CMLib.coffeeShops().purchaseAbility((Ability)product,merchantM,this,mobFor);
}
break;
}
case CMMsg.TYP_LIST:
{
super.executeMsg(myHost,msg);
final Vector<Environmental> inventory=new XVector<Environmental>(getShop().getStoreInventory());
final String forMask=CMLib.coffeeShops().getListForMask(msg.targetMessage());
final String s=CMLib.coffeeShops().getListInventory(merchantM,mob,inventory,0,this,forMask);
if(s.length()>0)
mob.tell(s);
break;
}
default:
super.executeMsg(myHost,msg);
break;
}
}
else
if((msg.targetMinor()==CMMsg.TYP_DROP)
&&(myHost==affected)
&&((affected instanceof Room)
||(affected instanceof Exit)
||((affected instanceof Item)&&(!canPossiblyVend(affected,msg.tool()))))
&&(putUpForSale(msg.source(),merchantM,msg.target())))
return;
else
if((msg.targetMinor()==CMMsg.TYP_THROW)
&&(myHost==affected)
&&(affected instanceof Area)
&&(msg.target() instanceof Room)
&&(((Room)msg.target()).domainType()==Room.DOMAIN_OUTDOORS_AIR)
&&(msg.source().location().getRoomInDir(Directions.UP)==msg.target())
&&(putUpForSale(msg.source(),merchantM,msg.tool())))
return;
else
super.executeMsg(myHost,msg);
}
@Override
public boolean invoke(MOB mob, List<String> commands, Physical givenTarget, boolean auto, int asLevel)
{
if(commands.size()==0)
{
commonTell(mob,L("Market what? Enter \"market list\" for a list or \"market item value\" to sell something."));
return false;
}
if(CMParms.combine(commands,0).equalsIgnoreCase("list"))
{
final CMMsg msg=CMClass.getMsg(mob,mob,CMMsg.MSG_LIST,L("<S-NAME> review(s) <S-HIS-HER> inventory."));
if(mob.location().okMessage(mob,msg))
mob.location().send(mob,msg);
return true;
}
if((commands.get(0)).equalsIgnoreCase("remove")
||(commands.get(0)).equalsIgnoreCase("delete"))
{
if(commands.size()==1)
{
commonTell(mob,L("Remove what item from the marketing list?"));
return false;
}
final String itemName=CMParms.combine(commands,1);
Item I=(Item)getShop().removeStock(itemName,mob);
if(I==null)
{
commonTell(mob,L("'@x1' is not on the list.",itemName));
return false;
}
final String iname=I.name();
while(I!=null)
{
mob.addItem(I);
I=(Item)getShop().removeStock(itemName,mob);
}
getShop().delAllStoreInventory(I);
mob.recoverCharStats();
mob.recoverPhyStats();
mob.recoverMaxState();
mob.tell(L("@x1 has been removed from your inventory list.",iname));
return true;
}
Environmental target=null;
double val=-1;
if(commands.size()>1)
{
final String s=commands.get(commands.size()-1);
if(CMath.isInteger(s))
{
val=CMath.s_int( s );
if(val>0)
commands.remove(s);
}
else
{
final long numberCoins=CMLib.english().numPossibleGold(mob,s);
if(numberCoins>0)
{
final String currency=CMLib.english().numPossibleGoldCurrency(mob,s);
final double denom=CMLib.english().numPossibleGoldDenomination(mob,currency,s);
if(denom>0.0)
{
val=CMath.mul(numberCoins,denom);
if(val>0)
commands.remove(s);
}
}
}
}
String itemName=CMParms.combine(commands,0);
final Vector<Item> V=new Vector<Item>();
boolean allFlag=commands.get(0).equalsIgnoreCase("all");
if(itemName.toUpperCase().startsWith("ALL.")){ allFlag=true; itemName="ALL "+itemName.substring(4);}
if(itemName.toUpperCase().endsWith(".ALL")){ allFlag=true; itemName="ALL "+itemName.substring(0,itemName.length()-4);}
int addendum=1;
String addendumStr="";
boolean doBugFix = true;
while(doBugFix || allFlag)
{
doBugFix=false;
final Item I=mob.fetchItem(null,Wearable.FILTER_UNWORNONLY,itemName+addendumStr);
if(I==null)
break;
if(target==null)
target=I;
else
if(!target.sameAs(I))
break;
if(CMLib.flags().canBeSeenBy(I,mob))
V.addElement(I);
addendumStr="."+(++addendum);
}
if(V.size()==0)
{
commonTell(mob,L("You don't seem to be carrying '@x1'.",itemName));
return false;
}
if((getShop().numberInStock(target)<=0)&&(val<=0))
{
commonTell(mob,L("You failed to specify a price for '@x1'.",itemName));
return false;
}
if(!super.invoke(mob,commands,givenTarget,auto,asLevel))
return false;
if(!proficiencyCheck(mob,0,auto))
{
commonTell(mob,target,null,L("You fail to put <T-NAME> up for sale."));
return false;
}
final CMMsg msg=CMClass.getMsg(mob,target,CMMsg.MSG_SELL,L("<S-NAME> put(s) <T-NAME> up for sale."));
if(mob.location().okMessage(mob,msg))
{
mob.location().send(mob,msg);
for(int i=0;i<V.size();i++)
{
final Item I=V.elementAt(i);
if(val<=0)
getShop().addStoreInventory(I);
else
getShop().addStoreInventory(I,1,(int)Math.round(val));
mob.delItem(I);
}
}
mob.location().recoverRoomStats();
mob.recoverPhyStats();
return true;
}
@Override
public boolean autoInvocation(MOB mob, boolean force)
{
if(mob instanceof ShopKeeper)
return false;
return super.autoInvocation(mob, force);
}
}