package com.planet_ink.coffee_mud.Abilities; 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.core.exceptions.CoffeeMudException; 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.lang.ref.WeakReference; import java.util.*; import java.util.concurrent.atomic.AtomicInteger; /* Copyright 2016-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 PlanarAbility extends StdAbility { @Override public String ID() { return "PlanarAbility"; } private final static String localizedName = CMLib.lang().L("Planar Shifting Ability"); @Override public String name() { return localizedName; } @Override protected int canTargetCode() { return 0; } @Override public long flags() { return 0; } @Override protected int overrideMana() { return Ability.COST_ALL - 90; } @Override public int abstractQuality() { return Ability.QUALITY_INDIFFERENT; } protected volatile long lastCasting = 0; protected WeakReference<Room> oldRoom = null; protected Area planeArea = null; protected Map<String, String> planeVars = null; protected WeakArrayList<Room> roomsDone = new WeakArrayList<Room>(); protected int planarLevel = 1; protected String planarPrefix = null; protected List<Pair<String, String>>behavList = null; protected List<Pair<String, String>>reffectList = null; protected List<Pair<String, String>>factionList = null; protected int bonusDmgStat = -1; protected Set<String> reqWeapons = null; protected int recoverRate = 0; protected int fatigueRate = 0; protected volatile int recoverTick = 0; protected Set<PlanarSpecFlag> specFlags = null; protected int hardBumpLevel = 0; protected final Map<String,long[]> recentVisits = new TreeMap<String,long[]>(); protected final static long hardBumpTimeout = (60L * 60L * 1000L); protected Pair<Pair<Integer,Integer>,List<Pair<String,String>>> enableList=null; public static enum PlanarVar { ID, TRANSITIONAL, ALIGNMENT, PREFIX, LEVELADJ, MOBRESIST, SETSTAT, BEHAVAFFID, ADJSTAT, ADJSIZE, ADJUST, MOBCOPY, BEHAVE, ENABLE, WEAPONMAXRANGE, BONUSDAMAGESTAT, REQWEAPONS, ATMOSPHERE, AREABLURBS, ABSORB, HOURS, RECOVERRATE, FATIGUERATE, REFFECT, AEFFECT, SPECFLAGS, MIXRACE, ELITE, ROOMCOLOR, ROOMADJS, AREAEMOTER, FACTIONS } public static enum PlanarSpecFlag { NOINFRAVISION, BADMUNDANEARMOR, ALLBREATHE } protected static final AtomicInteger planeIDNum = new AtomicInteger(0); public void clearVars() { planeArea = null; planarLevel=1; roomsDone=new WeakArrayList<Room>(); planeVars=null; planarPrefix=null; this.behavList=null; this.enableList=null; this.reffectList=null; this.factionList=null; bonusDmgStat=-1; this.reqWeapons=null; this.recoverTick=-1; recoverRate = 0; fatigueRate = 0; } @Override public void setMiscText(final String newText) { super.setMiscText(newText); clearVars(); if(newText.length()>0) { this.planeVars=getPlane(newText); if(this.planeVars==null) throw new IllegalArgumentException("Unknown: "+newText); this.roomsDone=new WeakArrayList<Room>(); this.planarPrefix=planeVars.get(PlanarVar.PREFIX.toString()); this.recoverRate = CMath.s_int(planeVars.get(PlanarVar.RECOVERRATE.toString())); this.fatigueRate = CMath.s_int(planeVars.get(PlanarVar.FATIGUERATE.toString())); this.recoverTick=1; if((planarPrefix!=null)&&(planarPrefix.indexOf(',')>0)) { final List<String> choices=CMParms.parseCommas(planarPrefix, true); planarPrefix=choices.get(CMLib.dice().roll(1, choices.size(), -1)); } if(affected instanceof Area) { planeArea=(Area)affected; int medianLevel=planeArea.getPlayerLevel(); if(medianLevel == 0) medianLevel=planeArea.getAreaIStats()[Area.Stats.MED_LEVEL.ordinal()]; planarLevel=medianLevel; } final String specflags = planeVars.get(PlanarVar.SPECFLAGS.toString()); if(specflags != null) { for(final String s : CMParms.parse(specflags)) { final PlanarSpecFlag flag=(PlanarSpecFlag)CMath.s_valueOf(PlanarSpecFlag.class, s); if(flag == null) Log.errOut("Spell_Planeshift","Unknown spec flag "+s); else { if(this.specFlags==null) this.specFlags=new HashSet<PlanarSpecFlag>(); this.specFlags.add(flag); } } } final String areablurbs = planeVars.get(PlanarVar.AREABLURBS.toString()); if((areablurbs!=null)&&(areablurbs.length()>0)) { final Map<String,String> blurbSets=CMParms.parseEQParms(areablurbs); for(final String key : blurbSets.keySet()) planeArea.addBlurbFlag(key.toUpperCase().trim().replace(' ', '_')+" "+blurbSets.get(key)); } final String behaves = planeVars.get(PlanarVar.BEHAVE.toString()); if(behaves!=null) this.behavList=CMParms.parseSpaceParenList(behaves); final String reffects = planeVars.get(PlanarVar.REFFECT.toString()); if(reffects!=null) this.reffectList=CMParms.parseSpaceParenList(reffects); final String factions = planeVars.get(PlanarVar.FACTIONS.toString()); if(factions!=null) this.factionList=CMParms.parseSpaceParenList(factions); final String enables = planeVars.get(PlanarVar.ENABLE.toString()); if(enables!=null) { final List<Pair<String,String>> enableAs=CMParms.parseSpaceParenList(enables); Integer perLevel=Integer.valueOf(CMProps.getIntVar(CMProps.Int.LASTPLAYERLEVEL)); Integer numSkills=Integer.valueOf(Integer.MAX_VALUE); for(final Iterator<Pair<String,String>> p=enableAs.iterator();p.hasNext();) { final Pair<String,String> P=p.next(); if(P.first.toLowerCase().equals("number")) { p.remove(); final String parms=P.second; final int x=parms.indexOf('/'); if(x<0) numSkills=Integer.valueOf(CMath.s_int(parms.trim())); else { numSkills=Integer.valueOf(CMath.s_int(parms.substring(0,x).trim())); perLevel=Integer.valueOf(CMath.s_int(parms.substring(x+1).trim())); } } } if(enableAs.size()>0) { final PairList<String,String> addThese = new PairVector<String,String>(); for(final Iterator<Pair<String,String>> p = enableAs.iterator();p.hasNext();) { final Pair<String,String> P=p.next(); Ability A=CMClass.getAbility(P.first); if(A==null) { p.remove(); boolean foundSomething=false; long flag=CMParms.indexOf(Ability.FLAG_DESCS, P.first); if(flag >=0 ) flag=CMath.pow(2, flag); int domain=CMParms.indexOf(Ability.DOMAIN_DESCS, P.first); if(domain > 0) domain = domain << 5; final int acode=CMParms.indexOf(Ability.ACODE_DESCS, P.first); for(final Enumeration<Ability> a=CMClass.abilities();a.hasMoreElements();) { A=a.nextElement(); if((A.Name().toUpperCase().equals(P.first)) ||((flag>0)&&(CMath.bset(A.flags(),flag))) ||((domain>0)&&(A.classificationCode()&Ability.ALL_DOMAINS)==domain) ||((acode>=0)&&(A.classificationCode()&Ability.ALL_ACODES)==acode) ) { if(!addThese.containsFirst(A.ID().toUpperCase())) { addThese.add(A.ID().toUpperCase(), P.second); foundSomething=true; } } } if(!foundSomething) Log.errOut("Spell_Planeshift","Unknown skill type/domain/flag: "+P.first); } } enableAs.addAll(addThese); if(enableAs.size()>0) this.enableList=new Pair<Pair<Integer,Integer>,List<Pair<String,String>>>(new Pair<Integer,Integer>(numSkills,perLevel),enableAs); } } final String bonusDamageStat = planeVars.get(PlanarVar.BONUSDAMAGESTAT.toString()); if(bonusDamageStat!=null) { this.bonusDmgStat=CMParms.indexOf(CharStats.CODES.BASENAMES(), bonusDamageStat.toUpperCase().trim()); } final String reqWeapons = planeVars.get(PlanarVar.REQWEAPONS.toString()); if(reqWeapons != null) this.reqWeapons = new HashSet<String>(CMParms.parse(reqWeapons.toUpperCase().trim())); final String atmosphere = planeVars.get(PlanarVar.ATMOSPHERE.toString()); if(atmosphere!=null) { if(atmosphere.length()==0) this.planeArea.setAtmosphere(Integer.MIN_VALUE); else { final int atmo=RawMaterial.CODES.FIND_IgnoreCase(atmosphere); this.planeArea.setAtmosphere(atmo); } } final String absorb = planeVars.get(PlanarVar.ABSORB.toString()); if(absorb != null) reEffect(planeArea,"Prop_AbsorbDamage",absorb); final TimeClock C=(TimeClock)CMLib.time().globalClock().copyOf(); C.setDayOfMonth(1); C.setYear(1); C.setMonth(1); C.setHourOfDay(0); final String hours = planeVars.get(PlanarVar.HOURS.toString()); if((hours != null)&&(CMath.isInteger(hours))) { final double mul=CMath.div(CMath.s_int(hours),CMLib.time().globalClock().getHoursInDay()); if(mul != 1.0) { final int newHours = (int)Math.round(CMath.mul(C.getHoursInDay(),mul)); C.setHoursInDay(newHours); C.setDawnToDusk((int)Math.round(CMath.mul(C.getDawnToDusk()[0],mul)) , (int)Math.round(CMath.mul(C.getDawnToDusk()[1],mul)) , (int)Math.round(CMath.mul(C.getDawnToDusk()[2],mul)) , (int)Math.round(CMath.mul(C.getDawnToDusk()[3],mul))); } } planeArea.setTimeObj(C); planeArea.addNonUninvokableEffect(CMClass.getAbility("Prop_NoTeleportOut")); planeArea.addNonUninvokableEffect(CMClass.getAbility("Prop_NoTeleport")); planeArea.addNonUninvokableEffect(CMClass.getAbility("Prop_NoRecall")); final String aeffects = planeVars.get(PlanarVar.AEFFECT.toString()); if(aeffects!=null) { final List<Pair<String,String>> affectList=CMParms.parseSpaceParenList(aeffects); if(affectList!=null) { for(final Pair<String,String> p : affectList) { if(planeArea.fetchBehavior(p.first)==null) { final Behavior B=CMClass.getBehavior(p.first); if(B==null) { final Ability A=CMClass.getAbility(p.first); if(A==null) Log.errOut("Spell_Planeshift","Unknown behavior : "+p.first); else { A.setMiscText(p.second); planeArea.addNonUninvokableEffect(A); } } else { B.setParms(p.second); planeArea.addBehavior(B); } } } } } } } protected void reEffect(final Physical M, final String ID, final String parms) { if(M!=null) { Ability A=M.fetchEffect(ID); if(A!=null) M.delEffect(A); else A=CMClass.getAbility(ID); if(A!=null) { M.addNonUninvokableEffect(A); A.setMiscText((parms+" "+A.text()).trim()); } } } public synchronized void fixRoom(final Room room) { try { room.toggleMobility(false); CMLib.threads().suspendResumeRecurse(room, false, true); for(int i=0;i<Directions.NUM_DIRECTIONS();i++) { final Room R=room.rawDoors()[i]; if((R!=null)&&(R.getArea()!=planeArea)) room.rawDoors()[i]=null; } if(planeVars.containsKey(PlanarVar.ATMOSPHERE.toString())) room.setAtmosphere(planeArea.getAtmosphere()); int eliteLevel=0; if(planeVars.containsKey(PlanarVar.ELITE.toString())) eliteLevel=CMath.s_int(planeVars.get(PlanarVar.ELITE.toString())); if(planeVars.containsKey(PlanarVar.ROOMCOLOR.toString())) { String prefix=""; String displayText = room.displayText(); if(displayText.toUpperCase().startsWith("<VARIES>")) { prefix="<VARIES>"; displayText=displayText.substring(prefix.length()); } String color=planeVars.get(PlanarVar.ROOMCOLOR.toString()); if(color.startsWith("UP ")) { color=color.substring(3).trim(); displayText=displayText.toUpperCase(); } room.setDisplayText(prefix+color+displayText+"^N"); } if(planeVars.containsKey(PlanarVar.ROOMADJS.toString())) { String wordStr=planeVars.get(PlanarVar.ROOMADJS.toString()); String prefix=""; String desc = room.description(); if(wordStr.startsWith("UP ")) { wordStr=wordStr.substring(3).trim(); desc=desc.toUpperCase(); } int chance=30; final int x=wordStr.indexOf(' '); if((x>0)&&(CMath.isInteger(wordStr.substring(0, x)))) { chance=CMath.s_int(wordStr.substring(0, x)); wordStr=wordStr.substring(x+1).trim(); } final String[] words= wordStr.split(","); if(desc.toUpperCase().startsWith("<VARIES>")) { prefix="<VARIES>"; desc=desc.substring(prefix.length()); } room.setDescription(prefix+CMLib.english().insertAdjectives(desc, words, chance)); } if(this.reffectList!=null) { for(final Pair<String,String> p : this.reffectList) { if(room.fetchBehavior(p.first)==null) { final Behavior B=CMClass.getBehavior(p.first); if(B==null) { final Ability A=CMClass.getAbility(p.first); if(A==null) Log.errOut("Spell_Planeshift","Unknown behavior : "+p.first); else { A.setMiscText(p.second); room.addNonUninvokableEffect(A); } } else { B.setParms(p.second); room.addBehavior(B); } } } } if(CMLib.law().getLandTitle(room)!=null) { final List<Physical> destroyMe=new ArrayList<Physical>(); final Set<Rider> protSet = new HashSet<Rider>(); for(final Enumeration<MOB> m=room.inhabitants();m.hasMoreElements();) { final MOB M=m.nextElement(); if((M!=null)&&(M.isPlayer())) { protSet.add(M); M.getGroupMembersAndRideables(protSet); } } for(final Enumeration<MOB> m=room.inhabitants();m.hasMoreElements();) { final MOB M=m.nextElement(); if((M!=null) && (!M.isPlayer()) && (!protSet.contains(M))) destroyMe.add(M); } for(final Enumeration<Item> i=room.items();i.hasMoreElements();) { final Item I=i.nextElement(); if((I!=null)&&(!protSet.contains(I))) destroyMe.add(I); } for(final Physical P : destroyMe) P.destroy(); } final int allLevelAdj=CMath.s_int(planeVars.get(PlanarVar.LEVELADJ.toString())); final List<Item> delItems=new ArrayList<Item>(0); for(final Enumeration<Item> i=room.items();i.hasMoreElements();) { final Item I=i.nextElement(); if(I==null) continue; if((I instanceof Exit)&&((I instanceof BoardableShip))) { for(int x=0;x<100;x++) { final Room R2=((Exit)I).lastRoomUsedFrom(room); if((R2!=null)&&(R2.getArea()!=planeArea)) { delItems.add(I); break; } } } else if(I instanceof Exit) I.setReadableText(""); else if((invoker!=null)&&((I instanceof Weapon)||(I instanceof Armor))) { final int newILevelAdj = (planarLevel - I.phyStats().level()); int newILevel=invoker.phyStats().level() - newILevelAdj + allLevelAdj; if(newILevel <= 0) newILevel = 1; CMLib.itemBuilder().itemFix(I, newILevel, null); I.basePhyStats().setLevel(newILevel); I.phyStats().setLevel(newILevel); CMLib.itemBuilder().balanceItemByLevel(I); if((I instanceof Weapon) &&(this.reqWeapons!=null) &&(this.reqWeapons.contains("MAGICAL"))) { I.basePhyStats().setDisposition(I.basePhyStats().disposition()|PhyStats.IS_BONUS); I.phyStats().setDisposition(I.phyStats().disposition()|PhyStats.IS_BONUS); } I.text(); } } for(final Item I : delItems) I.destroy(); for(final Enumeration<MOB> m=room.inhabitants();m.hasMoreElements();) { final MOB M=m.nextElement(); if((M!=null) &&(invoker!=null) &&(M.isMonster()) &&(M.getStartRoom()!=null) &&(M.getStartRoom().getArea()==planeArea)) { if(planeVars.containsKey(PlanarVar.MIXRACE.toString())) { final String mixRace = planeVars.get(PlanarVar.MIXRACE.toString()); final Race firstR=CMClass.getRace(mixRace); if(firstR==null) Log.errOut("PlanarAbility","Unknown mixrace: "+mixRace); else { final Race secondR=M.charStats().getMyRace(); final Race R=CMLib.utensils().getMixedRace(firstR.ID(),secondR.ID(), false); if(R!=null) { M.baseCharStats().setMyRace(R); M.charStats().setMyRace(R); M.charStats().setWearableRestrictionsBitmap(M.charStats().getWearableRestrictionsBitmap()|M.charStats().getMyRace().forbiddenWornBits()); } } } if(planeVars.containsKey(PlanarVar.ATMOSPHERE.toString())) M.baseCharStats().setBreathables(new int[]{room.getAtmosphere()}); final int newLevelAdj = (planarLevel - M.phyStats().level()); int newLevel = invoker.phyStats().level() - newLevelAdj + allLevelAdj; if(newLevel <= 0) newLevel = 1; if((planarPrefix!=null)&&(planarPrefix.length()>0)) { final String oldName=M.Name(); int x; if(oldName.toLowerCase().indexOf(planarPrefix.toLowerCase())<0) { if(CMLib.english().startsWithAnArticle(M.Name())) { final String Name = M.Name().substring(M.Name().indexOf(' ')).trim(); M.setName(CMLib.english().startWithAorAn(planarPrefix+" "+Name)); } else { M.setName(CMStrings.capitalizeFirstLetter(planarPrefix)+" "+M.Name()); } if((x=M.displayText().toLowerCase().indexOf(oldName.toLowerCase()))>=0) { M.setDisplayText(M.displayText().substring(0,x)+M.Name()+M.displayText().substring(x+oldName.length())); } else if(CMLib.english().startsWithAnArticle(M.displayText())) { final String Name = M.displayText().substring(M.displayText().indexOf(' ')).trim(); M.setDisplayText(CMLib.english().startWithAorAn(planarPrefix+" "+Name)); } else if((x=M.displayText().toLowerCase().indexOf(M.charStats().getMyRace().name().toLowerCase()))>=0) { final int len=M.charStats().getMyRace().name().toLowerCase().length(); M.setDisplayText(M.displayText().substring(0,x)+planarPrefix+M.Name()+M.displayText().substring(x+len)); } } } M.basePhyStats().setLevel(newLevel); M.phyStats().setLevel(newLevel); CMLib.leveler().fillOutMOB(M,M.basePhyStats().level()+hardBumpLevel); M.basePhyStats().setLevel(newLevel); M.phyStats().setLevel(newLevel); final String align=planeVars.get(PlanarVar.ALIGNMENT.toString()); if(align!=null) { M.removeFaction(CMLib.factions().getAlignmentID()); M.addFaction(CMLib.factions().getAlignmentID(), CMath.s_int(align)); } if(this.factionList!=null) { for(final Pair<String,String> p : this.factionList) { Faction F=null; if(CMLib.factions().isFactionID(p.first)) F=CMLib.factions().getFaction(p.first); if(F==null) F=CMLib.factions().getFactionByName(p.first); if(F!=null) { if(CMath.isInteger(p.second)) { M.removeFaction(F.factionID()); M.addFaction(F.factionID(), CMath.s_int(p.second)); } else { final Faction.FRange FR = F.fetchRange(p.second); if(FR != null) { M.removeFaction(F.factionID()); M.addFaction(F.factionID(), FR.random()); } } } } } for(final Enumeration<Item> mi=M.items();mi.hasMoreElements();) { final Item mI=mi.nextElement(); if((mI!=null)&&(invoker!=null)) { final int newILevelAdj = (planarLevel - mI.phyStats().level()); int newILevel=invoker.phyStats().level() + newILevelAdj + allLevelAdj; if(newILevel <= 0) newILevel = 1; mI.basePhyStats().setLevel(newILevel); mI.phyStats().setLevel(newILevel); CMLib.itemBuilder().balanceItemByLevel(mI); if((mI instanceof Weapon) &&(this.reqWeapons!=null) &&(this.reqWeapons.contains("MAGICAL"))) { mI.basePhyStats().setDisposition(mI.basePhyStats().disposition()|PhyStats.IS_BONUS); mI.phyStats().setDisposition(mI.phyStats().disposition()|PhyStats.IS_BONUS); } mI.text(); } } final String resistWeak = planeVars.get(PlanarVar.MOBRESIST.toString()); if(resistWeak != null) reEffect(M,"Prop_Resistance",resistWeak); else if(this.hardBumpLevel>0) reEffect(M,"Prop_Resistance","magic holy disease poison evil weapons "+(5*hardBumpLevel)+"% "); final String setStat = planeVars.get(PlanarVar.SETSTAT.toString()); if(setStat != null) reEffect(M,"Prop_StatTrainer",setStat); final String behavaffid=planeVars.get(PlanarVar.BEHAVAFFID.toString()); if(behavaffid!=null) { String changeToID; for(final Enumeration<Behavior> b=M.behaviors();b.hasMoreElements();) { final Behavior B=b.nextElement(); if((B!=null)&&((changeToID=CMParms.getParmStr(behavaffid, B.ID(), "")).length()>0)) { boolean copyParms=false; if(changeToID.startsWith("*")) { copyParms=true; changeToID=changeToID.substring(1); } M.delBehavior(B); final Behavior B2=CMClass.getBehavior(changeToID); if(B2 != null) { if(copyParms) B2.setParms(B.getParms()); M.addBehavior(B2); } } } } final String adjStat = planeVars.get(PlanarVar.ADJSTAT.toString()); if(adjStat != null) reEffect(M,"Prop_StatAdjuster",adjStat); if(eliteLevel > 0) { switch(eliteLevel) { case 1: reEffect(M,"Prop_Adjuster", "multiplych=true hitpoints+300 multiplyph=true attack+150 damage+150 armor+115 ALLSAVES+15"); reEffect(M,"Prop_ShortEffects", ""); break; default: reEffect(M,"Prop_Adjuster", "multiplych=true hitpoints+600 multiplyph=true attack+150 damage+150 armor+115 ALLSAVES+15"); reEffect(M,"Prop_ShortEffects", ""); break; } reEffect(M,"Prop_ModExperience","*2"); final String adjSize = planeVars.get(PlanarVar.ADJSIZE.toString()); if(adjSize != null) { final double heightAdj = CMParms.getParmDouble(adjSize, "HEIGHT", Double.MIN_VALUE); if(heightAdj > Double.MIN_VALUE) reEffect(M,"Prop_Adjuster","height+"+(100+(heightAdj*100))); } } else { final String adjust = planeVars.get(PlanarVar.ADJUST.toString()); if(adjust != null) reEffect(M,"Prop_Adjuster",adjust); final String adjSize = planeVars.get(PlanarVar.ADJSIZE.toString()); if(adjSize != null) { final double heightAdj = CMParms.getParmDouble(adjSize, "HEIGHT", Double.MIN_VALUE); if(heightAdj > Double.MIN_VALUE) reEffect(M,"Prop_Adjuster","height+"+(int)Math.round(CMath.mul(M.basePhyStats().height(),heightAdj))); } } final String adjSize = planeVars.get(PlanarVar.ADJSIZE.toString()); if(adjSize != null) { final double weightAdj = CMParms.getParmDouble(adjSize, "WEIGHT", Double.MIN_VALUE); if(weightAdj > Double.MIN_VALUE) reEffect(M,"Prop_StatAdjuster","weightadj="+(int)Math.round(CMath.mul(M.baseWeight(),weightAdj))); } if(this.behavList!=null) { for(final Pair<String,String> p : this.behavList) { if(M.fetchBehavior(p.first)==null) { final Behavior B=CMClass.getBehavior(p.first); if(B==null) Log.errOut("Spell_Planeshift","Unknown behavior : "+p.first); else { B.setParms(p.second); M.addBehavior(B); } } } } if(this.enableList != null) { final Pair<Integer,Integer> lv=this.enableList.first; final PairList<String,String> unused = new PairVector<String,String>(this.enableList.second); for(int l=0;l<M.phyStats().level() && (unused.size()>0);l+=lv.second.intValue()) { for(int a=0;a<lv.first.intValue() && (unused.size()>0);a++) { final int aindex=CMLib.dice().roll(1, unused.size(), -1); final Pair<String,String> P=unused.remove(aindex); final Ability A=CMClass.getAbility(P.first); if(M.fetchAbility(A.ID())==null) { A.setMiscText(P.second); M.addAbility(A); } } } } M.text(); M.recoverCharStats(); M.recoverMaxState(); M.recoverPhyStats(); M.recoverCharStats(); M.recoverMaxState(); M.recoverPhyStats(); M.resetToMaxState(); } } final int mobCopy=CMath.s_int(planeVars.get(PlanarVar.MOBCOPY.toString())); if(mobCopy>0) { final List<MOB> list=new ArrayList<MOB>(room.numInhabitants()); for(final Enumeration<MOB> m=room.inhabitants();m.hasMoreElements();) { final MOB M=m.nextElement(); if((M!=null) &&(M.isMonster()) &&(M.getStartRoom()!=null) &&(M.getStartRoom().getArea()==planeArea)) { list.add(M); } } for(final MOB M : list) { for(int i=0;i<mobCopy;i++) { final MOB M2=(MOB)M.copyOf(); M2.text(); M2.setSavable(M.isSavable()); M2.bringToLife(room, true); M2.recoverCharStats(); M2.recoverMaxState(); M2.recoverPhyStats(); } } } } catch(final Exception e) { Log.errOut(e); } finally { room.recoverRoomStats(); CMLib.threads().suspendResumeRecurse(room, false, false); room.toggleMobility(true); room.recoverRoomStats(); } } @Override public void affectCharStats(final MOB affected, final CharStats affectableStats) { super.affectCharStats(affected, affectableStats); if(this.specFlags!=null) { if(this.specFlags.contains(PlanarSpecFlag.ALLBREATHE)) affectableStats.setBreathables(new int[]{}); } } @Override public void affectPhyStats(final Physical affected, final PhyStats affectableStats) { super.affectPhyStats(affected, affectableStats); if(affected instanceof MOB) { if(this.bonusDmgStat>=0) affectableStats.setDamage(affectableStats.damage() + (((((MOB)affected).charStats().getStat(this.bonusDmgStat))-10)/2)); if(this.specFlags!=null) { if(this.specFlags.contains(PlanarSpecFlag.NOINFRAVISION)) affectableStats.setSensesMask(CMath.unsetb(affectableStats.sensesMask(), PhyStats.CAN_SEE_INFRARED)); if(this.specFlags.contains(PlanarSpecFlag.BADMUNDANEARMOR)) { final MOB M=(MOB)affected; int neg=0; for(final Enumeration<Item> i=M.items();i.hasMoreElements();) { final Item I=i.nextElement(); if((I instanceof Armor) &&(!I.amWearingAt(Wearable.IN_INVENTORY)) &&((!I.amWearingAt(Wearable.WORN_FLOATING_NEARBY))||(I.fitsOn(Wearable.WORN_FLOATING_NEARBY))) &&((!I.amWearingAt(Wearable.WORN_HELD))||(this instanceof Shield)) &&(!CMLib.flags().isABonusItems(I)) &&(I.phyStats().ability()<=0)) { neg += I.phyStats().armor(); } } affectableStats.setArmor(affectableStats.armor()+neg); } } } } @Override public boolean tick(final Tickable ticking, final int tickID) { if(!super.tick(ticking, tickID)) return false; if((ticking instanceof Area)&&(tickID == Tickable.TICKID_AREA)) { if(((this.recoverRate>0)||(this.fatigueRate>0)) &&(--this.recoverTick <= 0) && (this.planeArea!=null)) { this.recoverTick = CMProps.getIntVar(CMProps.Int.RECOVERRATE) * CharState.REAL_TICK_ADJUST_FACTOR; for(final Enumeration<Room> r=planeArea.getFilledProperMap();r.hasMoreElements();) { final Room R=r.nextElement(); if((R!=null)&&(R.numPCInhabitants()>0)) { for(final Enumeration<MOB> m=R.inhabitants();m.hasMoreElements();) { final MOB M=m.nextElement(); for(int i=0;i<this.recoverRate;i++) CMLib.combat().recoverTick(M); if(this.fatigueRate>100) { M.curState().setHunger(M.maxState().maxHunger(M.baseWeight())); M.curState().setThirst(M.maxState().maxThirst(M.baseWeight())); M.curState().setFatigue(0); } else for(int i=0;i<(this.recoverTick * this.fatigueRate);i++) CMLib.combat().expendEnergy(M, false); } } } } } return true; } protected boolean roomDone(final Room R) { synchronized(roomsDone) { return (this.roomsDone.contains(R)); } } protected synchronized void doneRoom(final Room R) { synchronized(roomsDone) { this.roomsDone.add(R); } } @Override public void executeMsg(final Environmental myHost, final CMMsg msg) { if(msg.targetMinor()==CMMsg.TYP_NEWROOM) { if((msg.target() instanceof Room) &&(!roomDone((Room)msg.target())) &&(((Room)msg.target()).getArea()==planeArea)) { doneRoom((Room)msg.target()); fixRoom((Room)msg.target()); } } super.executeMsg(myHost, msg); } @Override public boolean okMessage(final Environmental myHost, final CMMsg msg) { if(!super.okMessage(myHost, msg)) return false; switch(msg.targetMinor()) { case CMMsg.TYP_WEAPONATTACK: if((msg.tool() instanceof AmmunitionWeapon) &&(((AmmunitionWeapon)msg.tool()).requiresAmmunition()) &&(msg.target() instanceof MOB) &&(planeVars!=null) &&(planeVars.containsKey(PlanarVar.WEAPONMAXRANGE.toString())) &&((msg.source().rangeToTarget()>0)||(((MOB)msg.target()).rangeToTarget()>0))) { final int maxRange=CMath.s_int(planeVars.get(PlanarVar.WEAPONMAXRANGE.toString())); if(((msg.source().rangeToTarget()>maxRange)||(((MOB)msg.target()).rangeToTarget()>maxRange))) { final String ammo=((AmmunitionWeapon)msg.tool()).ammunitionType(); final String msgOut=L("The @x1 fired by <S-NAME> from <O-NAME> at <T-NAME> stops moving!",ammo); final Room R=msg.source().location(); if(R!=null) R.show(msg.source(), msg.target(), msg.tool(), CMMsg.MSG_OK_VISUAL, msgOut); return false; } } break; case CMMsg.TYP_DAMAGE: if((msg.tool() instanceof Weapon) &&(this.reqWeapons!=null) &&(msg.value()>0)) { if((CMLib.flags().isABonusItems((Weapon)msg.tool()) && (this.reqWeapons.contains("MAGICAL"))) ||(this.reqWeapons.contains(Weapon.CLASS_DESCS[((Weapon)msg.tool()).weaponClassification()])) ||(this.reqWeapons.contains(Weapon.TYPE_DESCS[((Weapon)msg.tool()).weaponDamageType()]))) { // pass } else msg.setValue(0); } break; } return true; } protected static List<String> getAllPlaneKeys() { final Map<String,Map<String,String>> map = getPlaneMap(); final List<String> transitions=new ArrayList<String>(map.size()); for(final String key : map.keySet()) transitions.add(key); return transitions; } protected static List<String> getTransitionPlaneKeys() { final Map<String,Map<String,String>> map = getPlaneMap(); final List<String> transitions=new ArrayList<String>(2); for(final String key : map.keySet()) { final Map<String,String> entry=map.get(key); if(CMath.s_bool(entry.get(PlanarVar.TRANSITIONAL.toString()))) transitions.add(key); } return transitions; } protected static String listOfPlanes() { final Map<String,Map<String,String>> map = getPlaneMap(); final StringBuilder str=new StringBuilder(); for(final String key : map.keySet()) { final Map<String,String> entry=map.get(key); str.append(entry.get(PlanarVar.ID.toString())).append(", "); } if(str.length()<2) return ""; return str.toString(); } @SuppressWarnings({ "unchecked", "rawtypes" }) public static Map<String,Map<String,String>> getPlaneMap() { Map<String,Map<String,String>> map = (Map)Resources.getResource("SKILL_PLANES_OF_EXISTENCE"); if(map == null) { map = new TreeMap<String,Map<String,String>>(); final CMFile F=new CMFile(Resources.makeFileResourceName("skills/planesofexistence.txt"),null); final List<String> lines = Resources.getFileLineVector(F.text()); for(String line : lines) { line=line.trim(); String planename=null; if(line.startsWith("\"")) { final int x=line.indexOf("\"",1); if(x>1) { planename=line.substring(1,x); line=line.substring(x+1).trim(); } } if(planename != null) { final Map<String,String> planeParms = CMParms.parseEQParms(line); for(final String key : planeParms.keySet()) { if((CMath.s_valueOf(PlanarVar.class, key)==null) &&(CMLib.factions().getFaction(key)==null) &&(CMLib.factions().getFactionByName(key)==null)) Log.errOut("Spell_Planeshift","Unknown planar var: "+key); } planeParms.put(PlanarVar.ID.toString(), planename); map.put(planename.toUpperCase(), planeParms); } } Resources.submitResource("SKILL_PLANES_OF_EXISTENCE", map); } return map; } protected static Map<String,String> getPlane(String name) { final Map<String,Map<String,String>> map = getPlaneMap(); name=name.trim().toUpperCase(); if(map.containsKey(name)) return map.get(name); for(final String key : map.keySet()) { if(key.startsWith(name)) return map.get(key); } for(final String key : map.keySet()) { if(key.indexOf(name)>=0) return map.get(key); } for(final String key : map.keySet()) { if(key.endsWith(name)) return map.get(key); } return null; } protected void destroyPlane(final Area planeA) { if(planeA != null) { Area parentArea = null; int x=planeA.Name().indexOf('_'); if(x<0) x=planeA.Name().indexOf(' '); if(x>=0) parentArea = CMLib.map().getArea(Name().substring(x+1)); for(final Enumeration<Room> r=planeA.getFilledProperMap();r.hasMoreElements();) { final Room R=r.nextElement(); if(R!=null) { if(R.numInhabitants()>0) R.showHappens(CMMsg.MSG_OK_ACTION, L("This plane is fading away...")); for(final Enumeration<MOB> i=R.inhabitants();i.hasMoreElements();) { final MOB M=i.nextElement(); if((M!=null) &&(M.isPlayer())) { Room oldRoom = (this.oldRoom!=null) ? CMLib.map().getRoom(this.oldRoom.get()) : null; if((oldRoom==null) ||(oldRoom.amDestroyed()) ||(oldRoom.getArea()==null) ||(!oldRoom.getArea().isRoom(oldRoom))) oldRoom=M.getStartRoom(); for(int i1=0; (i1<50) && (oldRoom != R) && (R.isInhabitant(M) || M.location()==R);i1++) { oldRoom.bringMobHere(M, true); CMLib.commands().postLook(M,true); R.delInhabitant(M); } } } for(final Enumeration<Item> i=R.items();i.hasMoreElements();) { final Item I=i.nextElement(); if((I instanceof DeadBody) &&(((DeadBody)I).isPlayerCorpse())) { if((parentArea != null) &&(R.roomID().length()>0) &&(R.roomID().indexOf(parentArea.Name()+"#")>=0) &&(parentArea.getRoom(parentArea.Name()+R.roomID().substring(R.roomID().lastIndexOf('#'))))!=null) { final Room sendR=parentArea.getRoom(parentArea.Name()+R.roomID().substring(R.roomID().lastIndexOf('#'))); sendR.moveItemTo(I); } else { MOB M=((DeadBody)I).getSavedMOB(); if(M==null) M=CMLib.players().getPlayerAllHosts(((DeadBody)I).getMobName()); if(M!=null) { if(M.location()!=null) M.location().moveItemTo(I); else if(M.getStartRoom()!=null) M.getStartRoom().moveItemTo(I); } } } } } } final MOB mob=CMClass.getFactoryMOB(); try { final CMMsg msg=CMClass.getMsg(mob,null,null,CMMsg.MSG_EXPIRE,null); final LinkedList<Room> propRooms = new LinkedList<Room>(); for(final Enumeration<Room> r=planeA.getFilledProperMap();r.hasMoreElements();) propRooms.add(r.nextElement()); // sends everyone home for(final Iterator<Room> r=propRooms.iterator();r.hasNext();) { final Room R=r.next(); try { CMLib.map().emptyRoom(R, null, true); } catch(final Exception e) { Log.errOut(e); } } // msgs only, handles saves and stuff, but ignores grid rooms!! for(final Iterator<Room> r=propRooms.iterator();r.hasNext();) { final Room R=r.next(); try { try { R.clearSky(); msg.setTarget(R); R.executeMsg(mob,msg); } catch(final Exception e) { Log.errOut(e); } R.destroy(); // destroys the mobs and items. the Deadly Thing. } catch(final Exception e) { Log.errOut(e); } } propRooms.clear(); CMLib.map().delArea(planeA); planeA.destroy(); } finally { mob.destroy(); } planeA.destroy(); } } protected void destroyPlane() { destroyPlane(planeArea); this.planeArea=null; } protected String getStrippedRoomID(final String roomID) { final int x=roomID.indexOf('#'); if(x<0) return null; return roomID.substring(x); } protected String convertToMyArea(final String Name, final String roomID) { final String strippedID=getStrippedRoomID(roomID); if(strippedID==null) return null; return Name+strippedID; } @Override public void setAffectedOne(final Physical P) { super.setAffectedOne(P); if(invoker != null) { final MOB mob=invoker; final Room R=mob.location(); if(R!=null) { final PlanarAbility currentShift = getPlanarAbility(R.getArea()); if((currentShift != null)&&(currentShift.oldRoom!=null)&&(currentShift.oldRoom.get()!=null)) oldRoom=currentShift.oldRoom; else if(currentShift != null) oldRoom=new WeakReference<Room>(mob.getStartRoom()); else oldRoom=new WeakReference<Room>(R); } } } protected PlanarAbility getPlanarAbility(final Physical P) { for(final Enumeration<Ability> a=P.effects();a.hasMoreElements();) { final Ability A=a.nextElement(); if(A instanceof PlanarAbility) return (PlanarAbility)A; } return null; } protected String castingMessage(final MOB mob, final boolean auto) { return auto?L(""):L("^S<S-NAME> conjur(s) a powerful planar connection!^?"); } protected String failMessage(final MOB mob, final boolean auto) { return L("^S<S-NAME> attempt(s) to conjure a powerful planar connection, and fails."); } @Override public void unInvoke() { if(canBeUninvoked()) { destroyPlane(); } super.unInvoke(); } protected boolean alwaysRandomArea=false; @Override public boolean invoke(final MOB mob, final List<String> commands, final Physical givenTarget, final boolean auto, final int asLevel) { oldRoom = null; clearVars(); if(commands.size()<1) { mob.tell(L("Go where?")); mob.tell(L("Known planes: @x1",listOfPlanes()+L("Prime Material"))); return false; } String planeName=CMParms.combine(commands,0).trim().toUpperCase(); int planeNameCt=0; if(planeName.toLowerCase().endsWith("prime material")) planeName="Prime Material"; else while((getPlane(planeName)==null)&&(commands.size()>planeNameCt)) planeName=CMParms.combine(commands,++planeNameCt).trim().toUpperCase(); oldRoom=new WeakReference<Room>(mob.location()); Area cloneArea = mob.location().getArea(); final Area mobArea = cloneArea; String cloneRoomID=CMLib.map().getExtendedRoomID(mob.location()); final PlanarAbility currentShift = getPlanarAbility(mobArea); if(planeName.equalsIgnoreCase("Prime Material")) { if(!super.invoke(mob,commands,givenTarget,auto,asLevel)) return false; if(currentShift == null) { mob.tell(L("You are already on the prime material plane.")); return false; } final boolean success=proficiencyCheck(mob,0,auto); if(success) { final CMMsg msg=CMClass.getMsg(mob,null,this,CMMsg.MASK_MOVE|verbalCastCode(mob,null,auto),castingMessage(mob, auto)); if(mob.location().okMessage(mob,msg)) { mob.location().send(mob,msg); currentShift.unInvoke(); } } else { this.beneficialVisualFizzle(mob, null, failMessage(mob, auto)); } return true; } else if(this.lastCasting > (System.currentTimeMillis() - (10 * TimeManager.MILI_MINUTE))) { final Ability A=CMClass.getAbility("Disease_PlanarInstability"); if((A!=null) &&(!CMSecurity.isDisabled(CMSecurity.DisFlag.AUTODISEASE)) &&(!CMSecurity.isAbilityDisabled(A.ID()))) A.invoke(mob, mob, true, 0); } Map<String,String> planeFound = getPlane(planeName); if(planeFound == null) { mob.tell(L("There is no known plane '@x1'.",planeName)); mob.tell(L("Known planes: @x1",listOfPlanes()+L("Prime Material"))); return false; } planeName = planeFound.get(PlanarVar.ID.toString()).toUpperCase().trim(); if(!super.invoke(mob,commands,givenTarget,auto,asLevel)) return false; final boolean success=proficiencyCheck(mob,0,auto); if((currentShift!=null)&&(currentShift.text().equalsIgnoreCase(planeName))) { this.beneficialVisualFizzle(mob, null, failMessage(mob, auto)); return false; } if(currentShift != null) { final String areaName = cloneArea.Name(); final int x=areaName.indexOf('_'); if((x>0)&&(CMath.isNumber(areaName.substring(0, x)))) { final Area newCloneArea=CMLib.map().getArea(areaName.substring(x+1)); if(newCloneArea!=null) { cloneArea=newCloneArea; if(cloneRoomID.startsWith(areaName) &&(cloneArea.getRoom(cloneRoomID.substring(x+1))!=null)) { cloneRoomID=cloneRoomID.substring(x+1); } else { for(int i=0;i<100;i++) { final Room R=cloneArea.getRandomProperRoom(); if((R!=null) &&(!CMLib.flags().isHidden(R)) &&(CMLib.map().getExtendedRoomID(R).length()>0)) { cloneRoomID=CMLib.map().getExtendedRoomID(R); break; } } } } } } boolean randomPlane=false; boolean randomTransitionPlane=false; boolean randomArea=alwaysRandomArea; if(((cloneArea.flags()&Area.FLAG_INSTANCE_CHILD)==Area.FLAG_INSTANCE_CHILD) &&(currentShift == null)) randomArea=true; if(!success) { if(CMLib.dice().rollPercentage()>5) { this.beneficialVisualFizzle(mob, null, failMessage(mob, auto)); return false; } else { if(proficiency()<50) { randomPlane=true; randomArea=true; } else if(proficiency()<75) { randomTransitionPlane=true; randomArea=true; } else if(proficiency()<100) { randomTransitionPlane=true; } else randomArea=true; } } else if(proficiencyCheck(mob,-95,auto)) { // kaplah! } else if(proficiencyCheck(mob,-50,auto)) { if(proficiency()<75) { randomTransitionPlane=true; randomArea=true; } else if(proficiency()<100) { randomTransitionPlane=true; } } else { if(proficiency()<75) { randomTransitionPlane=true; randomArea=true; } else if(proficiency()<100) { randomArea=true; } else randomTransitionPlane=true; } final List<String> transitionalPlaneKeys = getTransitionPlaneKeys(); if(currentShift!=null) { if(transitionalPlaneKeys.contains(currentShift.text().toUpperCase().trim())) { if(randomTransitionPlane) randomTransitionPlane=false; else if(randomPlane) { randomPlane=false; randomTransitionPlane=true; randomArea=true; } } } if(randomArea) { int tries=0; while(((++tries)<10000)) { final Room room=CMLib.map().getRandomRoom(); if((room!=null) &&(CMLib.flags().canAccess(mob,room)) &&(CMLib.map().getExtendedRoomID(room).length()>0) &&(room.getArea().numberOfProperIDedRooms()>2)) { cloneArea=room.getArea(); cloneRoomID=CMLib.map().getExtendedRoomID(room); break; } } } if(randomTransitionPlane) { planeName = transitionalPlaneKeys.get(CMLib.dice().roll(1, transitionalPlaneKeys.size(), -1)); planeFound = getPlane(planeName); } if(randomPlane) { final List<String> allPlaneKeys = getAllPlaneKeys(); planeName = allPlaneKeys.get(CMLib.dice().roll(1, allPlaneKeys.size(), -1)); planeFound = getPlane(planeName); } final String planeCodeString = planeName + "_" + cloneArea.Name(); int hardBumpLevel = 0; if(recentVisits.containsKey(planeCodeString) &&((recentVisits.get(planeCodeString)[0]+hardBumpTimeout)>System.currentTimeMillis())) { final long[] data = this.recentVisits.get(planeCodeString); data[0]=System.currentTimeMillis(); if(data[1]==0) data[1]++; else data[1]*=2; hardBumpLevel=(int)data[1]; } else this.recentVisits.put(planeCodeString, new long[] {System.currentTimeMillis(),0}); final String newPlaneName = planeIDNum.addAndGet(1)+"_"+cloneArea.Name(); final Area planeArea = CMClass.getAreaType("SubThinInstance"); planeArea.setName(newPlaneName); planeArea.addBlurbFlag("PLANEOFEXISTENCE {"+planeName+"}"); CMLib.map().addArea(planeArea); planeArea.setAreaState(Area.State.ACTIVE); // starts ticking final Room target=CMClass.getLocale("StdRoom"); String newRoomID=this.convertToMyArea(newPlaneName,cloneRoomID); if(newRoomID==null) newRoomID=cloneRoomID; target.setRoomID(newRoomID); target.setDisplayText("Between The Planes of Existence"); target.setDescription("You are a floating consciousness between the planes of existence..."); target.setArea(planeArea); //CMLib.map().delArea(this.planeArea); final CMMsg msg=CMClass.getMsg(mob,target,this,CMMsg.MASK_MOVE|verbalCastCode(mob,target,auto),castingMessage(mob, auto)); if((mob.location().okMessage(mob,msg))&&(target.okMessage(mob,msg))) { mob.location().send(mob,msg); final List<MOB> h=properTargetList(mob,givenTarget,false); if(h==null) return false; this.lastCasting=System.currentTimeMillis(); final PlanarAbility A=(PlanarAbility)this.beneficialAffect(mob, planeArea, asLevel, 0); if(A!=null) { A.hardBumpLevel = hardBumpLevel; A.setMiscText(planeName); } final Room thisRoom=mob.location(); for (final MOB follower : h) { final CMMsg enterMsg=CMClass.getMsg(follower,target,this,CMMsg.MSG_ENTER,null,CMMsg.MSG_ENTER,null,CMMsg.MSG_ENTER,("<S-NAME> fade(s) into view.")+CMLib.protocol().msp("appear.wav",10)); final CMMsg leaveMsg=CMClass.getMsg(follower,thisRoom,this,CMMsg.MSG_LEAVE|CMMsg.MASK_MAGIC,L("<S-NAME> fade(s) away.")); if(thisRoom.okMessage(follower,leaveMsg)&&target.okMessage(follower,enterMsg)) { if(follower.isInCombat()) { CMLib.commands().postFlee(follower,("NOWHERE")); follower.makePeace(false); } thisRoom.send(follower,leaveMsg); ((Room)enterMsg.target()).bringMobHere(follower,false); follower.tell(L("\n\r\n\r")); ((Room)enterMsg.target()).send(follower,enterMsg); CMLib.commands().postLook(follower,true); } else if(follower==mob) break; } planeArea.addBlurbFlag("PLANEOFEXISTENCE {"+planeName+"}"); } // return whether it worked return success; } }