package com.planet_ink.coffee_mud.Abilities.Properties; 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.MaskingLibrary; import com.planet_ink.coffee_mud.Locales.interfaces.*; import com.planet_ink.coffee_mud.MOBS.interfaces.*; import com.planet_ink.coffee_mud.Races.interfaces.*; import java.util.*; /* Copyright 2001-2019 Bo Zimmerman Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ public class Prop_SpellAdder extends Property implements AbilityContainer, TriggeredAffect { @Override public String ID() { return "Prop_SpellAdder"; } @Override public String name() { return "Casting spells on oneself"; } @Override protected int canAffectCode() { return Ability.CAN_ITEMS | Ability.CAN_ROOMS | Ability.CAN_AREAS | Ability.CAN_MOBS; } protected Physical lastMOB = null; protected MOB invokerMOB = null; protected boolean uninvocable = true; protected short level = -1; protected short maxTicks = -1; protected short chanceToHappen = -1; protected PairList<Ability, Integer> spellV = null; protected MaskingLibrary.CompiledZMask compiledMask= null; protected volatile boolean processing = false; protected List<Ability> unrevocableSpells = null; @Override public long flags() { return Ability.FLAG_CASTER; } @Override public int triggerMask() { return TriggeredAffect.TRIGGER_ALWAYS; } @Override protected void finalize() { spellV=null; compiledMask=null; chanceToHappen=-1; unrevocableSpells=null; if((invokerMOB!=null)&&(invokerMOB.Name().equals("invoker"))) invokerMOB.destroy(); } public String getMaskString(final String newText) { final int maskindex=newText.toUpperCase().indexOf("MASK="); if(maskindex>0) return newText.substring(maskindex+5).trim(); return ""; } public String getParmString(final String newText) { final int maskindex=newText.toUpperCase().indexOf("MASK="); if(maskindex>0) return newText.substring(0,maskindex).trim(); return newText; } @Override public void setMiscText(final String newText) { super.setMiscText(newText); spellV=null; compiledMask=null; lastMOB=null; chanceToHappen=-1; maxTicks=-1; final String maskString=getMaskString(newText); if(maskString.length()>0) compiledMask=CMLib.masking().getPreCompiledMask(maskString); } protected final PairList<Ability, Integer> getMySpellsV() { if(spellV!=null) return spellV; spellV=new PairVector<Ability, Integer>(); final String names=getParmString(text()); final List<String> set=CMParms.parseSemicolons(names,true); String thisOne=null; Integer ticks = Integer.valueOf(-1); for(int s=0;s<set.size();s++) { thisOne=set.get(s); if(thisOne.equalsIgnoreCase("NOUNINVOKE")) { this.uninvocable=false; continue; } if(thisOne.toUpperCase().startsWith("LEVEL")) { level=(short)CMParms.getParmInt(thisOne,"LEVEL",-1); if(level>=0) continue; } if(thisOne.toUpperCase().startsWith("MAXTICKS")) { maxTicks=(short)CMParms.getParmInt(thisOne,"MAXTICKS",-1); if(maxTicks!=-1) continue; } if(thisOne.toUpperCase().startsWith("TICKS")) { ticks = Integer.valueOf(CMParms.getParmInt(thisOne,"TICKS",-1)); continue; } final int pctDex=thisOne.indexOf("% "); if((pctDex>0) && (thisOne.substring(pctDex+1).trim().length()>0)) thisOne=thisOne.substring(pctDex+1).trim(); String parm=""; if((thisOne!=null)&&(thisOne.endsWith(")"))) { final int x=thisOne.indexOf('('); if(x>0) { parm=thisOne.substring(x+1,thisOne.length()-1); thisOne=thisOne.substring(0,x).trim(); } } Ability A=CMClass.getAbility(thisOne); if((A!=null)&&((A.classificationCode()&Ability.ALL_DOMAINS)!=Ability.DOMAIN_ARCHON)) { A=(Ability)A.copyOf(); A.setMiscText(parm); spellV.add(A, ticks); } } return spellV; } public boolean didHappen() { if(chanceToHappen<0) { final String parmString=getParmString(text()); int x=parmString.indexOf('%'); if(x<0) { chanceToHappen=100; return true; } int mul=1; int tot=0; while((--x)>=0) { if(Character.isDigit(parmString.charAt(x))) tot+=CMath.s_int(""+parmString.charAt(x))*mul; else x=-1; mul=mul*10; } chanceToHappen=(short)tot; } if(CMLib.dice().rollPercentage()<=chanceToHappen) return true; return false; } public Map<String, String> makeMySpellsH(final Iterator<Ability> v) { final Hashtable<String, String> spellH=new Hashtable<String, String>(); for(;v.hasNext();) { final Ability A=v.next(); spellH.put(A.ID(),A.ID()); } return spellH; } public MOB getBestInvokerMOB(final Environmental target) { if(target instanceof MOB) return (MOB)target; if((target instanceof Item)&&(((Item)target).owner()!=null)&&(((Item)target).owner() instanceof MOB)) return (MOB)((Item)target).owner(); return null; } public MOB getInvokerMOB(final Environmental source, final Environmental target) { MOB mob=getBestInvokerMOB(affected); if(mob==null) mob=getBestInvokerMOB(source); if(mob==null) mob=getBestInvokerMOB(target); if(mob==null) mob=invokerMOB; if(mob==null) { Room R=CMLib.map().roomLocation(target); if(R==null) R=CMLib.map().roomLocation(target); if(R==null) R=CMLib.map().getRandomRoom(); mob=CMLib.map().getFactoryMOB(R); mob.setName(L("invoker")); mob.basePhyStats().setLevel(affected.phyStats().level()); mob.phyStats().setLevel(affected.phyStats().level()); } invokerMOB=mob; return invokerMOB; } public List<Triad<Ability, List<String>, Integer>> convertToV2(final PairList<Ability, Integer> spellsV, final Physical target) { final List<Triad<Ability, List<String>, Integer>> VTOO=new ArrayList<Triad<Ability, List<String>, Integer>>(); for(int v=0;v<spellsV.size();v++) { Ability A=spellsV.getFirst(v); final Integer ticksA=spellsV.getSecond(v); final Ability EA=(target!=null)?target.fetchEffect(A.ID()):null; if((EA==null)&&(didHappen())) { final String t=A.text(); A=(Ability)A.copyOf(); List<String> V2=new ArrayList<String>(); if(t.length()>0) { final int x=t.indexOf('/'); if(x<0) { V2=CMParms.parse(t); A.setMiscText(""); } else { V2=CMParms.parse(t.substring(0,x)); A.setMiscText(t.substring(x+1)); } } VTOO.add(new Triad<Ability, List<String>, Integer>(A, V2, ticksA)); } } return VTOO; } public boolean addMeIfNeccessary(final PhysicalAgent source, final Physical target, final boolean makeLongLasting, int asLevel, final short maxTicks) { final PairList<Ability, Integer> V=getMySpellsV(); if((target==null) ||(V.size()==0) ||((compiledMask!=null) &&(!CMLib.masking().maskCheck(compiledMask,target,true)))) return false; final List<Triad<Ability, List<String>, Integer>> VTOO=convertToV2(V,target); if(VTOO.size()==0) return false; final MOB qualMOB=getInvokerMOB(source,target); for(int v=0;v<VTOO.size();v++) { final Triad<Ability, List<String>, Integer> triad = VTOO.get(v); final Ability A=triad.first; final List<String> V2=triad.second; final int ticksA=triad.third.intValue(); if(level >= 0) asLevel = level; else if(asLevel <=0) asLevel = (affected!=null)?affected.phyStats().level():0; A.invoke(qualMOB,V2,target,true,asLevel); final Ability EA=target.fetchEffect(A.ID()); lastMOB=target; // this needs to go here because otherwise it makes non-item-invoked spells long lasting, // which means they dont go away when item is removed. if(EA!=null) { if((maxTicks>0) &&(maxTicks<Short.MAX_VALUE) &&(CMath.s_int(EA.getStat("TICKDOWN"))>maxTicks)) EA.setStat("TICKDOWN", Short.toString(maxTicks)); else if((ticksA>0) &&(ticksA<Short.MAX_VALUE) &&(CMath.s_int(EA.getStat("TICKDOWN"))>ticksA)) EA.setStat("TICKDOWN", Integer.toString(ticksA)); else if(makeLongLasting) { EA.makeLongLasting(); if(!uninvocable) { EA.makeNonUninvokable(); if(unrevocableSpells == null) unrevocableSpells = new Vector<Ability>(); unrevocableSpells.add(EA); } } } } return true; } @Override public String accountForYourself() { return spellAccountingsWithMask("Casts ", " on the first one who enters."); } public void removeMyAffectsFromLastMOB() { removeMyAffectsFrom(lastMOB); lastMOB=null; } @Override public void setAffectedOne(final Physical P) { super.setAffectedOne(P); if(P == null) { removeMyAffectsFromLastMOB(); finalize(); } } public void removeMyAffectsFrom(final Physical P) { if(P==null) return; int x=0; final Vector<Ability> eff=new Vector<Ability>(); Ability thisAffect=null; for(x=0;x<P.numEffects();x++) // personal { thisAffect=P.fetchEffect(x); if(thisAffect!=null) eff.addElement(thisAffect); } if(eff.size()>0) { final Map<String,String> h=makeMySpellsH(getMySpellsV().firstIterator()); if(unrevocableSpells != null) { for(int v=unrevocableSpells.size()-1;v>=0;v--) { thisAffect = unrevocableSpells.get(v); if(h.containsKey(thisAffect.ID())) P.delEffect(thisAffect); } } else for(x=0;x<eff.size();x++) { thisAffect=eff.elementAt(x); final String ID=h.get(thisAffect.ID()); if((ID!=null) &&(thisAffect.invoker()==getInvokerMOB(P,P))) { thisAffect.unInvoke(); if((!uninvocable)&&(!thisAffect.canBeUninvoked())) P.delEffect(thisAffect); } } unrevocableSpells = null; } } @Override public void executeMsg(final Environmental host, final CMMsg msg) { if((affected instanceof Room)||(affected instanceof Area)) { if((msg.targetMinor()==CMMsg.TYP_LEAVE) ||(msg.sourceMinor()==CMMsg.TYP_RECALL)) removeMyAffectsFrom(msg.source()); if(msg.targetMinor()==CMMsg.TYP_ENTER) addMeIfNeccessary(msg.source(),msg.source(),true,0,maxTicks); } super.executeMsg(host,msg); } @Override public void affectPhyStats(final Physical host, final PhyStats affectableStats) { if(processing) return; if((affected instanceof MOB) ||(affected instanceof Item)) { try { processing=true; if((lastMOB!=null) &&(host!=lastMOB)) removeMyAffectsFrom(lastMOB); if((lastMOB==null)&&(host instanceof PhysicalAgent)) addMeIfNeccessary((PhysicalAgent)host,host,true,0,maxTicks); } finally { processing=false; } } } public String spellAccountingsWithMask(final String pre, final String post) { final PairList<Ability, Integer> spellList = getMySpellsV(); String id=""; for(int v=0;v<spellList.size();v++) { final Ability A=spellList.get(v).first; if(spellList.size()==1) id+=A.name(); else if(v==(spellList.size()-1)) id+="and "+A.name(); else id+=A.name()+", "; } if(spellList.size()>0) id=pre+id+post; final String maskString=getMaskString(text()); if(maskString.length()>0) id+=" Restrictions: "+CMLib.masking().maskDesc(maskString); return id; } @Override public void addAbility(final Ability to) { throw new java.lang.UnsupportedOperationException(); } @Override public void delAbility(final Ability to) { throw new java.lang.UnsupportedOperationException(); } @Override public int numAbilities() { return getMySpellsV().size(); } @Override public Ability fetchAbility(final int index) { final PairList<Ability, Integer> spellsV = getMySpellsV(); if (spellsV.size() == 0) return null; if ((index < 0) || (index >= spellsV.size())) return null; try { return spellsV.get(index).first; } catch (final Exception e) { return null; } } @Override public Ability fetchAbility(final String ID) { for (final Enumeration<Ability> a = abilities(); a.hasMoreElements();) { final Ability A = a.nextElement(); if (A == null) continue; if (A.ID().equalsIgnoreCase(ID)) return A; } return null; } @Override public Ability fetchRandomAbility() { final PairList<Ability, Integer> spellsV = getMySpellsV(); if (spellsV.size() == 0) return null; return spellsV.get(CMLib.dice().roll(1, spellsV.size(), -1)).first; } @Override public Enumeration<Ability> abilities() { return new FilteredEnumeration<Ability>(new IteratorEnumeration<Ability>(getMySpellsV().firstIterator()),new Filterer<Ability>() { @Override public boolean passesFilter(final Ability obj) { return didHappen(); } }); } @Override public void delAllAbilities() { setMiscText(""); } @Override public int numAllAbilities() { return numAbilities(); } @Override public Enumeration<Ability> allAbilities() { return new IteratorEnumeration<Ability>(getMySpellsV().firstIterator()); } @Override public boolean invoke(final MOB mob, final List<String> commands, final Physical givenTarget, final boolean auto, final int asLevel) { final String s=CMParms.combine(commands,0); if(s.length()>0) setMiscText(s); if(givenTarget!=null) addMeIfNeccessary(mob,givenTarget,false,asLevel,maxTicks); return true; } }