package com.planet_ink.coffee_mud.Abilities.Traps; import com.planet_ink.coffee_mud.Abilities.StdAbility; 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.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-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 StdTrap extends StdAbility implements Trap { @Override public String ID() { return "StdTrap"; } private final static String localizedName = CMLib.lang().L("standard trap"); @Override public String name() { return localizedName; } public static final String[] TRIGGER = { "SPRING" }; @Override public String[] triggerStrings() { return TRIGGER; } protected boolean sprung=false; protected int reset=60; // 5 minute reset is standard protected int ableCode=0; protected boolean disabled=false; public StdTrap() { super(); } @Override public int abstractQuality() { return Ability.QUALITY_MALICIOUS; } @Override public int enchantQuality() { return Ability.QUALITY_INDIFFERENT; } @Override protected int canAffectCode() { return 0; } @Override protected int canTargetCode() { return 0; } protected int trapLevel() { return -1; } @Override public void setAbilityCode(final int code) { ableCode = code; } @Override public int abilityCode() { return ableCode; } @Override public boolean isABomb() { return false; } @Override public String requiresToSet() { return ""; } protected List<String> newMessaging=new ArrayList<String>(0); private String invokerName=null; public PairVector<MOB,Integer> safeDirs=null; public int baseRejuvTime(int level) { if(level>=30) level=29; int ticks=(int)Math.round((30.0-(CMath.mul(level,.75)))*30.0); if(ticks<1) ticks=1; return ticks; } public int baseDestructTime(final int level) { return level*30; } public boolean getTravelThroughFlag() { return false; } @Override public boolean disabled() { return (sprung&&disabled) ||(affected==null) ||(affected.fetchEffect(ID())==null); } public boolean doesSaveVsTraps(final MOB target) { int save=target.charStats().getSave(CharStats.STAT_SAVE_TRAPS); if(invoker()!=null) { save += target.phyStats().level(); save -= invoker().phyStats().level(); } return (CMLib.dice().rollPercentage()<=save); } public boolean isLocalExempt(final MOB target) { if(target==null) return false; final Room R=target.location(); if((!canBeUninvoked()) &&(!isABomb()) &&(R!=null)) { if((CMLib.law().getLandTitle(R)!=null) &&(CMLib.law().doesHavePriviledgesHere(target,R))) return true; if((target.isMonster()) &&(target.getStartRoom()!=null) &&(target.getStartRoom().getArea()==R.getArea())) return true; } return false; } protected boolean canInvokeTrapOn(final MOB invoker, final MOB target) { if((invoker==null) ||(invoker.mayIFight(target) &&(!invoker.getGroupMembers(new HashSet<MOB>()).contains(target)))) { if(!isLocalExempt(target)) return true; } return false; } @Override public void disable() { disabled=true; sprung=true; if(!canBeUninvoked()) { tickDown=getReset(); CMLib.threads().startTickDown(this,Tickable.TICKID_TRAP_RESET,1); } else unInvoke(); } @Override public void setReset(final int Reset) { reset = Reset; } @Override public int getReset() { return reset; } @Override public MOB invoker() { if(invoker==null) { if((invokerName!=null)&&(!invokerName.equalsIgnoreCase("null"))) invoker=CMLib.players().getLoadPlayer(invokerName); if(invoker==null) { invoker=CMClass.getMOB("StdMOB"); invoker.setLocation(CMClass.getLocale("StdRoom")); invoker.basePhyStats().setLevel(affected.phyStats().level()); invoker.phyStats().setLevel(affected.phyStats().level()); } } else invokerName=invoker.Name(); return super.invoker(); } @Override public int classificationCode() { return Ability.ACODE_TRAP; } @Override public void setMiscText(String text) { text=text.trim(); if(text.startsWith("`")) { final int x=text.indexOf("` ",1); if(x>=0) { invokerName=text.substring(1,x); text=text.substring(x+2).trim(); } } while(text.startsWith("\"")) { final int x=text.indexOf("\"",1); if(x>=0) { newMessaging.add(text.substring(1,x)); text=text.substring(x+1).trim(); } else break; } if(text.startsWith(":")) { final int x=text.indexOf(':'); final int y=text.indexOf(':',x+1); if((x>=0)&&(y>x)&&(CMath.isInteger(text.substring(x+1,y).trim()))) { setAbilityCode(CMath.s_int(text.substring(x+1,y).trim())); text=text.substring(y+1).trim(); } } super.setMiscText(text); } @Override public String text() { return "`"+invokerName+"` :"+abilityCode()+":"+super.text(); } public synchronized PairVector<MOB,Integer> getSafeDirs() { if(safeDirs == null) safeDirs=new PairVector<MOB,Integer>(); return safeDirs; } @Override public CMObject copyOf() { final StdTrap obj=(StdTrap)super.copyOf(); obj.safeDirs=null; return obj; } @Override public boolean okMessage(final Environmental myHost, final CMMsg msg) { if((!disabled())&&(affected instanceof Item)) { if((msg.tool()==affected) &&(msg.targetMinor()==CMMsg.TYP_GIVE) &&(msg.targetMessage()!=null) &&(msg.target() instanceof MOB) &&(!msg.source().getGroupMembers(new HashSet<MOB>()).contains(msg.target()))) { msg.source().tell((MOB)msg.target(),msg.tool(),null,L("<S-NAME> can't accept <T-NAME>.")); return false; } } if((!sprung) && CMath.bset(canAffectCode(),Ability.CAN_ROOMS) && getTravelThroughFlag() && msg.amITarget(affected) && (affected instanceof Room) && (msg.tool() instanceof Exit)) { final Room room=(Room)affected; if ((msg.targetMinor()==CMMsg.TYP_LEAVE)||(msg.targetMinor()==CMMsg.TYP_FLEE)) { final int movingInDir=CMLib.map().getExitDir(room, (Exit)msg.tool()); if((movingInDir!=Directions.DOWN)&&(movingInDir!=Directions.UP)) { final PairVector<MOB,Integer> safeDirs=getSafeDirs(); synchronized(safeDirs) { for(final Iterator<Pair<MOB,Integer>> i=safeDirs.iterator();i.hasNext();) { final Pair<MOB,Integer> p=i.next(); if(p.first == msg.source()) { i.remove(); if(movingInDir==p.second.intValue()) return true; spring(msg.source()); return !sprung(); } } } } } } return super.okMessage(myHost,msg); } @Override public void activateBomb() { if(isABomb()) { tickDown=getReset(); sprung=false; disabled=false; CMLib.threads().startTickDown(this,Tickable.TICKID_TRAP_RESET,1); } } @Override public void executeMsg(final Environmental myHost, final CMMsg msg) { if(sprung) { super.executeMsg(myHost,msg); return; } if(CMath.bset(canAffectCode(),Ability.CAN_EXITS)) { if(msg.amITarget(affected)) { if((affected instanceof Exit) &&(((Exit)affected).hasADoor()) &&(((Exit)affected).hasALock()) &&(((Exit)affected).isLocked())) { if(msg.targetMinor()==CMMsg.TYP_UNLOCK) spring(msg.source()); } else if((affected instanceof Container) &&(((Container)affected).hasADoor()) &&(((Container)affected).hasALock()) &&(((Container)affected).isLocked())) { if(msg.targetMinor()==CMMsg.TYP_UNLOCK) spring(msg.source()); } else if(msg.targetMinor()==CMMsg.TYP_OPEN) spring(msg.source()); } } else if(CMath.bset(canAffectCode(),Ability.CAN_ITEMS)) { if(isABomb()) { if(msg.amITarget(affected)) { if((msg.targetMinor()==CMMsg.TYP_HOLD) &&(msg.source().isMine(affected))) { msg.source().tell(msg.source(),affected,null,L("You activate <T-NAME>.")); activateBomb(); } } } else if(msg.amITarget(affected)) { if(((msg.targetMinor()==CMMsg.TYP_GET)||(msg.targetMinor()==CMMsg.TYP_PUSH)||(msg.targetMinor()==CMMsg.TYP_PULL)) &&(!msg.source().isMine(affected))) spring(msg.source()); } } else if(CMath.bset(canAffectCode(), Ability.CAN_ROOMS) && msg.amITarget(affected) &&(msg.targetMinor()==CMMsg.TYP_ENTER)) { if(getTravelThroughFlag()) { if ((affected instanceof Room) && (msg.tool() instanceof Exit)) { final Room room=(Room)affected; final int movingInDir=CMLib.map().getExitDir(room, (Exit)msg.tool()); if((movingInDir!=Directions.DOWN)&&(movingInDir!=Directions.UP)) { final PairVector<MOB,Integer> safeDirs=getSafeDirs(); synchronized(safeDirs) { final int dex=safeDirs.indexOfFirst(msg.source()); if(dex>=0) safeDirs.remove(dex); while(safeDirs.size()>room.numInhabitants()+1) safeDirs.remove(0); safeDirs.add(new Pair<MOB,Integer>(msg.source(),Integer.valueOf(movingInDir))); } } } } else if(!msg.source().isMine(affected)) spring(msg.source()); } super.executeMsg(myHost,msg); } @Override public boolean maySetTrap(final MOB mob, final int asLevel) { if(mob==null) return false; if(trapLevel()<0) return false; if(asLevel<0) return true; if(asLevel>=trapLevel()) return true; return false; } @Override public boolean canReSetTrap(final MOB mob) { final Physical P=affected; if(P==null) { if(mob!=null) mob.tell(L("This trap is not presently set.")); return false; } if(mob!=null) { if((!maySetTrap(mob,mob.phyStats().level())) &&(!mob.charStats().getCurrentClass().leveless()) &&(!CMSecurity.isDisabled(CMSecurity.DisFlag.LEVELS))) { mob.tell(L("You are not high enough level (@x1) to set that trap.",""+trapLevel())); return false; } } final Trap T=(Trap)P.fetchEffect(ID()); if(T!=this) { if(mob!=null) mob.tell(L("This trap is not presently set on @x1.",P.name())); return false; } if(T.invoker() != mob) { if(mob!=null) mob.tell(L("The trap was not set by you.")); return false; } return true; } @Override public boolean canSetTrapOn(final MOB mob, final Physical P) { if(mob!=null) { if((!maySetTrap(mob,mob.phyStats().level())) &&(!mob.charStats().getCurrentClass().leveless()) &&(!CMSecurity.isDisabled(CMSecurity.DisFlag.LEVELS))) { mob.tell(L("You are not high enough level (@x1) to set that trap.",""+trapLevel())); return false; } } if(P.fetchEffect(ID())!=null) { if(mob!=null) mob.tell(L("This trap is already set on @x1.",P.name())); return false; } if(!canAffect(P)) { if(mob!=null) mob.tell(L("You can't set '@x1' on @x2.",name(),P.name())); return false; } if((canAffectCode()&Ability.CAN_EXITS)==Ability.CAN_EXITS) { if((P instanceof Item)&&(!(P instanceof Container))) { if(mob!=null) mob.tell(L("@x1 has no lid, so '@x2' cannot be set on it.",P.name(),name())); return false; } if(((P instanceof Exit)&&(!(((Exit)P).hasADoor())))) { if(mob!=null) mob.tell(L("@x1 has no door, so '@x2' cannot be set on it.",P.name(),name())); return false; } if(((P instanceof Container)&&(!(((Container)P).hasADoor())))) { if(mob!=null) mob.tell(L("@x1 has no lid, so '@x2' cannot be set on it.",P.name(),name())); return false; } } return true; } @Override public List<Item> getTrapComponents() { return new Vector<Item>(1); } @Override public Trap setTrap(final MOB mob, final Physical P, final int trapBonus, final int qualifyingClassLevel, final boolean perm) { if(P==null) return null; final int rejuv=baseRejuvTime(qualifyingClassLevel+trapBonus); final Trap T=(Trap)copyOf(); T.setReset(rejuv); T.setInvoker(mob); T.setSavable(false); T.setAbilityCode(trapBonus); P.addEffect(T); if(perm) { T.setSavable(true); T.makeNonUninvokable(); } else if(!isABomb()) CMLib.threads().startTickDown(T,Tickable.TICKID_TRAP_DESTRUCTION,baseDestructTime(qualifyingClassLevel+trapBonus)); return T; } @Override public void setInvoker(final MOB mob) { if(mob!=null) invokerName=mob.Name(); super.setInvoker(mob); } @Override public boolean tick(final Tickable ticking, final int tickID) { if((unInvoked)&&(canBeUninvoked())) return false; if(tickID==Tickable.TICKID_TRAP_DESTRUCTION) { if(canBeUninvoked()) disable(); return false; } else if((tickID==Tickable.TICKID_TRAP_RESET)&&(getReset()>0)) { if((--tickDown)<=0) { if((isABomb()) &&(affected instanceof Item) &&(((Item)affected).owner()!=null)) { final Item I=(Item)affected; if(I.owner() instanceof MOB) spring((MOB)I.owner()); else if(I.owner() instanceof Room) { final Room R=(Room)I.owner(); for(int i=R.numInhabitants()-1;i>=0;i--) { final MOB M=R.fetchInhabitant(i); if(M!=null) spring(M); } } disable(); unInvoke(); I.destroy(); return false; } sprung=false; disabled=false; return false; } } return true; } @Override public void resetTrap(final MOB mob) { if(sprung()) { sprung=false; disabled=false; if(!isABomb()) CMLib.threads().deleteTick(this, Tickable.TICKID_TRAP_RESET); } } @Override public boolean sprung() { return sprung && (!disabled()); } @Override public void spring(final MOB target) { sprung=true; disabled=false; tickDown=getReset(); if(!isABomb()) CMLib.threads().startTickDown(this,Tickable.TICKID_TRAP_RESET,1); } protected RawMaterial findFirstResource(final Room room, final int resource) { return CMLib.materials().findFirstResource(room, resource); } protected RawMaterial findMostOfMaterial(final Room room, final int material) { return CMLib.materials().findMostOfMaterial(room, material); } protected void destroyResources(final Room room, final int resource, final String subType, final int number) { CMLib.materials().destroyResourcesValue(room,number,resource,subType.hashCode(),0,0); } protected int findNumberOfResource(final Room room, final RawMaterial resource) { return CMLib.materials().findNumberOfResource(room, resource); } @Override public boolean invoke(final MOB mob, final List<String> commands, final Physical givenTarget, final boolean auto, final int asLevel) { final MOB target=super.getTarget(mob, commands, givenTarget); if(target == null) return false; if(!super.proficiencyCheck(mob, 0, auto)) return true; if(!super.invoke(mob, commands, target, auto, asLevel)) return false; final StdTrap T=(StdTrap)copyOf(); T.setInvoker(mob); T.setAffectedOne(mob); T.spring(target); return true; } }