package com.planet_ink.coffee_mud.Exits; 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.Basic.StdItem; 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 StdExit implements Exit { @Override public String ID() { return "StdExit"; } protected PhyStats phyStats = (PhyStats) CMClass.getCommon("DefaultPhyStats"); protected PhyStats basePhyStats = (PhyStats) CMClass.getCommon("DefaultPhyStats"); protected boolean isOpen = true; protected boolean isLocked = false; protected String miscText = ""; protected String cachedImageName = null; protected String rawImageName = null; protected boolean amDestroyed = false; protected short usage = 0; protected String lastRoomID = ""; protected CList<Ability> affects = null; protected CList<Behavior> behaviors = null; protected CList<ScriptingEngine>scripts = null; protected Exit me = this; protected ApplyAffectPhyStats<Ability> affectPhyStats = new ApplyAffectPhyStats<Ability>(this); public StdExit() { super(); //CMClass.bumpCounter(this,CMClass.CMObjectType.EXIT); isOpen=!defaultsClosed(); isLocked=defaultsLocked(); } /* protected void finalize() { CMClass.unbumpCounter(this, CMClass.CMObjectType.EXIT); }// removed for mem & perf */ @Override public void initializeClass() { } @Override public String Name() { return "a walkway"; } @Override public boolean hasADoor() { return false; } @Override public boolean hasALock() { return false; } @Override public boolean defaultsLocked() { return false; } @Override public boolean defaultsClosed() { return false; } @Override public String displayText() { return ""; } @Override public String description() { return ""; } @Override public String description(final MOB viewerMob) { return description(); } @Override public String doorName() { return "door"; } @Override public String closedText() { return "a closed door"; } @Override public String closeWord() { return "close"; } @Override public String openWord() { return "open"; } @Override public String displayText(final MOB viewerMob) { return displayText(); } @Override public String name(final MOB viewerMob) { return name(); } @Override public int getTickStatus() { return Tickable.STATUS_NOT; } @Override public short exitUsage(final short change) { if(change<0) { if((-change)>usage) usage=0; else usage+=change; } else if(Short.MAX_VALUE-change>usage) usage+=change; return usage; } @Override public void setName(final String newName) { } @Override public String name() { if(phyStats().newName()!=null) return phyStats().newName(); return Name(); } @Override public PhyStats phyStats() { return phyStats; } @Override public PhyStats basePhyStats() { return basePhyStats; } @Override public void recoverPhyStats() { basePhyStats.copyInto(phyStats); eachEffect(affectPhyStats); } @Override public void setBasePhyStats(final PhyStats newStats) { basePhyStats=(PhyStats)newStats.copyOf(); } @Override public void destroy() { CMLib.map().registerWorldObjectDestroyed(null,null,this); CMLib.threads().deleteTick(this,-1); affects=null; rawImageName=null; cachedImageName=null; behaviors=null; scripts=null; miscText=null; amDestroyed=true; } @Override public boolean amDestroyed() { return amDestroyed; } @Override public boolean isSavable() { return !amDestroyed && CMLib.flags().isSavable(this); } @Override public void setSavable(final boolean truefalse) { CMLib.flags().setSavable(this, truefalse); } @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().length()==0)) rawImageName=null; else rawImageName=newImage; if((cachedImageName!=null)&&(!cachedImageName.equals(newImage))) cachedImageName=null; } @Override public CMObject newInstance() { try { return this.getClass().newInstance(); } catch(final Exception e) { Log.errOut(ID(),e); } return new StdExit(); } @Override public boolean isGeneric() { return false; } protected void cloneFix(final Exit X) { me=this; basePhyStats=(PhyStats)X.basePhyStats().copyOf(); phyStats=(PhyStats)X.phyStats().copyOf(); affectPhyStats = new ApplyAffectPhyStats<Ability>(this); affects=null; behaviors=null; scripts=null; for(final Enumeration<Behavior> e=X.behaviors();e.hasMoreElements();) { final Behavior B=e.nextElement(); if(B!=null) addBehavior((Behavior)B.copyOf()); } for(final Enumeration<ScriptingEngine> e=X.scripts();e.hasMoreElements();) { final ScriptingEngine SE=e.nextElement(); if(SE!=null) addScript((ScriptingEngine)SE.copyOf()); } } @Override public CMObject copyOf() { try { final StdExit E=(StdExit)this.clone(); //CMClass.bumpCounter(this,CMClass.CMObjectType.EXIT);//removed for mem & perf E.cloneFix(this); return E; } catch(final CloneNotSupportedException e) { return this.newInstance(); } } @Override public void setMiscText(final String newMiscText) { miscText = newMiscText; } @Override public String text() { return miscText; } @Override public String miscTextFormat() { return CMParms.FORMAT_UNDEFINED; } @Override public long expirationDate() { return 0; } @Override public void setExpirationDate(final long time) { } @Override public void setDisplayText(final String newDisplayText) { } @Override public void setDescription(final String newDescription) { } @Override public int maxRange() { return Integer.MAX_VALUE; } @Override public int minRange() { return Integer.MIN_VALUE; } protected final String closeWordPastTense() { return CMLib.english().makePastTense(closeWord(), "closed"); } protected final String openWordPastTense() { return CMLib.english().makePastTense(openWord(), "opened"); } @Override public boolean okMessage(final Environmental myHost, final CMMsg msg) { MsgListener N=null; for(int b=0;b<numBehaviors();b++) { N=fetchBehavior(b); if((N!=null)&&(!N.okMessage(this,msg))) return false; } for(int s=0;s<numScripts();s++) { N=fetchScript(s); if((N!=null)&&(!N.okMessage(this,msg))) return false; } for(final Enumeration<Ability> a=effects();a.hasMoreElements();) { N=a.nextElement(); if((N!=null)&&(!N.okMessage(this,msg))) return false; } final MOB mob=msg.source(); if((!msg.amITarget(this))&&(msg.tool()!=this)) return true; else if(msg.targetMinor()==CMMsg.NO_EFFECT) return true; else switch(msg.targetMinor()) { case CMMsg.TYP_LOOK: case CMMsg.TYP_EXAMINE: case CMMsg.TYP_READ: case CMMsg.TYP_WASREAD: case CMMsg.TYP_OK_VISUAL: case CMMsg.TYP_KNOCK: case CMMsg.TYP_OK_ACTION: return true; case CMMsg.TYP_ENTER: if(msg.target() instanceof Room) lastRoomID=CMLib.map().getExtendedRoomID((Room)msg.target()); if((hasADoor())&&(!isOpen())&&(mob.phyStats().height()>=0)) { if(!CMLib.flags().canBeSeenBy(this,mob)) mob.tell(L("You can't go that way.")); else mob.tell(L("The @x1 is @x2.",doorName(),closeWordPastTense())); return false; } if((CMLib.flags().isFlying(this)) &&(!CMLib.flags().isInFlight(mob)) &&(!CMLib.flags().isFalling(mob))) { mob.tell(L("You can't fly.")); return false; } if((CMLib.flags().isClimbing(this)) &&(!CMLib.flags().isFalling(this)) &&(!CMLib.flags().isClimbing(mob)) &&(!CMLib.flags().isInFlight(mob))) { Rideable ladder=null; if(msg.target() instanceof Room) ladder=CMLib.tracking().findALadder(mob,(Room)msg.target()); if(ladder!=null) CMLib.tracking().postMountLadder(mob,ladder); if((!CMLib.flags().isClimbing(mob)) &&(!CMLib.flags().isFalling(mob))) { mob.tell(L("You need to climb that way, if you know how.")); return false; } } return true; case CMMsg.TYP_LEAVE: case CMMsg.TYP_FLEE: return true; case CMMsg.TYP_CLOSE: { if(closeWord().length()==0) setExitParams(doorName(),openWord(),"close",closedText()); if(isOpen()) { if(!hasADoor()) { mob.tell(L("There is nothing to @x1!",closeWord())); return false; } return true; } mob.tell(L("The @x1 is already @x2.",doorName(),closeWordPastTense())); return false; } case CMMsg.TYP_OPEN: { if(openWord().length()==0) setExitParams(doorName(),"open",closeWord(),closedText()); if(!hasADoor()) { mob.tell(L("There is nothing to @x1 that way!",openWord())); return false; } if(isOpen()) { mob.tell(L("The @x1 is already @x2!",doorName(),openWordPastTense())); return false; } if(isLocked()&&hasALock()) { mob.tell(L("The @x1 is locked.",doorName())); return false; } return true; } case CMMsg.TYP_PUSH: if((isOpen())||(!hasADoor())) { mob.tell(L("There is nothing to push over there.")); return false; } return true; case CMMsg.TYP_DELICATE_HANDS_ACT: case CMMsg.TYP_JUSTICE: case CMMsg.TYP_CAST_SPELL: case CMMsg.TYP_SPEAK: return true; case CMMsg.TYP_PULL: if((isOpen())||(!hasADoor())) { mob.tell(L("There is nothing to pull over there.")); return false; } return true; case CMMsg.TYP_LOCK: if(!hasADoor()) { mob.tell(L("There is nothing to lock that way!")); return false; } //$FALL-THROUGH$ case CMMsg.TYP_UNLOCK: if(!hasADoor()) { mob.tell(L("There is nothing to unlock that way!")); return false; } if(isOpen()) { mob.tell(L("The @x1 is already @x2!",doorName(),openWord())); return false; } else if(!hasALock()) { mob.tell(L("There is no lock!")); return false; } else { if((!isLocked())&&(msg.targetMinor()==CMMsg.TYP_UNLOCK)) { mob.tell(L("The @x1 is not locked.",doorName())); return false; } else if((isLocked())&&(msg.targetMinor()==CMMsg.TYP_LOCK)) { mob.tell(L("The @x1 is already locked.",doorName())); return false; } else { for(int i=0;i<mob.numItems();i++) { final Item item=mob.getItem(i); if((item!=null) &&(item instanceof DoorKey) &&((DoorKey)item).getKey().equals(keyName()) &&((item.container()==null) ||((item.container().container()==null) &&((item.container().containTypes()&Container.CONTAIN_KEYS)>0))) &&(CMLib.flags().canBeSeenBy(item,mob))) { if(msg.tool()==null) msg.setTool(item); return true; } } mob.tell(L("You don't seem to have the key.")); return false; } } //break; default: break; } if(msg.amITarget(this)) { mob.tell(L("You can't do that.")); return false; } return true; } @Override public StringBuilder viewableText(final MOB mob, final Room room) { final StringBuilder viewMsg=new StringBuilder(""); final CMFlagLibrary flags=CMLib.flags(); if(mob.isAttributeSet(MOB.Attrib.SYSOPMSGS)) { if(room==null) viewMsg.append("^Z(null)^.^? "); else viewMsg.append("^H("+CMLib.map().getExtendedRoomID(room)+")^? "+room.displayText(mob)+flags.getDispositionBlurbs(room,mob)+" "); viewMsg.append("via ^H("+ID()+")^? "+(isOpen()?displayText():closedText())); } else if(((flags.canBeSeenBy(this,mob))||(isOpen()&&hasADoor())) &&(flags.isSeeable(this))) { if(isOpen()) { if(room != null) { if(!flags.canBeSeenBy(room,mob)) viewMsg.append("darkness"); else if(displayText().length()>0) viewMsg.append(displayText() +flags.getDispositionBlurbs(this,mob) +flags.getDispositionBlurbs(room,mob)); else if(room!=null) viewMsg.append(room.displayText(mob)+flags.getDispositionBlurbs(room,mob)); } else if(displayText().length()>0) viewMsg.append(displayText()+flags.getDispositionBlurbs(this,mob)); } else if((flags.canBeSeenBy(this,mob)) &&(closedText().trim().length()>0)) viewMsg.append(closedText()+flags.getDispositionBlurbs(this,mob)); } return viewMsg; } @Override public void executeMsg(final Environmental myHost, final CMMsg msg) { 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.amITarget(this))&&(msg.tool()!=this)) return; switch(msg.targetMinor()) { 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_CLOSE: if((!hasADoor())||(!isOpen())) return; isOpen=false; break; case CMMsg.TYP_OPEN: if((!hasADoor())||(isOpen())) return; if(defaultsClosed()||defaultsLocked()) { CMLib.threads().deleteTick(this,Tickable.TICKID_EXIT_REOPEN); CMLib.threads().startTickDown(this,Tickable.TICKID_EXIT_REOPEN,openDelayTicks()); } isLocked=false; isOpen=true; break; case CMMsg.TYP_LOCK: if((!hasADoor())||(!hasALock())||(isLocked())) return; isOpen=false; isLocked=true; break; case CMMsg.TYP_PULL: case CMMsg.TYP_PUSH: mob.tell(L("It doesn't appear to be doing any good.")); break; case CMMsg.TYP_UNLOCK: if((!hasADoor())||(!hasALock())||(isOpen())||(!isLocked())) return; isLocked=false; break; default: break; } } @Override public int compareTo(final CMObject o) { return CMClass.classID(this).compareToIgnoreCase(CMClass.classID(o)); } @Override public boolean tick(final Tickable ticking, final int tickID) { if(amDestroyed()) return false; if(usage<=0) { destroy(); return false; } if(tickID==Tickable.TICKID_EXIT_REOPEN) { if(defaultsClosed()) isOpen=false; if(defaultsLocked()) { isOpen=false; isLocked=true; } return false; } else if(tickID==Tickable.TICKID_EXIT_BEHAVIOR) { if(numBehaviors()>0) { eachBehavior(new EachApplicable<Behavior>() { @Override public final void apply(final Behavior B) { B.tick(ticking, tickID); } }); } if(numScripts()>0) { eachScript(new EachApplicable<ScriptingEngine>() { @Override public final void apply(final ScriptingEngine S) { S.tick(ticking, tickID); } }); } return !amDestroyed(); } else { if(numEffects()>0) { eachEffect(new EachApplicable<Ability>() { @Override public final void apply(final Ability A) { if(!A.tick(ticking,tickID)) A.unInvoke(); } }); } return true; } } @Override public boolean isOpen() { return isOpen; } @Override public boolean isLocked() { return isLocked; } @Override public void setDoorsNLocks(final boolean newHasADoor, final boolean newIsOpen, final boolean newDefaultsClosed, final boolean newHasALock, final boolean newIsLocked, final boolean newDefaultsLocked) { isOpen=newIsOpen; isLocked=newIsLocked; } @Override public String readableText() { return (isReadable() ? miscText : ""); } @Override public boolean isReadable() { return false; } @Override public void setReadable(final boolean isTrue) { } @Override public void setReadableText(final String text) { miscText = temporaryDoorLink() + text; } @Override public void setExitParams(final String newDoorName, final String newCloseWord, final String newOpenWord, final String newClosedText) { } @Override public String keyName() { return (hasALock() ? miscText : ""); } @Override public void setKeyName(final String newKeyName) { miscText = temporaryDoorLink() + newKeyName; } @Override public Room lastRoomUsedFrom(final Room fromRoom) { return CMLib.map().getRoom(lastRoomID); } @Override public void affectPhyStats(final Physical affected, final PhyStats affectableStats) { }// exits will never be asked this, so this method should always do NOTHING @Override public void affectCharStats(final MOB affectedMob, final CharStats affectableStats) {// exits will never be asked this, so this method should always do NOTHING } @Override public void affectCharState(final MOB affectedMob, final CharState affectableMaxState) {// exits will never be asked this, so this method should always do NOTHING } @Override public String temporaryDoorLink() { if(miscText.startsWith("{#")) { final int x=miscText.indexOf("#}"); if(x>=0) return miscText.substring(2,x); } return ""; } @Override public void setTemporaryDoorLink(final String link) { if(link.startsWith("{{#")) { final int x=link.indexOf("#}}"); if(x>=0) lastRoomID=link.substring(3,x); return; } if(miscText.startsWith("{#")) { final int x=miscText.indexOf("#}"); if(x>=0) miscText=miscText.substring(x+2); } if(link.length()>0) miscText="{#"+link+"#}"+miscText; } @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.add(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.add(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) return; 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 CList<Ability> affects=this.affects; if(affects==null) return; for(int a=numEffects()-1;a>=0;a--) { final Ability A=fetchEffect(a); if(A!=null) { if(unInvoke) A.unInvoke(); A.setAffectedOne(null); } } affects.clear(); } @Override @SuppressWarnings("unchecked") 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.get(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(behaviors==null) behaviors=new SVector<Behavior>(1); if(to==null) return; for(final Behavior B : behaviors) { if((B!=null)&&(B.ID().equals(to.ID()))) return; } // first one! so start ticking... if(behaviors.size()==0) CMLib.threads().startTickDown(this,Tickable.TICKID_EXIT_BEHAVIOR,1); to.startBehavior(this); behaviors.add(to); } @Override public void delBehavior(final Behavior to) { if(behaviors==null) return; if(behaviors.remove(to)) to.endBehavior(this); if(((behaviors==null)||(behaviors.size()==0))&&((scripts==null)||(scripts.size()==0))) CMLib.threads().deleteTick(this,Tickable.TICKID_EXIT_BEHAVIOR); } @Override public void delAllBehaviors() { final boolean didSomething=(behaviors!=null)&&(behaviors.size()>0); if(didSomething) behaviors.clear(); behaviors=null; if(didSomething && ((scripts==null)||(scripts.size()==0))) CMLib.threads().deleteTick(this,Tickable.TICKID_EXIT_BEHAVIOR); } @Override public int numBehaviors() { if(behaviors==null) return 0; return behaviors.size(); } @Override @SuppressWarnings("unchecked") 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.get(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.get(s); if((S2!=null)&&(S2.getScript().equalsIgnoreCase(S.getScript()))) return; } if(scripts.size()==0) CMLib.threads().startTickDown(this,Tickable.TICKID_EXIT_BEHAVIOR,1); scripts.add(S); } } @Override public void delScript(final ScriptingEngine S) { if(scripts!=null) { if(scripts.remove(S)) { if(scripts.size()==0) scripts=new SVector<ScriptingEngine>(1); if(((behaviors==null)||(behaviors.size()==0))&&((scripts==null)||(scripts.size()==0))) CMLib.threads().deleteTick(this,Tickable.TICKID_EXIT_BEHAVIOR); } } } @Override public void delAllScripts() { final boolean didSomething=(scripts!=null)&&(scripts.size()>0); if(didSomething) scripts.clear(); scripts=null; if(didSomething && ((behaviors==null)||(behaviors.size()==0))) CMLib.threads().deleteTick(this,Tickable.TICKID_EXIT_BEHAVIOR); } @Override public int numScripts() { return (scripts == null) ? 0 : scripts.size(); } @Override @SuppressWarnings("unchecked") public Enumeration<ScriptingEngine> scripts() { return (scripts == null) ? EmptyEnumeration.INSTANCE : scripts.elements(); } @Override public ScriptingEngine fetchScript(final int x) { try { return scripts.get(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) { } } @Override public int openDelayTicks() { return 45; } @Override public void setOpenDelayTicks(final int numTicks) { } @Override public String L(final String str, final String... xs) { return CMLib.lang().fullSessionTranslation(str, xs); } @Override public int getSaveStatIndex() { return getStatCodes().length; } private static final String[] CODES = { "CLASS", "TEXT" }; @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 String getStat(final String code) { switch(getCodeNum(code)) { case 0: return ID(); case 1: return text(); } return ""; } @Override public void setStat(final String code, final String val) { switch(getCodeNum(code)) { case 0: return; case 1: setMiscText(val); break; } } @Override public boolean sameAs(final Environmental E) { if(!(E instanceof StdExit)) 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; } }