package com.planet_ink.coffee_mud.Items.Basic; import com.planet_ink.coffee_mud.core.interfaces.*; import com.planet_ink.coffee_mud.core.interfaces.EachApplicable.ApplyAffectPhyStats; 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.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 2001-2019 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 StdItem implements Item { @Override public String ID() { return "StdItem"; } protected String name = "an ordinary item"; protected String displayText = L("a nondescript item sits here doing nothing."); protected Object description = null; protected int myUses = Integer.MAX_VALUE; protected long myWornCode = Wearable.IN_INVENTORY; protected String miscText = ""; protected Rideable riding = null; protected String rawImageName = null; protected String cachedImageName = null; protected String secretIdentity = null; protected boolean wornLogicalAnd = false; protected long properWornBitmap = Wearable.WORN_HELD; protected int baseGoldValue = 0; protected int material = RawMaterial.RESOURCE_COTTON; protected String[] xtraValues = null; protected long dispossessionTime = 0; protected short tickCtr = (short)(40+(hashCode()%40)); protected int tickStatus = Tickable.STATUS_NOT; protected String databaseID = ""; protected boolean destroyed = false; protected Item me = this; protected PhyStats phyStats = (PhyStats) CMClass.getCommon("DefaultPhyStats"); protected PhyStats basePhyStats = (PhyStats) CMClass.getCommon("DefaultPhyStats"); protected volatile Container myContainer = null; protected volatile ItemPossessor owner = null; protected SVector<Ability> affects = null; protected SVector<Behavior> behaviors = null; protected SVector<ScriptingEngine> scripts = null; @SuppressWarnings("rawtypes") protected ApplyAffectPhyStats affectPhyStats = new ApplyAffectPhyStats(this); public StdItem() { super(); //CMClass.bumpCounter(this,CMClass.CMObjectType.ITEM);//removed for mem & perf if(basePhyStats == null) basePhyStats = (PhyStats) CMClass.getCommon("DefaultPhyStats"); if(phyStats == null) phyStats = (PhyStats) CMClass.getCommon("DefaultPhyStats"); basePhyStats().setWeight(1); basePhyStats().setArmor(0); xtraValues=CMProps.getExtraStatCodesHolder(this); } protected boolean abilityImbuesMagic() { return true; } // protected void finalize() // { // CMClass.unbumpCounter(this,CMClass.CMObjectType.ITEM); }//removed for mem // & perf @Override public void initializeClass() { } @Override public boolean isGeneric() { return false; } @Override public String Name() { return name; } @Override public void setName(final String newName) { name = newName; } @Override public String name() { if(phyStats().newName()!=null) return phyStats().newName(); return Name(); } @Override public String displayText(final MOB viewerMob) { return displayText(); } @Override public String name(final MOB viewerMob) { return name(); } @Override public void setDatabaseID(final String id) { databaseID = id; } @Override public boolean canSaveDatabaseID() { return true; } @Override public String databaseID() { return databaseID; } @Override public String image() { if(cachedImageName==null) { if((rawImageName!=null)&&(rawImageName.length()>0)) cachedImageName=rawImageName; else cachedImageName=CMLib.protocol().getDefaultMXPImage(this); } return cachedImageName; } @Override public String rawImage() { if(rawImageName==null) return ""; return rawImageName; } @Override public void setImage(final String newImage) { if((newImage==null)||(newImage.trim().isEmpty())) rawImageName=null; else rawImageName=newImage; if((cachedImageName!=null)&&(!cachedImageName.equals(newImage))) cachedImageName=null; } @Override public PhyStats phyStats() { return phyStats; } @Override public PhyStats basePhyStats() { return basePhyStats; } @SuppressWarnings("unchecked") @Override public void recoverPhyStats() { basePhyStats.copyInto(phyStats); eachEffect(affectPhyStats); if(((phyStats().ability()>0)&&abilityImbuesMagic())||(this instanceof MiscMagic)) phyStats().setDisposition(phyStats().disposition()|PhyStats.IS_BONUS); if((owner()!=null) &&(owner() instanceof MOB) &&(CMLib.flags().isHidden(this))) phyStats().setDisposition((int)(phyStats().disposition()&(PhyStats.ALLMASK-PhyStats.IS_HIDDEN))); } @Override public void setBasePhyStats(final PhyStats newStats) { basePhyStats=(PhyStats)newStats.copyOf(); } @Override public CMObject newInstance() { try { return this.getClass().newInstance(); } catch(final Exception e) { Log.errOut(ID(),e); } return new StdItem(); } @Override public boolean subjectToWearAndTear() { return false; } @SuppressWarnings("rawtypes") protected void cloneFix(final Item I) { destroyed=false; me=this; basePhyStats=(PhyStats)I.basePhyStats().copyOf(); phyStats=(PhyStats)I.phyStats().copyOf(); affectPhyStats = new ApplyAffectPhyStats(this); affects=null; behaviors=null; scripts=null; for(final Enumeration<Behavior> e=I.behaviors();e.hasMoreElements();) { final Behavior B=e.nextElement(); if(B!=null) addBehavior((Behavior)B.copyOf()); } for(final Enumeration<ScriptingEngine> e=I.scripts();e.hasMoreElements();) { final ScriptingEngine SE=e.nextElement(); if(SE!=null) addScript((ScriptingEngine)SE.copyOf()); } Ability A; for(final Enumeration<Ability> a=I.effects();a.hasMoreElements();) { A=a.nextElement(); if((A!=null)&&(!A.ID().equals("ItemRejuv"))) { A=(Ability)A.copyOf(); addEffect(A); if(A.canBeUninvoked()) { A.unInvoke(); delEffect(A); } } } // an item copy isn't linked to until added somewhere... owner=null; } @Override public CMObject copyOf() { try { final StdItem E=(StdItem)this.clone(); //CMClass.bumpCounter(E,CMClass.CMObjectType.ITEM);//removed for mem & perf E.xtraValues=(xtraValues==null)?null:(String[])xtraValues.clone(); E.cloneFix(this); CMLib.catalog().newInstance(this); return E; } catch(final CloneNotSupportedException e) { return this.newInstance(); } } @Override public Rideable riding() { return riding; } @Override public void setRiding(final Rideable ride) { if((ride!=null)&&(riding()!=null)&&(riding()==ride)&&(riding().amRiding(this))) return; if((riding()!=null)&&(riding().amRiding(this))) riding().delRider(this); riding=ride; if((riding()!=null)&&(!riding().amRiding(this))) riding().addRider(this); } @Override public ItemPossessor owner() { return owner; } @Override public void setOwner(final ItemPossessor E) { owner=E; if((E!=null)&&(!(E instanceof Room))) setExpirationDate(0); recoverPhyStats(); } @Override public long expirationDate() { return dispossessionTime; } @Override public void setExpirationDate(final long time) { dispossessionTime=time; } @Override public boolean amDestroyed() { return destroyed; } @Override public boolean amWearingAt(final long wornCode) { if(wornCode == 0) return myWornCode == 0; return (myWornCode & wornCode)==wornCode; } @Override public boolean amBeingWornProperly() { if(this.myWornCode==0) return false; if(this.properWornBitmap==0) return false; if(this.wornLogicalAnd) return this.myWornCode==this.properWornBitmap; else return (this.properWornBitmap & this.myWornCode) != 0; } @Override public boolean fitsOn(final long wornCode) { if(wornCode<=0) return true; return ((properWornBitmap & wornCode)==wornCode); } @Override public void wearEvenIfImpossible(final MOB mob) { for(final long code : Wearable.CODES.ALL_ORDERED()) { if(fitsOn(code)) { wearAt(code); break; } } } @Override public boolean wearIfPossible(final MOB mob, final long wearCode) { if(wearCode<=0) return false; if((fitsOn(wearCode)) &&(canWear(mob,wearCode))) { wearAt(wearCode); return true; } return false; } @Override public boolean wearIfPossible(final MOB mob) { for(final long code : Wearable.CODES.ALL_ORDERED()) { if((code>0) && wearIfPossible(mob,code)) return true; } return false; } @Override public void wearAt(final long wornCode) { if(wornCode==Wearable.IN_INVENTORY) { unWear(); return; } if(wornLogicalAnd) setRawWornCode(properWornBitmap); else setRawWornCode(wornCode); recoverPhyStats(); } @Override public long rawProperLocationBitmap() { return properWornBitmap; } @Override public boolean rawLogicalAnd() { return wornLogicalAnd; } @Override public void setRawProperLocationBitmap(final long newValue) { properWornBitmap=newValue; } @Override public void setRawLogicalAnd(final boolean newAnd) { wornLogicalAnd=newAnd; } @Override public boolean compareProperLocations(final Item toThis) { if(toThis.rawLogicalAnd()!=wornLogicalAnd) return false; if((toThis.rawProperLocationBitmap()|Wearable.WORN_HELD)==(properWornBitmap|Wearable.WORN_HELD)) return true; return false; } @Override public long whereCantWear(final MOB mob) { long couldHaveBeenWornAt=-1; if(properWornBitmap==0) return couldHaveBeenWornAt; short layer=0; short layerAtt=0; if(this instanceof Armor) { layer=((Armor)this).getClothingLayer(); layerAtt=((Armor)this).getLayerAttributes(); } final Wearable.CODES codes = Wearable.CODES.instance(); if(!wornLogicalAnd) { for(final long wornCode : codes.all()) { if(wornCode != Wearable.IN_INVENTORY) { if(fitsOn(wornCode)) { couldHaveBeenWornAt=wornCode; if(mob.freeWearPositions(wornCode,layer,layerAtt)>0) return 0; } } } return couldHaveBeenWornAt; } for(final long wornCode : codes.all()) { if(wornCode != Wearable.IN_INVENTORY) { if((fitsOn(wornCode)) &&(mob.freeWearPositions(wornCode,layer,layerAtt)==0)) return wornCode; } } return 0; } @Override public boolean canWear(final MOB mob, final long where) { if(where==0) return (whereCantWear(mob)==0); if((rawProperLocationBitmap()&where)!=where) return false; return mob.freeWearPositions(where,(short)0,(short)0)>0; } @Override public long rawWornCode() { return myWornCode; } @Override public void setRawWornCode(final long newValue) { myWornCode=newValue; } @Override public void unWear() { setRawWornCode(Wearable.IN_INVENTORY); recoverPhyStats(); } @Override public int material() { return material; } @Override public void setMaterial(final int newValue) { material=newValue; } @Override public int value() { return baseGoldValue()+(10*phyStats().ability()); } @Override public int baseGoldValue() { return baseGoldValue; } @Override public void setBaseValue(final int newValue) { baseGoldValue=newValue; } @Override public String readableText() { return miscText; } @Override public void setReadableText(final String text) { miscText = text; } @Override public boolean isReadable() { return CMLib.flags().isReadable(this); } @Override public void setReadable(final boolean truefalse) { CMLib.flags().setReadable(this, truefalse); } @Override public void affectPhyStats(final Physical affected, final PhyStats affectableStats) { if(affected instanceof Room) { if((CMLib.flags().isLightSource(this))&&(CMLib.flags().isInDark(affected))) affectableStats.setDisposition(affectableStats.disposition()-PhyStats.IS_DARK); } else { if(CMLib.flags().isLightSource(this)) { if(rawWornCode()!=Wearable.IN_INVENTORY) affectableStats.setDisposition(affectableStats.disposition()|PhyStats.IS_LIGHTSOURCE); if(CMLib.flags().isInDark(affected)) affectableStats.setDisposition(affectableStats.disposition()-PhyStats.IS_DARK); } if((amWearingAt(Wearable.WORN_MOUTH))&&(affected instanceof MOB)) { if(!(this instanceof Light)) affectableStats.setSensesMask(affectableStats.sensesMask()|PhyStats.CAN_NOT_SPEAK); if((this instanceof Armor)&&(!CMath.bset(((Armor)this).getClothingLayer(),Armor.LAYERMASK_SEETHROUGH))) affectableStats.setSensesMask(affectableStats.sensesMask()|PhyStats.CAN_NOT_TASTE); } final Item ultiCon=ultimateContainer(null); if(((ultiCon==null)||(!ultiCon.amWearingAt(Wearable.WORN_FLOATING_NEARBY))) &&((!(affected instanceof MOB))||(((MOB)affected).riding()!=this))) affectableStats.setWeight(affectableStats.weight()+phyStats().weight()); } final List<Ability> affects=this.affects; if((affects!=null)&&(!affects.isEmpty())) { try { for(int a=0;a<affects.size();a++) { final Ability A=affects.get(a); if((A!=null)&&(A.bubbleAffect())) A.affectPhyStats(affected,affectableStats); } } catch (final ArrayIndexOutOfBoundsException e) { } } } @Override public void affectCharStats(final MOB affectedMob, final CharStats affectableStats) { final List<Ability> affects=this.affects; if((affects!=null)&&(!affects.isEmpty())) { try { for(int a=0;a<affects.size();a++) { final Ability A=affects.get(a); if((A!=null)&&(A.bubbleAffect())) A.affectCharStats(affectedMob,affectableStats); } } catch (final ArrayIndexOutOfBoundsException e) { } } } @Override public void affectCharState(final MOB affectedMob, final CharState affectableMaxState) { final List<Ability> affects=this.affects; if((affects!=null)&&(!affects.isEmpty())) { try { for(int a=0;a<affects.size();a++) { final Ability A=affects.get(a); if((A!=null)&&(A.bubbleAffect())) A.affectCharState(affectedMob,affectableMaxState); } } catch (final ArrayIndexOutOfBoundsException e) { } } } @Override public void setMiscText(final String newText) { miscText=newText; } @Override public String text() { return miscText; } @Override public String miscTextFormat() { return CMParms.FORMAT_UNDEFINED; } @Override public int compareTo(final CMObject o) { return CMClass.classID(this).compareToIgnoreCase(CMClass.classID(o)); } @Override public int getTickStatus() { return tickStatus; } @Override public boolean tick(final Tickable ticking, final int tickID) { if(destroyed) return false; tickStatus=Tickable.STATUS_START; if((--tickCtr)<=0) { tickCtr=(short)(10+CMLib.dice().roll(1, 100, 0)); final String itemReport = CMLib.flags().validCheck(this); if(itemReport != null) { Log.warnOut("Destroy: "+itemReport); this.destroy(); return false; } } switch(tickID) { case Tickable.TICKID_ITEM_BEHAVIOR: { tickStatus=Tickable.STATUS_BEHAVIOR; if(numBehaviors()>0) { eachBehavior(new EachApplicable<Behavior>() { @Override public final void apply(final Behavior B) { B.tick(ticking,tickID); } }); } tickStatus=Tickable.STATUS_SCRIPT; if(numScripts()>0) { eachScript(new EachApplicable<ScriptingEngine>() { @Override public final void apply(final ScriptingEngine S) { S.tick(ticking,tickID); } }); } if((numBehaviors()==0)&&(numScripts()==0)) return false; break; } case Tickable.TICKID_CLANITEM: case Tickable.TICKID_ELECTRONICS: case Tickable.TICKID_SPECIALCOMBAT: case Tickable.TICKID_SPECIALMANEUVER: break; default: { tickStatus=Tickable.STATUS_AFFECT; if(numEffects()>0) { eachEffect(new EachApplicable<Ability>() { @Override public final void apply(final Ability A) { if(!A.tick(ticking,tickID)) A.unInvoke(); } }); } break; } } tickStatus=Tickable.STATUS_NOT; return !amDestroyed(); } @Override public Item ultimateContainer(final Physical stopAtC) { final Container C=container(); if(C==null) return this; else if(C==stopAtC) return C; else if(C==this) return null; else return C.ultimateContainer(stopAtC); } @Override public Container container() { return myContainer; } @Override public String rawSecretIdentity() { return ((secretIdentity == null) ? "" : secretIdentity); } @Override public String secretIdentity() { if((secretIdentity!=null)&&(secretIdentity.length()>0)) return secretIdentity+"\n\rLevel: "+phyStats().level()+tackOns(); return description()+"\n\rLevel: "+phyStats().level()+tackOns(); } @Override public void setSecretIdentity(final String newIdentity) { if((newIdentity==null) ||(newIdentity.trim().equalsIgnoreCase(description())) ||(newIdentity.isEmpty())) secretIdentity=null; else secretIdentity=newIdentity; } @Override public String displayText() { return displayText; } @Override public void setDisplayText(final String newDisplayText) { displayText=newDisplayText; } @Override public String description() { if(description == null) return ""; else if(description instanceof byte[]) { final byte[] descriptionBytes=(byte[])description; if(descriptionBytes.length==0) return ""; if(CMProps.getBoolVar(CMProps.Bool.ITEMDCOMPRESS)) return CMLib.encoder().decompressString(descriptionBytes); else return CMStrings.bytesToStr(descriptionBytes); } else return (String)description; } @Override public String description(final MOB viewerMob) { return description(); } @Override public void setDescription(final String newDescription) { if(newDescription.isEmpty()) description=null; else if(CMProps.getBoolVar(CMProps.Bool.ITEMDCOMPRESS)) description=CMLib.encoder().compressString(newDescription); else description=newDescription; } @Override public void setContainer(final Container newContainer) { if((newContainer!=this) &&((newContainer==null)||(newContainer.container()!=this))) myContainer=newContainer; } @Override public int numberOfItems() { return 1; } @Override public int usesRemaining() { return myUses; } @Override public void setUsesRemaining(final int newUses) { myUses=newUses; } @Override public boolean isSavable() { if(!CMLib.flags().isSavable(this)) return false; if(container()!=null) return container().isSavable(); return true; } @Override public void setSavable(final boolean truefalse) { CMLib.flags().setSavable(this, truefalse); } protected boolean canWearComplete(final MOB mob, final long wearWhere) { if(!canWear(mob,wearWhere)) { long cantWearAt=whereCantWear(mob); if(wearWhere!=0) cantWearAt = cantWearAt & wearWhere; Item alreadyWearing=(cantWearAt==0)?null:mob.fetchFirstWornItem(cantWearAt); final Wearable.CODES codes = Wearable.CODES.instance(); if(alreadyWearing!=null) { if((cantWearAt!=Wearable.WORN_HELD)&&(cantWearAt!=Wearable.WORN_WIELD)) { final boolean amWearingOther = !alreadyWearing.amWearingAt(Item.IN_INVENTORY); if(!CMLib.commands().postRemove(mob,alreadyWearing,false)) { mob.tell(L("You are already wearing @x1 on your @x2.",alreadyWearing.name(),codes.name(cantWearAt))); return false; } if(amWearingOther && alreadyWearing.amWearingAt(Item.IN_INVENTORY) && (!canWear(mob,wearWhere))) return canWearComplete(mob, wearWhere); alreadyWearing=mob.fetchFirstWornItem(cantWearAt); if((alreadyWearing!=null)&&(!canWear(mob,0))) { mob.tell(L("You are already wearing @x1 on your @x2.",alreadyWearing.name(),codes.name(cantWearAt))); return false; } } else { final short layer=(this instanceof Armor)?((Armor)this).getClothingLayer():0; final short layer2=(alreadyWearing instanceof Armor)?((Armor)alreadyWearing).getClothingLayer():0; if((rawProperLocationBitmap() == alreadyWearing.rawProperLocationBitmap()) &&(rawLogicalAnd()) &&(alreadyWearing.rawLogicalAnd()) &&(layer == layer2) &&(CMLib.commands().postRemove(mob,alreadyWearing,false))) return true; if(cantWearAt==Wearable.WORN_HELD) mob.tell(L("You are already holding @x1.",alreadyWearing.name())); else if(cantWearAt==Wearable.WORN_WIELD) mob.tell(L("You are already wielding @x1.",alreadyWearing.name())); else mob.tell(L("You are already wearing @x1 on your @x2.",alreadyWearing.name(),codes.name(cantWearAt))); return false; } } else if(wearWhere!=0) { final StringBuilder locs=new StringBuilder(""); for(int i=0;i<codes.total();i++) { if((codes.get(i)&wearWhere)>0) locs.append(", " + codes.name(i)); } if(locs.length()==0) mob.tell(L("You can't wear that there.")); else mob.tell(L("You can't wear that on your @x1.",locs.toString().substring(1).trim())); return false; } else { mob.tell(L("You don't have anywhere you can wear that.")); return false; } } return true; } protected boolean alreadyWornMsg(final MOB mob, final Item thisItem) { if(!thisItem.amWearingAt(Wearable.IN_INVENTORY)) { if(thisItem.amWearingAt(Wearable.WORN_WIELD)) mob.tell(L("@x1 is already being wielded.",thisItem.name())); else if(thisItem.amWearingAt(Wearable.WORN_HELD)) mob.tell(L("@x1 is already being held.",thisItem.name())); else if(thisItem.amWearingAt(Wearable.WORN_FLOATING_NEARBY)) mob.tell(L("@x1 is floating nearby.",thisItem.name())); else mob.tell(L("@x1is already being worn.",thisItem.name())); return false; } return true; } @Override public boolean okMessage(final Environmental myHost, final CMMsg msg) { // the order that these things are checked in should // be holy, and etched in stone. int num=numBehaviors(); MsgListener N=null; for(int b=0;b<num;b++) { N=fetchBehavior(b); if((N!=null)&&(!N.okMessage(this,msg))) return false; } num=numScripts(); for(int s=0;s<num;s++) { N=fetchScript(s); if((N!=null)&&(!N.okMessage(this,msg))) return false; } num=numEffects(); for(int i=0;i<num;i++) { N=fetchEffect(i); if((N!=null)&&(!N.okMessage(this,msg))) return false; } final MOB mob=msg.source(); if(msg.tool()==this) { switch(msg.sourceMinor()) { case CMMsg.TYP_THROW: if(mob.isMine(this)) { if((phyStats().weight()>(mob.maxCarry()/5)) &&(phyStats().weight()!=0)) { mob.tell(L("@x1 is too heavy to throw.",name())); return false; } if(!CMLib.flags().isDroppable(this)) { mob.tell(L("You can't seem to let go of @x1.",name())); return false; } } break; case CMMsg.TYP_POUR: if(!(this instanceof Drink)) { mob.tell(mob,this,null,L("You can't do that with <T-NAMESELF>.")); return false; } break; default: break; } return true; } else if(!msg.amITarget(this)) return true; else if(msg.targetMinor()==CMMsg.NO_EFFECT) return true; else if((CMath.bset(msg.targetMajor(),CMMsg.MASK_MAGIC)) &&(!CMLib.flags().isGettable(this)) &&((displayText().isEmpty()) ||((msg.tool() instanceof Ability) &&(((Ability)msg.tool()).abstractQuality()==Ability.QUALITY_MALICIOUS)))) { mob.tell(L("Please don't do that.")); return false; } else switch(msg.targetMinor()) { case CMMsg.TYP_ENHANCE: case CMMsg.TYP_REPAIR: if(this instanceof Technical) return true; break; case CMMsg.TYP_DAMAGE: case CMMsg.TYP_WEAPONATTACK: case CMMsg.TYP_ATTACKMISS: if((this instanceof BoardableShip) ||((this instanceof Rideable)&&(((Rideable)this).rideBasis()==Rideable.RIDEABLE_WATER))) return true; break; case CMMsg.TYP_EXPIRE: case CMMsg.TYP_LOOK: case CMMsg.TYP_EXAMINE: case CMMsg.TYP_READ: case CMMsg.TYP_WASREAD: case CMMsg.TYP_QUIETMOVEMENT: case CMMsg.TYP_NOISYMOVEMENT: case CMMsg.TYP_HANDS: case CMMsg.TYP_SPEAK: case CMMsg.TYP_OK_ACTION: case CMMsg.TYP_OK_VISUAL: case CMMsg.TYP_DEATH: case CMMsg.TYP_NOISE: case CMMsg.TYP_EMOTE: case CMMsg.TYP_SNIFF: return true; case CMMsg.TYP_SIT: if((this instanceof DeadBody) &&(mob.phyStats().height()<=0) &&(mob.phyStats().weight()<=0)) return true; //$FALL-THROUGH$ case CMMsg.TYP_SLEEP: case CMMsg.TYP_MOUNT: case CMMsg.TYP_DISMOUNT: case CMMsg.TYP_ENTER: if(this instanceof Rideable) return true; break; case CMMsg.TYP_LIST: if(CMLib.coffeeShops().getShopKeeper(this)!=null) return true; break; case CMMsg.TYP_RELOAD: if((this instanceof AmmunitionWeapon) &&(((AmmunitionWeapon)this).requiresAmmunition())) { if(((AmmunitionWeapon)this).ammunitionRemaining()>=((AmmunitionWeapon)this).ammunitionCapacity()) { mob.tell(L("@x1 is already loaded.",name())); return false; } return true; } break; case CMMsg.TYP_UNLOAD: if((this instanceof AmmunitionWeapon) &&(((AmmunitionWeapon)this).requiresAmmunition())) { if(((AmmunitionWeapon)this).ammunitionRemaining()<=0) { mob.tell(L("@x1 is not loaded.",name())); return false; } return true; } break; case CMMsg.TYP_HOLD: if((!fitsOn(Wearable.WORN_HELD))||(properWornBitmap==0)) { final StringBuilder str=new StringBuilder(L("You can't hold @x1.",name())); if(fitsOn(Wearable.WORN_WIELD)) str.append(L("Try WIELDing it.")); else if(properWornBitmap>0) str.append(L("Try WEARing it.")); mob.tell(str.toString()); return false; } if(!alreadyWornMsg(msg.source(),this)) return false; if(phyStats().level()>mob.phyStats().level()) { mob.tell(L("That looks too advanced for you.")); return false; } if((!rawLogicalAnd())||(properWornBitmap==0)) { if(!canWear(mob,Wearable.WORN_HELD)) { final Item alreadyWearing=mob.fetchHeldItem(); if(alreadyWearing!=null) { if((!CMLib.commands().postRemove(mob,alreadyWearing,false)) ||(!canWear(mob,Wearable.WORN_HELD))) { mob.tell(L("Your hands are full.")); return false; } } else { mob.tell(L("You need hands to hold things.")); return false; } } return true; } return canWearComplete(mob,0); case CMMsg.TYP_WEAR: if(properWornBitmap==0) { mob.tell(L("You can't wear @x1.",name())); return false; } if(!alreadyWornMsg(msg.source(),this)) return false; if(phyStats().level()>mob.phyStats().level()) { mob.tell(L("That looks too advanced for you.")); return false; } return canWearComplete(mob,(msg.value()<=0)?0:((long)(1<<msg.value())/2)); case CMMsg.TYP_WIELD: if((!fitsOn(Wearable.WORN_WIELD))||(properWornBitmap==0)) { mob.tell(L("You can't wield @x1 as a weapon.",name())); return false; } if(!alreadyWornMsg(msg.source(),this)) return false; if(phyStats().level()>mob.phyStats().level()) { mob.tell(L("That looks too advanced for you.")); return false; } if((!rawLogicalAnd())||(properWornBitmap==0)) { if(!canWear(mob,Wearable.WORN_WIELD)) { final Item alreadyWearing=mob.fetchFirstWornItem(Wearable.WORN_WIELD); if(alreadyWearing!=null) { if(!CMLib.commands().postRemove(mob,alreadyWearing,false)) { mob.tell(L("You are already wielding @x1.",alreadyWearing.name())); return false; } } else { mob.tell(L("You need hands to wield things.")); return false; } } } return canWearComplete(mob,0); case CMMsg.TYP_PUSH: case CMMsg.TYP_PULL: if(msg.source().isMine(this)) { mob.tell(L("You'll need to put that down first.")); return false; } if(!CMLib.flags().isGettable(this)) { mob.tell(L("You can't move @x1.",name())); return false; } return true; case CMMsg.TYP_GET: if((msg.tool()==null)||(msg.tool() instanceof MOB)) { if((!CMLib.flags().canBeSeenBy(this,mob)) &&(!msg.sourceMajor(CMMsg.MASK_ALWAYS)) &&(amWearingAt(Wearable.IN_INVENTORY))) { mob.tell(L("You can't see that.")); return false; } if(riding()!=null) { mob.tell(L("@x1 is mounted to @x2.",name(msg.source()),riding().name(msg.source()))); return false; } if((mob.phyStats().level()<phyStats().level()-(10+(mob.phyStats().level()/5))) &&(!(mob instanceof ShopKeeper)) &&(!mob.charStats().getMyRace().leveless()) &&(!mob.charStats().getCurrentClass().leveless())) { mob.tell(L("@x1 is too powerful to endure possessing it.",name())); return false; } if((phyStats().weight()>(mob.maxCarry()-mob.phyStats().weight())) &&(!mob.isMine(this)) &&(phyStats().weight()!=0)) { mob.tell(L("@x1 is too heavy.",name())); return false; } if((numberOfItems()>(mob.maxItems()-mob.numItems()))&&(!mob.isMine(this))) { mob.tell(L("You can't carry that many items.")); return false; } if(!CMLib.flags().isGettable(this)) { mob.tell(L("You can't get @x1.",name())); return false; } if((this instanceof Rideable)&&(((Rideable)this).numRiders()>0)) { if((mob.riding()!=null)&&(mob.riding()==this)) mob.tell(L("You are @x1 @x2!",((Rideable)this).stateString(mob),name())); else mob.tell(L("Someone is @x1 @x2!",((Rideable)this).stateString(mob),name())); return false; } return true; } if(this instanceof Container) return true; switch(msg.sourceMinor()) { case CMMsg.TYP_BUY: case CMMsg.TYP_BID: case CMMsg.TYP_GET: case CMMsg.TYP_GENERAL: case CMMsg.TYP_REMOVE: case CMMsg.TYP_SELL: case CMMsg.TYP_VALUE: case CMMsg.TYP_VIEW: case CMMsg.TYP_GIVE: return true; } break; case CMMsg.TYP_REMOVE: if((msg.tool()==null)||(msg.tool() instanceof MOB)) { if((!CMLib.flags().canBeSeenBy(this,mob)) &&(!msg.sourceMajor(CMMsg.MASK_ALWAYS)) &&(amWearingAt(Wearable.IN_INVENTORY))) { mob.tell(L("You can't see that.")); return false; } if((!amWearingAt(Wearable.IN_INVENTORY))&&(!CMLib.flags().isRemovable(this))) { if(amWearingAt(Wearable.WORN_WIELD)||amWearingAt(Wearable.WORN_HELD)) { mob.tell(L("You can't seem to let go of @x1.",name())); return false; } mob.tell(L("You can't seem to remove @x1.",name())); return false; } Item I=null; final short layer=(this instanceof Armor)?((Armor)this).getClothingLayer():0; short thislayer=0; if(rawWornCode()>0) for(int i=0;i<mob.numItems();i++) { I=mob.getItem(i); if((I!=null)&&(I!=this)&&((I.rawWornCode()&rawWornCode())>0)) { thislayer=(I instanceof Armor)?((Armor)I).getClothingLayer():0; if(thislayer>layer) { mob.tell(mob,I,null,L("You must remove <T-NAME> first.")); return false; } } } return true; } if(this instanceof Container) return true; switch(msg.sourceMinor()) { case CMMsg.TYP_BUY: case CMMsg.TYP_BID: case CMMsg.TYP_GET: case CMMsg.TYP_GENERAL: case CMMsg.TYP_REMOVE: case CMMsg.TYP_SELL: case CMMsg.TYP_VALUE: case CMMsg.TYP_VIEW: case CMMsg.TYP_GIVE: return true; } break; case CMMsg.TYP_DROP: if(!mob.isMine(this)) { mob.tell(L("You don't have that.")); return false; } if(!CMLib.flags().isDroppable(this)) { mob.tell(L("You can't seem to let go of @x1.",name())); return false; } return true; case CMMsg.TYP_BUY: case CMMsg.TYP_BID: case CMMsg.TYP_SELL: case CMMsg.TYP_VALUE: case CMMsg.TYP_VIEW: case CMMsg.TYP_ITEMGENERATED: case CMMsg.TYP_ITEMSGENERATED: return true; case CMMsg.TYP_OPEN: case CMMsg.TYP_CLOSE: case CMMsg.TYP_LOCK: case CMMsg.TYP_INSTALL: case CMMsg.TYP_UNLOCK: if(this instanceof Container) return true; break; case CMMsg.TYP_PUT: if(this instanceof Container) return true; if(CMath.bset(phyStats().sensesMask(), PhyStats.SENSE_INSIDEACCESSIBLE)) return true; mob.tell(mob,this,null,L("You can't put things inside <T-NAME>.")); return false; case CMMsg.TYP_DELICATE_HANDS_ACT: case CMMsg.TYP_JUSTICE: case CMMsg.TYP_WAND_USE: case CMMsg.TYP_RECIPELEARNED: case CMMsg.TYP_COLD: // free metal case CMMsg.TYP_ACID: // slime case CMMsg.TYP_FIRE: // lighting case CMMsg.TYP_WATER: // rust case CMMsg.TYP_CAST_SPELL: case CMMsg.TYP_POISON: // for use poison return true; case CMMsg.TYP_EXTINGUISH: { final Ability A=fetchEffect("Burning"); if((A!=null)&&(!CMath.bset(A.abilityCode(), 2048))) // yes, magic numbers suck return true; break; } case CMMsg.TYP_CAUSESINK: if(this instanceof BoardableShip) return true; break; case CMMsg.TYP_FILL: if(this instanceof Drink) return true; if(this instanceof Lantern) return true; break; case CMMsg.TYP_POUR: if(msg.tool() instanceof Drink) return true; break; case CMMsg.TYP_EAT: if(this instanceof Food) return true; break; case CMMsg.TYP_DRINK: if(this instanceof Drink) return true; break; case CMMsg.TYP_ADVANCE: if((this instanceof BoardableShip) ||((this instanceof Rideable)||(((Rideable)this).rideBasis()==Rideable.RIDEABLE_WATER))) return true; break; case CMMsg.TYP_ACTIVATE: case CMMsg.TYP_POWERCURRENT: case CMMsg.TYP_DEACTIVATE: if((this instanceof Electronics)||(this instanceof Software)) return true; break; case CMMsg.TYP_COLLISION: if(this instanceof SpaceObject) return true; break; case CMMsg.TYP_REWRITE: if((isReadable()) &&(!(this instanceof Scroll)) &&(!(this instanceof Electronics))) { if(msg.targetMessage().trim().isEmpty()) { if(this instanceof Electronics) mob.tell(L("Enter what into @x1?",name())); else mob.tell(L("Write what on @x1?",name())); return false; } if(readableText().startsWith("FILE=")) { mob.tell(L("There's no more room to write on @x1.",name())); return false; } return true; } return false; case CMMsg.TYP_WRITE: if(((isReadable())&&(!(this instanceof Scroll))) ||(this instanceof ElecPanel) ||(this instanceof Software)) { if(msg.targetMessage().trim().isEmpty()) { if(this instanceof Electronics) mob.tell(L("Enter what into @x1?",name())); else mob.tell(L("Write what on @x1?",name())); return false; } if(readableText().startsWith("FILE=")) { mob.tell(L("There's no more room to write on @x1.",name())); return false; } return true; } if(this instanceof Electronics) mob.tell(L("You can't enter commands into @x1.",name())); else mob.tell(L("You can't write on @x1.",name())); return false; default: break; } mob.tell(mob,this,null,L("You can't do that to <T-NAMESELF>.")); return false; } @Override public void executeMsg(final Environmental myHost, final CMMsg msg) { // the order that these things are checked in should // be holy, and etched in stone. if(numBehaviors()>0) { eachBehavior(new EachApplicable<Behavior>() { @Override public final void apply(final Behavior B) { B.executeMsg(me,msg); } }); } if(numScripts()>0) { eachScript(new EachApplicable<ScriptingEngine>() { @Override public final void apply(final ScriptingEngine S) { S.executeMsg(me,msg); } }); } if(numEffects()>0) { eachEffect(new EachApplicable<Ability>() { @Override public final void apply(final Ability A) { A.executeMsg(me, msg); } }); } final MOB mob=msg.source(); if((msg.tool()==this) &&(msg.sourceMinor()==CMMsg.TYP_THROW) &&(mob!=null) &&(msg.target()!=null)) { final Room R=CMLib.map().roomLocation(msg.target()); if(mob.isMine(this)) { mob.delItem(this); if(!R.isContent(this)) R.addItem(this,ItemPossessor.Expire.Player_Drop); if(!CMath.bset(msg.sourceMajor(),CMMsg.MASK_OPTIMIZE)) { R.recoverRoomStats(); if(mob.location()!=R) mob.location().recoverRoomStats(); } } unWear(); setContainer(null); } else if(!msg.amITarget(this)) return; else switch(msg.targetMinor()) { case CMMsg.TYP_SNIFF: CMLib.commands().handleBeingSniffed(msg); break; case CMMsg.TYP_LOOK: case CMMsg.TYP_EXAMINE: CMLib.commands().handleBeingLookedAt(msg); break; case CMMsg.TYP_READ: CMLib.commands().handleBeingRead(msg); break; case CMMsg.TYP_HOLD: CMLib.commands().handleBeingHeld(msg); break; case CMMsg.TYP_WEAR: CMLib.commands().handleBeingWorn(msg); break; case CMMsg.TYP_WIELD: CMLib.commands().handleBeingWielded(msg); break; case CMMsg.TYP_GET: CMLib.commands().handleBeingGetted(msg); break; case CMMsg.TYP_REMOVE: CMLib.commands().handleBeingRemoved(msg); break; case CMMsg.TYP_DROP: CMLib.commands().handleBeingDropped(msg); break; case CMMsg.TYP_WRITE: if(isReadable() && (!(this instanceof Electronics))) setReadableText((readableText()+" "+msg.targetMessage()).trim()); break; case CMMsg.TYP_REWRITE: if(isReadable() && (!(this instanceof Electronics))) { if((""+msg.targetMessage()).startsWith("DELETE ")) setReadableText(""); else if(!CMath.isInteger(""+msg.targetMessage())) setReadableText(""+msg.targetMessage().trim()); } break; case CMMsg.TYP_EXTINGUISH: if(CMLib.flags().isOnFire(this)) { final Ability A=fetchEffect("Burning"); if(A!=null) { A.setAbilityCode(0); A.unInvoke(); delEffect(A); } } break; case CMMsg.TYP_EXPIRE: case CMMsg.TYP_DEATH: destroy(); break; default: break; } } @Override public int recursiveWeight() { return phyStats().weight(); } @Override public void stopTicking() { destroyed=true; // WHY?!?!? CMLib.threads().deleteTick(this,-1); } @Override public void destroy() { if((phyStats().sensesMask()&PhyStats.SENSE_UNDESTROYABLE)>0) return; myContainer=null; CMLib.map().registerWorldObjectDestroyed(null,null,this); try { CMLib.catalog().changeCatalogUsage(this, false); } catch (final Exception t) { } delAllEffects(true); delAllBehaviors(); delAllScripts(); CMLib.threads().deleteTick(this,Tickable.TICKID_ITEM_BEHAVIOR); riding=null; destroyed=true; if(owner!=null) { if (owner instanceof Room) { final Room thisRoom=(Room)owner; if(this instanceof Container) { for(int r=thisRoom.numItems()-1;r>=0;r--) { final Item thisItem = thisRoom.getItem(r); if((thisItem!=null) &&(!thisItem.amDestroyed()) &&(thisItem.container()!=null) &&(thisItem.container()==this)) thisItem.destroy(); } } thisRoom.delItem(this); } else if (owner instanceof MOB) { final MOB mob=(MOB)owner; if(this instanceof Container) { for(int r=mob.numItems()-1;r>=0;r--) { final Item thisItem = mob.getItem(r); if((thisItem!=null) &&(!thisItem.amDestroyed()) &&(thisItem.container()==this)) thisItem.destroy(); } } mob.delItem(this); } } myContainer=null; rawImageName=null; cachedImageName=null; secretIdentity=null; owner=null; affects=null; behaviors=null; scripts=null; } @Override public void removeFromOwnerContainer() { myContainer=null; if(owner==null) return; if (owner instanceof Room) { final Room thisRoom=(Room)owner; for(int r=thisRoom.numItems()-1;r>=0;r--) { final Item thisItem = thisRoom.getItem(r); if((thisItem!=null) &&(thisItem.container()!=null) &&(thisItem.container()==this)) thisItem.removeFromOwnerContainer(); } thisRoom.delItem(this); } else if (owner instanceof MOB) { final MOB mob=(MOB)owner; for(int r=mob.numItems()-1;r>=0;r--) { final Item thisItem = mob.getItem(r); if((thisItem!=null) &&(thisItem.container()!=null) &&(thisItem.container()==this)) thisItem.removeFromOwnerContainer(); } mob.delItem(this); } recoverPhyStats(); } @Override public void addNonUninvokableEffect(final Ability to) { if(to==null) return; if(fetchEffect(to.ID())!=null) return; if(affects==null) affects=new SVector<Ability>(1); to.makeNonUninvokable(); to.makeLongLasting(); affects.addElement(to); to.setAffectedOne(this); } @Override public void addEffect(final Ability to) { if(to==null) return; if(fetchEffect(to.ID())!=null) return; if(affects==null) affects=new SVector<Ability>(1); affects.addElement(to); to.setAffectedOne(this); } @Override public void delEffect(final Ability to) { if(affects==null) return; if(affects.remove(to)) to.setAffectedOne(null); } @Override public void eachEffect(final EachApplicable<Ability> applier) { final List<Ability> affects=this.affects; if((affects!=null)&&(!affects.isEmpty())) { try { for(int a=0;a<affects.size();a++) { final Ability A=affects.get(a); if(A!=null) applier.apply(A); } } catch (final ArrayIndexOutOfBoundsException e) { } } } @Override public void delAllEffects(final boolean unInvoke) { final SVector<Ability> affects=this.affects; if(affects==null) return; Ability keepThisOne=null; for(int a=numEffects()-1;a>=0;a--) { final Ability A=fetchEffect(a); if(A!=null) { if(unInvoke) { if(A.ID().equals("ItemRejuv")) { keepThisOne=A; continue; } A.unInvoke(); } A.setAffectedOne(null); } } affects.clear(); if(keepThisOne != null) { affects.add(keepThisOne); } } @SuppressWarnings("unchecked") @Override public Enumeration<Ability> effects() { return (affects==null)?EmptyEnumeration.INSTANCE:affects.elements(); } @Override public int numEffects() { if(affects==null) return 0; return affects.size(); } @Override public Ability fetchEffect(final int index) { if(affects==null) return null; try { return affects.elementAt(index); } catch (final java.lang.ArrayIndexOutOfBoundsException x) { } return null; } @Override public Ability fetchEffect(final String ID) { if(affects==null) return null; for(final Enumeration<Ability> a=effects();a.hasMoreElements();) { final Ability A=a.nextElement(); if((A!=null)&&(A.ID().equals(ID))) return A; } return null; } /** Manipulation of Behavior objects, which includes * movement, speech, spellcasting, etc, etc.*/ @Override public void addBehavior(final Behavior to) { if(to==null) return; if(behaviors==null) behaviors=new SVector<Behavior>(1); for(final Behavior B : behaviors) { if(B.ID().equals(to.ID())) return; } // first one! so start ticking... if(behaviors.isEmpty()) CMLib.threads().startTickDown(this,Tickable.TICKID_ITEM_BEHAVIOR,1); to.startBehavior(this); behaviors.addElement(to); } @Override public void delAllBehaviors() { final boolean didSomething=(behaviors!=null)&&(!behaviors.isEmpty()); if(didSomething) behaviors.clear(); behaviors=null; if(didSomething && ((scripts==null)||(scripts.isEmpty()))) CMLib.threads().deleteTick(this,Tickable.TICKID_ITEM_BEHAVIOR); } @Override public void delBehavior(final Behavior to) { if(behaviors==null) return; if(behaviors.remove(to)) { to.endBehavior(this); if(((behaviors==null)||(behaviors.isEmpty()))&&((scripts==null)||(scripts.isEmpty()))) CMLib.threads().deleteTick(this,Tickable.TICKID_ITEM_BEHAVIOR); } } @Override public int numBehaviors() { if(behaviors==null) return 0; return behaviors.size(); } @SuppressWarnings("unchecked") @Override public Enumeration<Behavior> behaviors() { return (behaviors==null)?EmptyEnumeration.INSTANCE:behaviors.elements(); } @Override public Behavior fetchBehavior(final int index) { if(behaviors==null) return null; try { return behaviors.elementAt(index); } catch(final java.lang.ArrayIndexOutOfBoundsException x) { } return null; } @Override public Behavior fetchBehavior(final String ID) { if(behaviors==null) return null; for(final Behavior B : behaviors) { if((B!=null)&&(B.ID().equalsIgnoreCase(ID))) return B; } return null; } @Override public void eachBehavior(final EachApplicable<Behavior> applier) { final List<Behavior> behaviors=this.behaviors; if(behaviors!=null) { try { for(int a=0;a<behaviors.size();a++) { final Behavior B=behaviors.get(a); if(B!=null) applier.apply(B); } } catch (final ArrayIndexOutOfBoundsException e) { } } } /** Manipulation of the scripts list */ @Override public void addScript(final ScriptingEngine S) { if(scripts==null) scripts=new SVector<ScriptingEngine>(1); if(S==null) return; if(!scripts.contains(S)) { ScriptingEngine S2=null; for(int s=0;s<scripts.size();s++) { S2=scripts.elementAt(s); if((S2!=null)&&(S2.getScript().equalsIgnoreCase(S.getScript()))) return; } if(scripts.isEmpty()) CMLib.threads().startTickDown(this,Tickable.TICKID_ITEM_BEHAVIOR,1); scripts.addElement(S); } } @Override public void delScript(final ScriptingEngine S) { if(scripts!=null) { if(scripts.remove(S)) { if(scripts.isEmpty()) scripts=new SVector<ScriptingEngine>(1); if(((behaviors==null)||(behaviors.isEmpty()))&&((scripts==null)||(scripts.isEmpty()))) CMLib.threads().deleteTick(this,Tickable.TICKID_ITEM_BEHAVIOR); } } } @Override public void delAllScripts() { final boolean didSomething=(scripts!=null)&&(!scripts.isEmpty()); if(didSomething) scripts.clear(); scripts=null; if(didSomething && ((behaviors==null)||(behaviors.isEmpty()))) CMLib.threads().deleteTick(this,Tickable.TICKID_ITEM_BEHAVIOR); } @Override public int numScripts() { return (scripts == null) ? 0 : scripts.size(); } @SuppressWarnings("unchecked") @Override public Enumeration<ScriptingEngine> scripts() { return (scripts == null) ? EmptyEnumeration.INSTANCE : scripts.elements(); } @Override public ScriptingEngine fetchScript(final int x) { try { return scripts.elementAt(x); } catch (final Exception e) { } return null; } @Override public void eachScript(final EachApplicable<ScriptingEngine> applier) { final List<ScriptingEngine> scripts=this.scripts; if(scripts!=null) { try { for(int a=0;a<scripts.size();a++) { final ScriptingEngine S=scripts.get(a); if(S!=null) applier.apply(S); } } catch (final ArrayIndexOutOfBoundsException e) { } } } protected String tackOns() { final StringBuilder identity=new StringBuilder(""); if(numEffects()>0) identity.append("\n\rHas the following magical properties: "); if(numEffects()>0) { eachEffect(new EachApplicable<Ability>() { @Override public final void apply(final Ability A) { if(A.accountForYourself().length()>0) identity.append("\n\r"+A.accountForYourself()); } }); } return identity.toString(); } @Override public int maxRange() { return 0; } @Override public int minRange() { return 0; } protected static String[] CODES={"CLASS","USES","LEVEL","ABILITY","TEXT"}; @Override public String getStat(final String code) { switch(getCodeNum(code)) { case 0: return ID(); case 1: return "" + usesRemaining(); case 2: return "" + basePhyStats().ability(); case 3: return "" + basePhyStats().level(); case 4: return text(); } return ""; } @Override public void setStat(final String code, final String val) { switch(getCodeNum(code)) { case 0: return; case 1: setUsesRemaining(CMath.s_parseIntExpression(val)); break; case 2: basePhyStats().setLevel(CMath.s_parseIntExpression(val)); break; case 3: basePhyStats().setAbility(CMath.s_parseIntExpression(val)); break; case 4: setMiscText(val); break; } } @Override public String L(final String str, final String... xs) { return CMLib.lang().fullSessionTranslation(str, xs); } protected String I(final String str) { return CMLib.lang().commandWordTranslation(str); } @Override public int getSaveStatIndex() { return (xtraValues == null) ? getStatCodes().length : getStatCodes().length - xtraValues.length; } @Override public String[] getStatCodes() { return CODES; } @Override public boolean isStat(final String code) { return CMParms.indexOf(getStatCodes(), code.toUpperCase().trim()) >= 0; } protected int getCodeNum(final String code) { for(int i=0;i<CODES.length;i++) { if(code.equalsIgnoreCase(CODES[i])) return i; } return -1; } @Override public boolean sameAs(final Environmental E) { if(!(E instanceof StdItem)) return false; final String[] codes=getStatCodes(); for(int i=0;i<codes.length;i++) { if(!E.getStat(codes[i]).equals(getStat(codes[i]))) return false; } return true; } }