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-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.
*/
@SuppressWarnings({"unchecked","rawtypes"})
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 processing = false;
protected boolean uninvocable = true;
protected short level = -1;
protected short maxTicks = -1;
protected short chanceToHappen = -1;
protected List<Ability> spellV = null;
protected MaskingLibrary.CompiledZapperMask compiledMask=null;
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(String newText)
{
final int maskindex=newText.toUpperCase().indexOf("MASK=");
if(maskindex>0)
return newText.substring(maskindex+5).trim();
return "";
}
public String getParmString(String newText)
{
final int maskindex=newText.toUpperCase().indexOf("MASK=");
if(maskindex>0)
return newText.substring(0,maskindex).trim();
return newText;
}
@Override
public void setMiscText(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);
}
public List<Ability> getMySpellsV()
{
if(spellV!=null)
return spellV;
spellV=new Vector<Ability>();
final String names=getParmString(text());
final List<String> set=CMParms.parseSemicolons(names,true);
String thisOne=null;
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;
}
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);
}
}
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(List<Ability> V)
{
final Hashtable<String, String> spellH=new Hashtable<String, String>();
for(int v=0;v<V.size();v++)
spellH.put(V.get(v).ID(),V.get(v).ID());
return spellH;
}
public MOB getBestInvokerMOB(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(Environmental source, 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<Object> convertToV2(List<Ability> spellsV, Physical target)
{
final List<Object> VTOO=new Vector<Object>();
for(int v=0;v<spellsV.size();v++)
{
Ability A=spellsV.get(v);
final Ability EA=(target!=null)?target.fetchEffect(A.ID()):null;
if((EA==null)&&(didHappen()))
{
final String t=A.text();
A=(Ability)A.copyOf();
Vector<String> V2=new Vector<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(A);
VTOO.add(V2);
}
}
return VTOO;
}
public boolean addMeIfNeccessary(PhysicalAgent source, Physical target, boolean makeLongLasting, int asLevel, short maxTicks)
{
final List<Ability> V=getMySpellsV();
if((target==null)
||(V.size()==0)
||((compiledMask!=null)
&&(!CMLib.masking().maskCheck(compiledMask,target,true))))
return false;
final List VTOO=convertToV2(V,target);
if(VTOO.size()==0)
return false;
final MOB qualMOB=getInvokerMOB(source,target);
for(int v=0;v<VTOO.size();v+=2)
{
final Ability A=(Ability)VTOO.get(v);
final Vector V2=(Vector)VTOO.get(v+1);
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(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(Physical P)
{
super.setAffectedOne(P);
if(P == null)
{
removeMyAffectsFromLastMOB();
finalize();
}
}
public void removeMyAffectsFrom(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());
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(Environmental host, 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(Physical host, PhyStats affectableStats)
{
if(processing)
return;
if((affected instanceof MOB)
||(affected instanceof Item))
{
processing=true;
if((lastMOB!=null)
&&(host!=lastMOB))
removeMyAffectsFrom(lastMOB);
if((lastMOB==null)&&(host instanceof PhysicalAgent))
addMeIfNeccessary((PhysicalAgent)host,host,true,0,maxTicks);
processing=false;
}
}
public String spellAccountingsWithMask(String pre, String post)
{
final List<Ability> spellList=getMySpellsV();
String id="";
for(int v=0;v<spellList.size();v++)
{
final Ability A=spellList.get(v);
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(Ability to)
{
}
@Override
public void delAbility(Ability to)
{
}
@Override
public int numAbilities()
{
return getMySpellsV().size();
}
@Override
public Ability fetchAbility(int index)
{
final List<Ability> spellsV = getMySpellsV();
if (spellsV.size() == 0)
return null;
if ((index < 0) || (index >= spellsV.size()))
return null;
try
{
return spellsV.get(index);
}
catch (final Exception e)
{
return null;
}
}
@Override
public Ability fetchAbility(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 List<Ability> spellsV = getMySpellsV();
if (spellsV.size() == 0)
return null;
return spellsV.get(CMLib.dice().roll(1, spellsV.size(), -1));
}
@Override
public Enumeration<Ability> abilities()
{
return new FilteredEnumeration<Ability>(new IteratorEnumeration<Ability>(getMySpellsV().iterator()),new Filterer<Ability>()
{
@Override
public boolean passesFilter(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().iterator());
}
@Override
public boolean invoke(MOB mob, List<String> commands, Physical givenTarget, boolean auto, 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;
}
}