package com.planet_ink.coffee_mud.Areas; 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.core.exceptions.CMException; 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.Locales.interfaces.*; import com.planet_ink.coffee_mud.Libraries.interfaces.*; import com.planet_ink.coffee_mud.Libraries.interfaces.XMLLibrary.XMLTag; import com.planet_ink.coffee_mud.Libraries.interfaces.XMLLibrary.XMLTag; import com.planet_ink.coffee_mud.MOBS.GenShopkeeper; 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 2011-2016 Bo Zimmerman Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ public class StdAutoGenInstance extends StdArea implements AutoGenArea { @Override public String ID() { return "StdAutoGenInstance"; } private long flags = Area.FLAG_INSTANCE_PARENT; @Override public long flags() { return flags; } protected CList<AreaInstanceChild> instanceChildren= new SVector<AreaInstanceChild>(); protected volatile int instanceCounter = 0; protected long childCheckDown = CMProps.getMillisPerMudHour() / CMProps.getTickMillis(); protected WeakReference<Area> parentArea = null; protected String filePath = "randareas/example.xml"; protected Map<String, String> varMap = new Hashtable<String, String>(1); protected String getStrippedRoomID(String roomID) { final int x=roomID.indexOf('#'); if(x<0) return null; return roomID.substring(x); } protected String convertToMyArea(String roomID) { final String strippedID=getStrippedRoomID(roomID); if(strippedID==null) return null; return Name()+strippedID; } protected Area getParentArea() { if((parentArea!=null)&&(parentArea.get()!=null)) return parentArea.get(); final int x=Name().indexOf('_'); if(x<0) return null; if(!CMath.isNumber(Name().substring(0,x))) return null; final Area parentA = CMLib.map().getArea(Name().substring(x+1)); if((parentA==null) ||(!CMath.bset(parentA.flags(),Area.FLAG_INSTANCE_PARENT)) ||(CMath.bset(parentA.flags(),Area.FLAG_INSTANCE_CHILD))) return null; parentArea=new WeakReference<Area>(parentA); return parentA; } @Override public int getPercentRoomsCached() { return (getParentArea()==null)?0:100; } @Override public int[] getAreaIStats() { if(!CMProps.getBoolVar(CMProps.Bool.MUDSTARTED)) return emptyStats; final Area parentArea=getParentArea(); final String areaName = (parentArea==null)?Name():parentArea.Name(); int[] statData=(int[])Resources.getResource("STATS_"+areaName.toUpperCase()); if(statData!=null) return statData; List<Area> workList = new LinkedList<Area>(); synchronized(("STATS_"+areaName).intern()) { if(parentArea==null) { for(final Enumeration<AreaInstanceChild> childE=instanceChildren.elements();childE.hasMoreElements();) workList.add(childE.nextElement().A); } else { statData=buildAreaIStats(); } } if(parentArea == null) { int ct=0; statData=new int[Area.Stats.values().length]; for(Area childA : workList) { final int[] theseStats=childA.getAreaIStats(); if(theseStats != emptyStats) { ct++; for(int i=0;i<theseStats.length;i++) statData[i]+=theseStats[i]; } } if(ct==0) return emptyStats; for(int i=0;i<statData.length;i++) statData[i]=statData[i]/ct; Resources.removeResource("HELP_"+areaName.toUpperCase()); Resources.submitResource("STATS_"+areaName.toUpperCase(),statData); } return statData; } @Override public boolean tick(Tickable ticking, int tickID) { if(!super.tick(ticking, tickID)) return false; if(CMath.bset(flags(),Area.FLAG_INSTANCE_CHILD)) return true; if((--childCheckDown)<=0) { childCheckDown=CMProps.getMillisPerMudHour()/CMProps.getTickMillis(); LinkedList<AreaInstanceChild> workList = new LinkedList<AreaInstanceChild>(); final List<AreaInstanceChild> children = this.instanceChildren; if(children == null) return true; synchronized(children) { for(int i=children.size()-1;i>=0;i--) { final AreaInstanceChild child = children.get(i); final Area childA=child.A; if(childA.getAreaState() != Area.State.ACTIVE) { workList.add(child); } } } for(AreaInstanceChild child : workList) { final Area childA=child.A; final List<WeakReference<MOB>> V=child.mobs; boolean anyInside=false; for(final WeakReference<MOB> wmob : V) { final MOB M=wmob.get(); if((M!=null) &&CMLib.flags().isInTheGame(M,true) &&(M.location()!=null) &&(M.location().getArea()==childA)) { anyInside=true; break; } } if(!anyInside) { synchronized(children) { children.remove(child); } for(final WeakReference<MOB> wmob : V) { final MOB M=wmob.get(); if((M!=null) &&(M.location()!=null) &&(M.location().getArea()==this)) M.setLocation(M.getStartRoom()); } final MOB mob=CMClass.sampleMOB(); for(final Enumeration<Room> e=childA.getProperMap();e.hasMoreElements();) { final Room R=e.nextElement(); R.executeMsg(mob,CMClass.getMsg(mob,R,null,CMMsg.MSG_EXPIRE,null)); } CMLib.map().delArea(childA); childA.destroy(); } } } return true; } @Override public void executeMsg(final Environmental myHost, final CMMsg msg) { super.executeMsg(myHost, msg); if(CMath.bset(flags(),Area.FLAG_INSTANCE_CHILD)) { if((msg.sourceMinor()==CMMsg.TYP_SPEAK) &&(msg.sourceMessage()!=null) &&((msg.sourceMajor()&CMMsg.MASK_MAGIC)==0)) { final String said=CMStrings.getSayFromMessage(msg.sourceMessage()); if("RESET INSTANCE".equalsIgnoreCase(said)) { Room returnToRoom=null; final Room thisRoom=msg.source().location(); if(thisRoom.getArea()==this) { for(int d=0;d<Directions.NUM_DIRECTIONS();d++) { final Room R=thisRoom.getRoomInDir(d); if((R!=null)&&(R.getArea()!=null)&&(R.getArea()!=this)) returnToRoom=R; } } if(returnToRoom==null) { msg.addTrailerMsg(CMClass.getMsg(msg.source(),null,null,CMMsg.MSG_OK_ACTION,CMMsg.NO_EFFECT,CMMsg.NO_EFFECT, L("You must be at an entrance to reset the area."))); return; } final Area A=this.getParentArea(); if(A instanceof StdAutoGenInstance) { final StdAutoGenInstance parentA=(StdAutoGenInstance)A; synchronized(parentA.instanceChildren) { for(int i=parentA.instanceChildren.size()-1;i>=0;i--) { final List<WeakReference<MOB>> V=parentA.instanceChildren.get(i).mobs; if(parentA.instanceChildren.get(i).A==this) { for(final WeakReference<MOB> wM : V) { final MOB M=wM.get(); if((M!=null) &&CMLib.flags().isInTheGame(M,true) &&(M.location()!=null) &&(M.location()!=returnToRoom) &&(M.location().getArea()==this)) { returnToRoom.bringMobHere(M, true); CMLib.commands().postLook(M, true); } } parentA.instanceChildren.remove(i); final MOB mob=CMClass.sampleMOB(); for(final Enumeration<Room> e=getProperMap();e.hasMoreElements();) { final Room R=e.nextElement(); R.executeMsg(mob,CMClass.getMsg(mob,R,null,CMMsg.MSG_EXPIRE,null)); } msg.addTrailerMsg(CMClass.getMsg(msg.source(),CMMsg.MSG_OK_ACTION,L("The instance has been reset."))); CMLib.map().delArea(this); destroy(); return; } } } } msg.addTrailerMsg(CMClass.getMsg(msg.source(),CMMsg.MSG_OK_ACTION,L("The instance failed to reset."))); } } else if((msg.sourceMinor()==CMMsg.TYP_QUIT)&&(CMLib.map().isHere(msg.source(), this))) { final MOB mob = msg.source(); CMLib.tracking().forceRecall(mob); } } } @Override public boolean okMessage(final Environmental myHost, final CMMsg msg) { if(!super.okMessage(myHost, msg)) return false; if(CMath.bset(flags(),Area.FLAG_INSTANCE_CHILD)) return true; setAreaState(Area.State.PASSIVE); if((msg.sourceMinor()==CMMsg.TYP_ENTER) &&(msg.target() instanceof Room) &&(CMath.bset(flags(),Area.FLAG_INSTANCE_PARENT)) &&(isRoom((Room)msg.target())) &&(((msg.source().getStartRoom()==null)||(msg.source().getStartRoom().getArea()!=this)))) { if(msg.source().isMonster()) { final Set<MOB> friends=msg.source().getGroupMembers(new HashSet<MOB>()); boolean playerInvolved=false; for(final MOB M : friends) playerInvolved = playerInvolved || (!M.isMonster()); if(!playerInvolved) { msg.source().tell(L("You'll need to be accompanied by an adult to enter there.")); return false; } } synchronized(instanceChildren) { int myDex=-1; for(int i=0;i<instanceChildren.size();i++) { final List<WeakReference<MOB>> V=instanceChildren.get(i).mobs; for (final WeakReference<MOB> weakReference : V) { if(msg.source() == weakReference.get()) { myDex = i; break; } } } final Set<MOB> grp = msg.source().getGroupMembers(new HashSet<MOB>()); for(int i=0;i<instanceChildren.size();i++) { if(i!=myDex) { final List<WeakReference<MOB>> V=instanceChildren.get(i).mobs; for(int v=V.size()-1;v>=0;v--) { final WeakReference<MOB> wmob=V.get(v); if(wmob==null) continue; final MOB M=wmob.get(); if(grp.contains(M)) { if(myDex<0) { myDex=i; break; } else if((CMLib.flags().isInTheGame(M,true)) &&(M.location().getArea()!=instanceChildren.get(i).A)) { V.remove(M); instanceChildren.get(myDex).mobs.add(new WeakReference<MOB>(M)); } } } } } Area redirectA = null; int direction = CMLib.map().getRoomDir(msg.source().location(), (Room)msg.target()); if((direction<0)&&(msg.tool() instanceof Exit)) direction = CMLib.map().getExitDir(msg.source().location(), (Exit)msg.target()); if(direction < 0) { msg.source().tell(L("Can't figure out where you're coming from?!")); return false; } if(myDex<0) { final StdAutoGenInstance newA=(StdAutoGenInstance)this.copyOf(); newA.properRooms=new STreeMap<String, Room>(new Area.RoomIDComparator()); newA.properRoomIDSet = null; newA.metroRoomIDSet = null; newA.blurbFlags=new STreeMap<String,String>(); newA.setName((++instanceCounter)+"_"+Name()); newA.flags |= Area.FLAG_INSTANCE_CHILD; final Set<MOB> myGroup=msg.source().getGroupMembers(new HashSet<MOB>()); final StringBuffer xml = Resources.getFileResource(getGeneratorXmlPath(), true); if((xml==null)||(xml.length()==0)) { msg.source().tell(L("Unable to load this area. Please try again later.")); return false; } final List<XMLLibrary.XMLTag> xmlRoot = CMLib.xml().parseAllXML(xml); final Hashtable<String,Object> definedIDs = new Hashtable<String,Object>(); CMLib.percolator().buildDefinedIDSet(xmlRoot,definedIDs); String idName = ""; final List<String> idChoices = new Vector<String>(); for(final String key : getAutoGenVariables().keySet()) if(key.equalsIgnoreCase("AREA_ID")||key.equalsIgnoreCase("AREA_IDS")||key.equalsIgnoreCase("AREAID")||key.equalsIgnoreCase("AREAIDS")) idChoices.addAll(CMParms.parseCommas(getAutoGenVariables().get(key),true)); if(idChoices.size()==0) { for(final Object key : definedIDs.keySet()) { final Object val=definedIDs.get(key); if((key instanceof String) &&(val instanceof XMLTag) &&(((XMLTag)val).tag().equalsIgnoreCase("area"))) { final XMLTag piece=(XMLTag)val; final String inserter = piece.getParmValue("INSERT"); if(inserter!=null) { final List<String> V=CMParms.parseCommas(inserter,true); for(int v=0;v<V.size();v++) { String s = V.get(v); if(s.startsWith("$")) s=s.substring(1).trim(); final XMLTag insertPiece =(XMLTag)definedIDs.get(s.toUpperCase().trim()); if(insertPiece == null) continue; if(insertPiece.tag().equalsIgnoreCase("area")) if(!idChoices.contains(s.toUpperCase().trim())) idChoices.add(s.toUpperCase().trim()); } } else idChoices.add((String)key); } } } if(idChoices.size()>0) idName=idChoices.get(CMLib.dice().roll(1, idChoices.size(), -1)).toUpperCase().trim(); if((!(definedIDs.get(idName) instanceof XMLTag)) ||(!((XMLTag)definedIDs.get(idName)).tag().equalsIgnoreCase("area"))) { msg.source().tell(L("The area id '@x1' has not been defined in the data file.",idName)); return false; } final ScriptingEngine scrptEng=(ScriptingEngine)CMClass.getCommon("DefaultScriptingEngine"); final Object[] scriptObjs = new Object[ScriptingEngine.SPECIAL_NUM_OBJECTS]; final List<Double> levels=new ArrayList<Double>(); final Set<MOB> followers=msg.source().getGroupMembers(new HashSet<MOB>()); if(!followers.contains(msg.source())) followers.add(msg.source()); double totalLevels=0.0; for(final MOB M : followers) { final Double D=Double.valueOf(M.basePhyStats().level()); levels.add(D); totalLevels+=D.doubleValue(); } final Double[] sortedLevels=levels.toArray(new Double[0]); final double lowestLevel=sortedLevels[0].doubleValue(); final double medianLevel=sortedLevels[(int)Math.round(Math.floor(sortedLevels.length/2))].doubleValue(); final double averageLevel=Math.round(10.0*totalLevels/(sortedLevels.length))/10.0; final double highestLevel=sortedLevels[sortedLevels.length-1].doubleValue(); final double groupSize=Double.valueOf(followers.size()).doubleValue(); final double values[]={msg.source().basePhyStats().level(),lowestLevel,medianLevel,averageLevel,highestLevel,totalLevels,groupSize}; for(final String key : getAutoGenVariables().keySet()) if(!(key.equalsIgnoreCase("AREA_ID")||key.equalsIgnoreCase("AREA_IDS")||key.equalsIgnoreCase("AREAID")||key.equalsIgnoreCase("AREAIDS"))) { final String rawValue = CMath.replaceVariables(getAutoGenVariables().get(key),values); final String val=scrptEng.varify(msg.source(), newA, msg.source(), msg.source(), null, null, msg.sourceMessage(), scriptObjs, rawValue); definedIDs.put(key.toUpperCase(),val); } definedIDs.put("AREANAME", Name()); if(!definedIDs.containsKey("AREASIZE")) definedIDs.put("AREASIZE", "50"); if(!definedIDs.containsKey("LEVEL_RANGE")) definedIDs.put("LEVEL_RANGE", (msg.source().basePhyStats().level()-4)+"?"+(msg.source().basePhyStats().level())); if(!definedIDs.containsKey("AGGROCHANCE")) definedIDs.put("AGGROCHANCE", ""+msg.source().basePhyStats().level()); try { XMLTag piece=(XMLTag)definedIDs.get(idName); final List<XMLTag> pieces = CMLib.percolator().getAllChoices(piece.tag(), piece, definedIDs); if(pieces.size()>0) piece=pieces.get(CMLib.dice().roll(1, pieces.size(), -1)); //TODO: fix this //FIXME: this can maybe pick town. if(!definedIDs.containsKey("THEME")) { final Map<String,String> unfilled = CMLib.percolator().getUnfilledRequirements(definedIDs,piece); final List<String> themes = CMParms.parseCommas(unfilled.get("THEME"), true); if(themes.size()>0) definedIDs.put("THEME", themes.get(CMLib.dice().roll(1, themes.size(), -1)).toUpperCase().trim()); } try { CMLib.percolator().checkRequirements(piece, definedIDs); } catch(final CMException cme) { msg.source().tell(L("Required ids for @x1 were missing: @x2",idName,cme.getMessage())); return false; } for(final MOB M : myGroup) M.tell(L("^x------------------------------------------------------\n\rPreparing to enter @x1, please stand by...\n\r------------------------------------------------------^N^.",Name())); definedIDs.put("ROOMTAG_NODEGATEEXIT", Directions.getDirectionName(Directions.getOpDirectionCode(direction))); definedIDs.put("ROOMTAG_GATEEXITROOM", msg.source().location()); if(!CMLib.percolator().fillInArea(piece, definedIDs, newA, direction)) { msg.source().tell(L("Failed to enter the new area. Try again later.")); return false; } CMLib.percolator().postProcess(definedIDs); } catch(final CMException cme) { Log.errOut("StdAutoGenInstance",cme); msg.source().tell(L("Failed to finish entering the new area. Try again later.")); return false; } redirectA=newA; CMLib.map().addArea(newA); newA.setAreaState(Area.State.ACTIVE); // starts ticking final List<WeakReference<MOB>> newMobList = new SVector<WeakReference<MOB>>(5); newMobList.add(new WeakReference<MOB>(msg.source())); final AreaInstanceChild child = new AreaInstanceChild(redirectA,newMobList); instanceChildren.add(child); getAreaIStats(); // if this is the first child ever, this will force stat making final Room R=redirectA.getRoom(redirectA.Name()+"#0"); if(R!=null) { Exit E=R.getExitInDir(Directions.getOpDirectionCode(direction)); if(E==null) E = CMClass.getExit("Open"); final int opDir=Directions.getOpDirectionCode(direction); if(R.getRoomInDir(opDir)!=null) msg.source().tell(L("An error has caused the following exit to be one-way.")); else { R.setRawExit(opDir, E); R.rawDoors()[opDir]=msg.source().location(); } } } else redirectA=instanceChildren.get(myDex).A; if(redirectA instanceof StdAutoGenInstance) { final Room R=redirectA.getRoom(redirectA.Name()+"#0"); if(R!=null) { msg.setTarget(R); } } } } return true; } private final static String[] MYCODES={"GENERATIONFILEPATH","OTHERVARS"}; @Override public String getStat(String code) { if(CMParms.indexOfIgnoreCase(STDAREACODES, code)>=0) return super.getStat(code); else switch(getCodeNum(code)) { case 0: return this.getGeneratorXmlPath(); case 1: return CMParms.toEqListString(this.getAutoGenVariables()); default: break; } return ""; } @Override public void setStat(String code, String val) { if(CMParms.indexOfIgnoreCase(STDAREACODES, code)>=0) super.setStat(code, val); else switch(getCodeNum(code)) { case 0: setGeneratorXmlPath(val); break; case 1: setAutoGenVariables(val); break; default: break; } } @Override protected int getCodeNum(String code) { for(int i=0;i<MYCODES.length;i++) { if(code.equalsIgnoreCase(MYCODES[i])) return i; } return -1; } private static String[] codes=null; @Override public String[] getStatCodes() { if(codes!=null) return codes; final String[] MYCODES=CMProps.getStatCodesList(StdAutoGenInstance.MYCODES,this); final String[] superCodes=STDAREACODES; codes=new String[superCodes.length+MYCODES.length]; int i=0; for(;i<superCodes.length;i++) codes[i]=superCodes[i]; for(int x=0;x<MYCODES.length;i++,x++) codes[i]=MYCODES[x]; return codes; } @Override public boolean sameAs(Environmental E) { if(!(E instanceof StdAutoGenInstance)) 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; } @Override public String getGeneratorXmlPath() { return filePath; } @Override public Map<String, String> getAutoGenVariables() { return varMap; } @Override public void setGeneratorXmlPath(String path) { filePath = path; } @Override public void setAutoGenVariables(Map<String, String> vars) { varMap = vars; } @Override public void setAutoGenVariables(String vars) { setAutoGenVariables(CMParms.parseEQParms(vars)); } }