package com.planet_ink.coffee_mud.Behaviors; import com.planet_ink.coffee_mud.core.interfaces.*; import com.planet_ink.coffee_mud.core.interfaces.ItemPossessor.Expire; import com.planet_ink.coffee_mud.core.interfaces.ItemPossessor.Move; 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.TrackingLibrary; 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.lang.ref.WeakReference; 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 Patroller extends ActiveTicker { @Override public String ID() { return "Patroller"; } @Override protected int canImproveCode() { return Behavior.CAN_MOBS | Behavior.CAN_ITEMS; } @Override public long flags() { return Behavior.FLAG_MOBILITY; } protected int step = 0; protected int diameter = 20; protected boolean rideOk = false; protected boolean rideOnly = false; protected List<Room> correction = null; protected List<String> cachedSteps = null; protected int tickStatus = Tickable.STATUS_NOT; protected volatile int rideCheckCt = 0; protected WeakReference<Room> startRoom=new WeakReference<Room>(null); @Override public String accountForYourself() { return "regular patrolling"; } public Patroller() { super(); minTicks=5; maxTicks=10; chance=100; rideOk=false; diameter=20; tickReset(); } @Override public void setParms(final String newParms) { super.setParms(newParms); final String rideokString=CMParms.getParmStr(newParms,"rideok","false"); rideOnly=rideokString.equalsIgnoreCase("only"); rideOk=rideOnly||rideokString.equalsIgnoreCase("true"); diameter=CMParms.getParmInt(newParms,"diameter",20); cachedSteps = null; } protected List<String> getSteps() { if(cachedSteps != null) return cachedSteps; final Vector<String> V=new Vector<String>(); String path=getParms().trim(); int x=path.indexOf(';'); if(x<0) return V; path=path.substring(x+1).trim(); x=path.indexOf(';'); String s=null; while(x>=0) { s=path.substring(0,x).trim(); if(s.length()>0) V.addElement(s); path=path.substring(x+1).trim(); x=path.indexOf(';'); } if(path.length()>0) V.addElement(path); if(V.size()>1) { for(int i=V.size()-1;i>=0;i--) { s=V.elementAt(i); if(s.equalsIgnoreCase("RESTART")||s.equalsIgnoreCase("REPEAT")) break; final int dir=CMLib.directions().getGoodDirectionCode(s); if(dir>=0) V.addElement(CMLib.directions().getDirectionName(Directions.getOpDirectionCode(dir))); else if(i<(V.size()-1)) V.addElement(V.elementAt(i)); } } V.trimToSize(); cachedSteps = V; return cachedSteps; } @Override public boolean okMessage(final Environmental host, final CMMsg msg) { if((rideOnly) &&(rideCheckCt<=0) &&(rideOk) &&(host instanceof Rideable) &&((msg.targetMinor()==CMMsg.TYP_ENTER)||(msg.targetMinor()==CMMsg.TYP_LEAVE)) &&(msg.source()!=host) &&(msg.source().riding()==host) &&(!(host instanceof BoardableShip))) { if(host instanceof MOB) msg.source().tell(L("You must dismount before you can do that.")); else msg.source().tell(L("You must disembark before you can do that.")); return false; } return super.okMessage(host,msg); } @Override public int getTickStatus() { return tickStatus; } @Override public boolean tick(final Tickable ticking, final int tickID) { if(!super.tick(ticking,tickID)) return false; if((startRoom.get()==null)&&(ticking instanceof Physical)) startRoom=new WeakReference<Room>(CMLib.map().roomLocation((Physical)ticking)); if(canAct(ticking,tickID)) { tickStatus=Tickable.STATUS_MISC+0; if(!rideOk) { if(((ticking instanceof Rideable) &&(((Rideable)ticking).numRiders()>0)) ||((ticking instanceof MOB) &&(((MOB)ticking).amFollowing()!=null))) { tickStatus=Tickable.STATUS_NOT; return true; } } if((ticking instanceof Physical) &&(!CMLib.flags().canTrack((Physical)ticking)) && (CMLib.dice().roll(1,100,0)>1)) { tickStatus=Tickable.STATUS_NOT; return true; } if((ticking instanceof BoardableShip) &&(((BoardableShip)ticking).getShipItem() instanceof SailingShip)) { final SailingShip ship=(SailingShip)((BoardableShip)ticking).getShipItem(); if((ship.isInCombat()) ||((ship.subjectToWearAndTear())&&(ship.usesRemaining()<=0)) ||(CMLib.flags().isFalling(ship))) { tickStatus=Tickable.STATUS_NOT; return true; } } tickStatus=Tickable.STATUS_MISC+1; ArrayList<Rider> riders=null; if((ticking instanceof Rideable) &&(!(ticking instanceof BoardableShip))) { riders=new ArrayList<Rider>(((Rideable)ticking).numRiders()); for(int i=0;i<((Rideable)ticking).numRiders();i++) riders.add(((Rideable)ticking).fetchRider(i)); } tickStatus=Tickable.STATUS_START; Room thisRoom=null; if(ticking instanceof MOB) thisRoom=((MOB)ticking).location(); else if((ticking instanceof Item) &&(((Item)ticking).owner() instanceof Room) &&(!((Item)ticking).amDestroyed())) thisRoom=(Room)((Item)ticking).owner(); if(thisRoom instanceof GridLocale) { final Room R=((GridLocale)thisRoom).getRandomGridChild(); if(R!=null) { if(ticking instanceof Item) R.moveItemTo((Item)ticking); else if((ticking instanceof MOB) &&(CMLib.flags().isInTheGame((MOB)ticking,true))) { R.bringMobHere((MOB)ticking,true); R.show((MOB)ticking,R,null,CMMsg.MASK_ALWAYS|CMMsg.MSG_ENTER,null); } } thisRoom=R; } if(thisRoom==null) { tickStatus=Tickable.STATUS_NOT; return true; } Room thatRoom=null; final List<String> steps=getSteps(); if(steps.size()==0) { tickStatus=Tickable.STATUS_NOT; return true; } tickStatus=Tickable.STATUS_MISC+2; if((step<0)||(step>=steps.size())) step=0; String nxt=steps.get(step); if((nxt.equalsIgnoreCase("RESTART")||nxt.equalsIgnoreCase("REPEAT"))&&(step>0)) { step=0; nxt=steps.get(step); } if(nxt.equalsIgnoreCase(".")) { step++; tickStatus=Tickable.STATUS_NOT; return true; } tickStatus=Tickable.STATUS_MISC+3; int direction=CMLib.directions().getGoodDirectionCode(nxt); if(direction<0) { if(CMLib.map().getExtendedRoomID(thisRoom).toUpperCase().endsWith(nxt.toUpperCase())) { correction=null; step++; tickStatus=Tickable.STATUS_NOT; return true; } tickStatus=Tickable.STATUS_MISC+4; for(int d=Directions.NUM_DIRECTIONS()-1;d>=0;d--) { final Room R=thisRoom.getRoomInDir(d); if((R!=null) &&(CMLib.map().getExtendedRoomID(R).toUpperCase().endsWith(nxt.toUpperCase()))) { correction=null; thatRoom=R; direction=d; break; } } } else { thatRoom=thisRoom.getRoomInDir(direction); if(thatRoom==null) { if(step>0) { thisRoom = CMLib.map().getRoom(thisRoom); if((ticking instanceof Physical)&&(((Physical)ticking).amDestroyed())) { CMLib.threads().deleteAllTicks(ticking); ((Physical)ticking).destroy(); Log.errOut("Patroller","'"+nxt+"' for "+ticking.name()+" at "+CMLib.map().getDescriptiveExtendedRoomID(thisRoom)+" is destroyed!"); } else if(thisRoom.amDestroyed() || (CMLib.map().getRoom(CMLib.map().getExtendedRoomID(thisRoom))==null)) { Log.errOut("Patroller","'"+nxt+"' for "+ticking.name()+" at "+CMLib.map().getDescriptiveExtendedRoomID(thisRoom)+" is lost room! Destroying!"); if(ticking instanceof Physical) ((Physical)ticking).destroy(); } else Log.errOut("Patroller","'"+nxt+"' for "+ticking.name()+" at "+CMLib.map().getDescriptiveExtendedRoomID(thisRoom)+" is impossible!"); } step=-1; tickStatus=Tickable.STATUS_NOT; final Room myStartRoom=CMLib.map().getRoom(startRoom.get()); if((startRoom!=null)&&(startRoom!=thisRoom)) { if(myStartRoom.amDestroyed() || (CMLib.map().getRoom(CMLib.map().getExtendedRoomID(myStartRoom))==null)) { Log.errOut("Patroller","'"+nxt+"' for "+ticking.name()+" FROM "+CMLib.map().getDescriptiveExtendedRoomID(thisRoom)+" is lost room! Destroying!"); if(ticking instanceof Physical) ((Physical)ticking).destroy(); } else { final Environmental E=(Environmental)ticking; if(ticking instanceof MOB) myStartRoom.bringMobHere((MOB)E,true); else if(E instanceof Item) myStartRoom.moveItemTo((Item)E,Expire.Never,Move.Followers); } } return true; } } Room destinationRoomForThisStep=thatRoom; tickStatus=Tickable.STATUS_MISC+5; if((direction<0)||(destinationRoomForThisStep==null)) { Room R=CMLib.map().getRoom(nxt); if(R==null) R=CMLib.map().getRoom(thisRoom.getArea()+nxt); if(R==null) R=CMLib.map().getRoom(thisRoom.getArea()+"#"+nxt); if(R!=null) { final boolean airOk=(((ticking instanceof Physical)&&CMLib.flags().isFlying((Physical)ticking)) ||((ticking instanceof Rider)&&(((Rider)ticking).riding()!=null)&&(((Rider)ticking).riding().rideBasis()==Rideable.RIDEABLE_AIR)) ||((ticking instanceof Rideable)&&(((Rideable)ticking).rideBasis()==Rideable.RIDEABLE_AIR))); final boolean waterOk=((ticking instanceof Physical)&&CMLib.flags().isWaterWorthy((Physical)ticking)); tickStatus=Tickable.STATUS_MISC+6; if(R instanceof GridLocale) { boolean GridLocaleFixed=false; if(correction!=null) { for(int r=0;r<correction.size();r++) { if(((GridLocale)R).isMyGridChild(correction.get(r))) { GridLocaleFixed=true; R=correction.get(r); break; } } } if(!GridLocaleFixed) { correction=null; R=((GridLocale)R).getRandomGridChild(); } } tickStatus=Tickable.STATUS_MISC+7; destinationRoomForThisStep=R; direction=-1; if(correction!=null) { direction=CMLib.tracking().trackNextDirectionFromHere(correction,thisRoom,ticking instanceof Item); if(direction<0) correction=null; else thatRoom=thisRoom.getRoomInDir(direction); } tickStatus=Tickable.STATUS_MISC+8; if((direction<0)||(thatRoom==null)) { final TrackingLibrary.TrackingFlags flags=CMLib.tracking().newFlags(); if(ticking instanceof Item) flags.plus(TrackingLibrary.TrackingFlag.OPENONLY); flags.plus(TrackingLibrary.TrackingFlag.NOEMPTYGRIDS); if(!airOk) flags.plus(TrackingLibrary.TrackingFlag.NOAIR); if(!waterOk) flags.plus(TrackingLibrary.TrackingFlag.NOWATER); correction=CMLib.tracking().findTrailToAnyRoom(thisRoom,new XVector<Room>(R),flags,diameter); tickStatus=Tickable.STATUS_MISC+9; if(correction!=null) direction=CMLib.tracking().trackNextDirectionFromHere(correction,thisRoom,ticking instanceof Item); else direction=-1; tickStatus=Tickable.STATUS_MISC+10; if(direction>=0) thatRoom=thisRoom.getRoomInDir(direction); else correction=null; } tickStatus=Tickable.STATUS_MISC+11; if((direction<0)||(thatRoom==null)) { step=0; tickStatus=Tickable.STATUS_NOT; return true; } tickStatus=Tickable.STATUS_MISC+12; } else { Log.errOut("Patroller","'"+nxt+"' for "+ticking.name()+" ("+CMLib.map().getDescriptiveExtendedRoomID(CMLib.map().getStartRoom((Environmental)ticking))+") is utterly unknown!"); tickStatus=Tickable.STATUS_NOT; return true; } } else correction=null; tickStatus=Tickable.STATUS_MISC+13; final Exit E=thisRoom.getExitInDir(direction); if(E==null) { tickStatus=Tickable.STATUS_NOT; return true; } tickStatus=Tickable.STATUS_MISC+14; final Set<MOB> mobsHere=CMLib.players().getPlayersHere(thisRoom); if(mobsHere.size()>0) { for(final MOB inhab : mobsHere) { if((!inhab.isMonster()) &&(CMSecurity.isAllowed(inhab,thisRoom,CMSecurity.SecFlag.CMDMOBS) ||CMSecurity.isAllowed(inhab,thisRoom,CMSecurity.SecFlag.CMDROOMS))) { tickStatus=Tickable.STATUS_NOT; return true; } } } tickStatus=Tickable.STATUS_MISC+15; if(ticking instanceof Item) { final Item I=(Item)ticking; if((ticking instanceof BoardableShip) &&(((BoardableShip)ticking).getShipItem() instanceof SailingShip) &&(thatRoom!=null)) { final SailingShip ship = (SailingShip)((BoardableShip)ticking).getShipItem(); if(ship.isAnchorDown()) ship.setAnchorDown(false); ship.setCurrentCourse(new XVector<Integer>(Integer.valueOf(direction))); ship.tick(ship, Tickable.TICKID_AREA); // this should force a move } else { if((ticking instanceof Rideable) &&(thatRoom!=null) &&(riders!=null)) { final Exit opExit=thatRoom.getReverseExit(direction); for(int i=0;i<riders.size();i++) { final Rider R=riders.get(i); if(R instanceof MOB) { tickStatus=Tickable.STATUS_MISC+16; final MOB mob=(MOB)R; mob.setRiding((Rideable)ticking); // overboard check if(mob.isMonster() && mob.isSavable() && (mob.location() != thisRoom) && CMLib.flags().isInTheGame(mob,true)) thisRoom.bringMobHere(mob,false); final CMMsg enterMsg=CMClass.getMsg(mob,thatRoom,E,CMMsg.MSG_ENTER,null,CMMsg.MSG_ENTER,null,CMMsg.MSG_ENTER,null); final CMMsg leaveMsg=CMClass.getMsg(mob,thisRoom,opExit,CMMsg.MSG_LEAVE,null,CMMsg.MSG_LEAVE,null,CMMsg.MSG_LEAVE,null); leaveMsg.setValue(direction+1); enterMsg.setValue(thatRoom.getReverseDir(direction)+1); try { rideCheckCt++; if(!E.okMessage(mob,enterMsg)) { tickStatus=Tickable.STATUS_NOT; return true; } else if((opExit!=null)&&(!opExit.okMessage(mob,leaveMsg))) { tickStatus=Tickable.STATUS_NOT; return true; } else if(!enterMsg.target().okMessage(mob,enterMsg)) { tickStatus=Tickable.STATUS_NOT; return true; } else if(!mob.okMessage(mob,enterMsg)) { tickStatus=Tickable.STATUS_NOT; return true; } } finally { rideCheckCt--; } } } } tickStatus=Tickable.STATUS_MISC+17; thisRoom.showHappens(CMMsg.MSG_OK_ACTION,I,L("<S-NAME> goes @x1.",CMLib.directions().getDirectionName(direction))); tickStatus=Tickable.STATUS_MISC+18; if(thatRoom!=null) thatRoom.moveItemTo(I); tickStatus=Tickable.STATUS_MISC+19; if((I.owner()==thatRoom)&&(thatRoom!=null)) { tickStatus=Tickable.STATUS_MISC+20; thatRoom.showHappens(CMMsg.MSG_OK_ACTION,I,L("<S-NAME> arrives from @x1.",CMLib.directions().getFromCompassDirectionName(Directions.getOpDirectionCode(direction)))); tickStatus=Tickable.STATUS_MISC+21; if(riders!=null) { for(int i=0;i<riders.size();i++) { final Rider R=riders.get(i); if(CMLib.map().roomLocation(R)!=thatRoom) { if((((Rideable)ticking).rideBasis()!=Rideable.RIDEABLE_SIT) &&(((Rideable)ticking).rideBasis()!=Rideable.RIDEABLE_TABLE) &&(((Rideable)ticking).rideBasis()!=Rideable.RIDEABLE_ENTERIN) &&(((Rideable)ticking).rideBasis()!=Rideable.RIDEABLE_SLEEP) &&(((Rideable)ticking).rideBasis()!=Rideable.RIDEABLE_LADDER)) { if((R instanceof MOB) &&(CMLib.flags().isInTheGame((MOB)R,true))) { tickStatus=Tickable.STATUS_MISC+30; thatRoom.bringMobHere((MOB)R,true); ((MOB)R).setRiding((Rideable)ticking); tickStatus=Tickable.STATUS_MISC+31; CMLib.commands().postLook((MOB)R,true); thatRoom.show((MOB)R,thatRoom,E,CMMsg.MASK_ALWAYS|CMMsg.MSG_ENTER,null); tickStatus=Tickable.STATUS_MISC+32; } else if(R instanceof Item) { tickStatus=Tickable.STATUS_MISC+33; thatRoom.moveItemTo((Item)R); tickStatus=Tickable.STATUS_MISC+34; } } else R.setRiding(null); } tickStatus=Tickable.STATUS_MISC+35; } } tickStatus=Tickable.STATUS_MISC+36; } } tickStatus=Tickable.STATUS_MISC+37; if(I.owner()==destinationRoomForThisStep) step++; else tickDown=0; } else if(ticking instanceof MOB) { tickStatus=Tickable.STATUS_MISC+22; // ridden things dont wander! final MOB mob=(MOB)ticking; // handle doors! if(E.hasADoor()&&(!E.isOpen())) { if((E.hasALock())&&(E.isLocked())) { CMMsg msg=CMClass.getMsg(mob,E,null,CMMsg.MSG_OK_VISUAL,CMMsg.MSG_OK_VISUAL,CMMsg.MSG_OK_VISUAL,null); if(mob.location().okMessage(mob,msg)) { msg=CMClass.getMsg(mob,E,null,CMMsg.MSG_OK_VISUAL,CMMsg.MSG_UNLOCK,CMMsg.MSG_OK_VISUAL,L("<S-NAME> unlock(s) <T-NAMESELF><O-WITHNAME>.")); CMLib.utensils().roomAffectFully(msg,thisRoom,direction); } } CMMsg msg=CMClass.getMsg(mob,E,null,CMMsg.MSG_OK_VISUAL,CMMsg.MSG_OK_VISUAL,CMMsg.MSG_OK_VISUAL,null); if(mob.location().okMessage(mob,msg)) { msg=CMClass.getMsg(mob,E,null,CMMsg.MSG_OK_VISUAL,CMMsg.MSG_OPEN,CMMsg.MSG_OK_VISUAL, L("<S-NAME> @x1 <T-NAMESELF>.",((E.openWord().indexOf('(')>0)?E.openWord():(E.openWord()+"(s)")))); CMLib.utensils().roomAffectFully(msg,thisRoom,direction); } } if(!E.isOpen()) { tickStatus=Tickable.STATUS_NOT; return true; } tickStatus=Tickable.STATUS_MISC+23; final Ability A=mob.fetchAbility("Thief_Sneak"); if(A!=null) { final Vector<String> V=new Vector<String>(); V.add(CMLib.directions().getDirectionName(direction)); if(A.proficiency()<50) { A.setProficiency(CMLib.dice().roll(1,50,A.adjustedLevel(mob,0)*15)); final Ability A2=mob.fetchAbility("Thief_Hide"); if(A2!=null) A2.setProficiency(CMLib.dice().roll(1,50,A.adjustedLevel(mob,0)*15)); } final CharState oldState=(CharState)mob.curState().copyOf(); A.invoke(mob,V,null,false,0); mob.curState().setMana(oldState.getMana()); mob.curState().setMovement(oldState.getMovement()); } else { try { rideCheckCt++; CMLib.tracking().walk(mob,direction,false,false); } finally { rideCheckCt--; } } tickStatus=Tickable.STATUS_MISC+24; if(mob.location()==destinationRoomForThisStep) step++; else tickDown=0; } } tickStatus=Tickable.STATUS_NOT; return true; } }