/* * (c) 2000-2009 Carlos G�mez Rodr�guez, todos los derechos reservados / all rights reserved. * Licencia en license/bsd.txt / License in license/bsd.txt */ package eu.irreality.age; import java.util.*; import eu.irreality.age.debug.Debug; import eu.irreality.age.scripting.ScriptException; import eu.irreality.age.util.Conversions; public class Spell extends Entity implements SupportingCode, UniqueNamed { /*00*/ private String spellType; /**ID del objeto.*/ /*01*/ private int idnumber; /**Hereda de.*/ /*02*/ private int inheritsFrom; /*03*/ // inherited protected int state; // inherited protected long timeunitsleft; /**Nombre sint�tico del objeto.*/ /*04*/ protected String title; /**Es instancia de.*/ /*05*/ private int isInstanceOf; /**C�digo en Ensamblador Virtual Aetheria (EVA)*/ /*80*/ protected ObjectCode itsCode; //reference names, added 2012-03-29 protected List respondToSing; protected List respondToPlur; //world reference, added 2012-03-29 private World mundo; //generador de numeros aleatorios private Random aleat; long minimalUsage; String[] involvedSkills; double[] involvedSkillRelevance; double castProbabilitySteepness; double castStartingTime; double castTimeSteepness; double durationStartingTime; double durationTimeSteepness; double manaCostSteepness; double startingManaCost; double intensitySteepness; double startingIntensity; EffectList effects; List intensities; //lista paralela de intensidades de efectos private static int USAGE_CORRECTOR = 100; public void cast ( Entity caster , Entity target ) { boolean ejecutado = false; try { ejecutado = execCode( "beforeCast" , new Object[] { caster , target } ); } catch (ScriptException bshte) { //escribir("bsh.TargetError found at fail routine" ); ; } if ( ejecutado ) return; if ( effects != null ) { for ( int i = 0 ; i < effects.size() ; i++ ) { Effect ef = (Effect) effects.elementAt(i); ef.enable ( caster , target , ((Integer)intensities.get(i)).intValue() , (int)getTypicalDuration((Mobile)caster) ); } } try { ejecutado = execCode( "afterCast" , new Object[] { caster , target } ); } catch (ScriptException bshte) { //escribir("bsh.TargetError found at fail routine" ); ; } } public void fail ( Entity caster , Entity target ) { boolean ejecutado = false; try { ejecutado = execCode( "beforeFail" , new Object[] { caster , target } ); } catch (ScriptException bshte) { //escribir("bsh.TargetError found at fail routine" ); ; } if ( ejecutado ) return; if ( effects != null ) { for ( int i = 0 ; i < effects.size() ; i++ ) { Effect ef = (Effect) effects.elementAt(i); ef.fail ( caster , target , ((Integer)intensities.get(i)).intValue() ); } } try { ejecutado = execCode( "afterFail" , new Object[] { caster , target } ); } catch (ScriptException bshte) { //escribir("bsh.TargetError found at fail routine" ); ; } } //copypasted from Weapon class //typical seg�n skill (variar� further seg�n su destreza y el factor aleatorio) private double getTypicalTime ( long usage , double starting , long minusage , double steepness ) { if ( usage < minusage ) return 0; else return ( (double)starting / ( Math.pow ( ( (double)usage - (double)minusage ) / (double)USAGE_CORRECTOR + 1 , ( Math.exp(steepness) ) ) ) ); } //uses involved skills and relevance to draw this spell's specific usage skill. public long getUsage ( Mobile m ) { double theUsage = 0; for ( int i = 0 ; i < involvedSkills.length ; i++ ) { Debug.println("Skill: " + involvedSkills[i]); Debug.println("Relevance: " + involvedSkillRelevance[i]); Debug.println("Mobile: " + m); Debug.println("Traits: " + m.caracteristicas); Debug.println("Skill for mobile: " + m.getSkill(involvedSkills[i])); theUsage += involvedSkillRelevance[i] * m.getSkill(involvedSkills[i]); } Debug.println("Usage: " + theUsage); return (long)theUsage; } //casting time //typical steepness is 1 public double getTypicalCastTime ( Mobile m ) { return getTypicalTime ( getUsage(m) , castStartingTime , minimalUsage , castTimeSteepness ); } //casting success probability //typical steepness is 0 public double getTypicalCastProbability ( Mobile m ) { return 1 - getTypicalTime ( getUsage(m) , 1.0 , minimalUsage , castProbabilitySteepness ); } //casting mana cost //typical steepness is 1 public double getTypicalManaCost ( Mobile m ) { return getTypicalTime ( getUsage(m) , startingManaCost , minimalUsage , manaCostSteepness ); } //casting duration //inverted law! public double getTypicalDuration ( Mobile m ) { Debug.println("STARTING: " + durationStartingTime); if ( durationStartingTime == 0.0 ) { Debug.println("RET 0"); return 0.0; } else { Debug.println("RET 1.0/"+getTypicalTime ( getUsage(m) , 1.0/durationStartingTime , minimalUsage , durationTimeSteepness )); return 1.0/getTypicalTime ( getUsage(m) , 1.0/durationStartingTime , minimalUsage , durationTimeSteepness ); } } //casting intensity //inverted law! public double getTypicalIntensity ( Mobile m ) { if ( startingIntensity == 0.0 ) return 0.0; else return 1.0/getTypicalTime ( getUsage(m) , 1.0/startingIntensity , minimalUsage , intensitySteepness ); } public int getID ( ) { return idnumber; } /** * @deprecated Use {@link #getUniqueName()} instead */ public String getTitle ( ) { return getUniqueName(); } public String getUniqueName ( ) { return title; } /*ejecuta el codigo bsh del objeto correspondiente a la rutina dada si existe. Si no existe, simplemente no ejecuta nada y devuelve false.*/ public boolean execCode ( String routine , Object[] args ) throws ScriptException { if ( itsCode != null ) return itsCode.run ( routine , this , args ); else return false; } /*ejecuta el codigo bsh del objeto correspondiente a la rutina dada si existe. Si no existe, simplemente no ejecuta nada y devuelve false.*/ public boolean execCode ( String routine , Object[] args , ReturnValue retval ) throws ScriptException { //S/ystem.out.println("Mobile code runnin'."); //Debug.println("Its Code: " + itsCode); if ( itsCode != null ) return itsCode.run ( routine , this , args , retval ); else return false; } //legacy change-state //[old!!] public void changeState ( World mundo ) { ; /* try { execCode("event_endstate","this: "+ getID() + "state: " + getState() ); } catch ( EVASemanticException exc ) { mundo.escribir("EVASemanticException found at event_endstate , item number " + getID() ); } */ } //important //[define event here!! and do it for other entity types, too!] public /*abstract*/ boolean update ( PropertyEntry pe , World mundo ) { return true; } public void setID ( int newid ) { if ( newid < Utility.spell_summand ) idnumber = newid + Utility.spell_summand; else idnumber = newid; } public void loadNumberGenerator ( World mundo ) { aleat = mundo.getRandom(); } public java.util.Random getRandom() { return aleat; } public org.w3c.dom.Node getXMLRepresentation ( org.w3c.dom.Document doc ) { org.w3c.dom.Element suElemento = doc.createElement( "Spell" ); suElemento.setAttribute ( "id" , String.valueOf( idnumber ) ); suElemento.setAttribute ( "name" , String.valueOf ( title ) ); suElemento.setAttribute ( "extends" , String.valueOf ( inheritsFrom ) ); suElemento.setAttribute ( "clones" , String.valueOf ( isInstanceOf ) ); suElemento.setAttribute ( "type" , String.valueOf ( spellType ) ); suElemento.setAttribute ( "minUsage" , String.valueOf ( minimalUsage ) ); suElemento.appendChild ( getPropListXMLRepresentation(doc) ); suElemento.appendChild ( getRelationshipListXMLRepresentation(doc) ); org.w3c.dom.Element attackElement = suElemento; //for copy-paste purposes org.w3c.dom.Element castProbabilityElement = doc.createElement("CastProbability"); { castProbabilityElement.setAttribute("steepness",String.valueOf(castProbabilitySteepness)); attackElement.appendChild(castProbabilityElement); } org.w3c.dom.Element castTimeElement = doc.createElement("CastTime"); { castTimeElement.setAttribute("steepness",String.valueOf(castTimeSteepness)); castTimeElement.setAttribute("starting",String.valueOf(castStartingTime)); attackElement.appendChild(castTimeElement); } org.w3c.dom.Element durationTimeElement = doc.createElement("Duration"); { durationTimeElement.setAttribute("steepness",String.valueOf(durationTimeSteepness)); durationTimeElement.setAttribute("starting",String.valueOf(durationStartingTime)); attackElement.appendChild(durationTimeElement); } org.w3c.dom.Element attackSkillsElement = doc.createElement("InvolvedSkills"); { if ( involvedSkills != null ) { for ( int i = 0 ; i < involvedSkills.length ; i++ ) { org.w3c.dom.Element thisSkillElement = doc.createElement("Skill"); thisSkillElement.setAttribute("name",involvedSkills[i]); thisSkillElement.setAttribute("relevance",String.valueOf(involvedSkillRelevance[i])); attackSkillsElement.appendChild(thisSkillElement); } } attackElement.appendChild(attackSkillsElement); } org.w3c.dom.Element manaCostElement = doc.createElement("ManaCost"); { manaCostElement.setAttribute("steepness",String.valueOf(manaCostSteepness)); manaCostElement.setAttribute("starting",String.valueOf(startingManaCost)); attackElement.appendChild(manaCostElement); } org.w3c.dom.Element intensityElement = doc.createElement("Intensity"); { intensityElement.setAttribute("steepness",String.valueOf(intensitySteepness)); intensityElement.setAttribute("starting",String.valueOf(startingIntensity)); attackElement.appendChild(intensityElement); } /* org.w3c.dom.Element attackDamageElement = doc.createElement("DamageList"); { if ( attack_damage_formula != null ) { StringTokenizer st = new StringTokenizer(attack_damage_formula,"$"); while ( st.hasMoreTokens() ) { String curToken = st.nextToken(); StringTokenizer st2 = new StringTokenizer ( curToken , ":" ); //separa el tipo de da�o del da�o String damTypeString = st2.nextToken().trim(); String damFormulaString = st2.nextToken().trim(); org.w3c.dom.Element curDamageElement = doc.createElement("Damage"); curDamageElement.setAttribute("type",damTypeString); curDamageElement.setAttribute("formula",damFormulaString); attackDamageElement.appendChild(curDamageElement); } } attackElement.appendChild(attackDamageElement); } */ //"respond to" names (temp XML representation) if ( respondToSing != null ) { org.w3c.dom.Element respTo = getNameListXMLRepresentation(doc,respondToSing,"SingularReferenceNames"); suElemento.appendChild(respTo); } if ( respondToPlur != null ) { org.w3c.dom.Element respTo = getNameListXMLRepresentation(doc,respondToPlur,"PluralReferenceNames"); suElemento.appendChild(respTo); } if ( effects != null ) suElemento.appendChild ( effects.getXMLRepresentation ( doc , intensities ) ); //object code if ( itsCode != null ) suElemento.appendChild(itsCode.getXMLRepresentation(doc)); return suElemento; } /** ** CONSTRUCTING CODE ** **/ public static Spell getInstance ( World mundo , org.w3c.dom.Node n ) throws XMLtoWorldException { if ( ! ( n instanceof org.w3c.dom.Element ) ) { throw ( new XMLtoWorldException("Spell node not Element") ); } //{n is an Element} org.w3c.dom.Element e = (org.w3c.dom.Element)n; Spell ourNewSpell; if ( !e.hasAttribute("type") ) { ourNewSpell = new Spell ( mundo , n ); } /* else if ( e.getAttribute("type").equalsIgnoreCase("effect") ) { ourNewAbstractEntity = new Effect ( mundo , n ); } */ else { ourNewSpell = new Spell ( mundo , n ); } return ourNewSpell; } public Spell ( World mundo , org.w3c.dom.Node n ) throws XMLtoWorldException { constructSpell ( mundo , n , true , "none" ); } public void constructSpell ( World mundo , org.w3c.dom.Node n , boolean allowInheritance , String spelltype ) throws XMLtoWorldException { this.mundo = mundo; if ( ! ( n instanceof org.w3c.dom.Element ) ) { throw ( new XMLtoWorldException ( "Spell node not Element" ) ); } //{n is an Element} org.w3c.dom.Element e = (org.w3c.dom.Element) n; //type spellType = spelltype; //attribs //weak inheritance? if ( e.hasAttribute("extends") && !e.getAttribute("extends").equals("0") && allowInheritance ) { //item must extend from existing item. //clonamos ese item y overrideamos lo overrideable //(n�tese que la ID del item extendido ha de ser menor). //por eso los associated nodes de los items quedan guardados [por ref] en el World hasta que //haya concluido la construccion del mundo //1. overrideamos el super-item usando su associated node para construirlo constructSpell ( mundo , mundo.getSpellNode( e.getAttribute("extends") ) , true , spelltype ); //2. overrideamos lo que debamos overridear constructSpell ( mundo , n , false , spelltype ); return; } //strong inheritance? if ( e.hasAttribute("clones") && !e.getAttribute("clones").equals("0") && allowInheritance ) { //funciona igual que la weak inheritance a este nivel. //no deberian aparecer los dos; pero si asi fuera esta herencia (la fuerte) tendria precedencia. //1. overrideamos el super-item usando su associated node para construirlo constructSpell ( mundo , mundo.getSpellNode( e.getAttribute("clones") ) , true , spelltype ); //2. overrideamos lo que debamos overridear constructSpell ( mundo , n , false , spelltype ); return; } //mandatory XML-attribs exceptions if ( !e.hasAttribute("name") ) throw ( new XMLtoWorldException ( "Spell node lacks attribute name" ) ); //mandatory XML-attribs parsing //id no longer mandatory try { if ( e.hasAttribute("id") ) idnumber = Integer.valueOf ( e.getAttribute("id") ).intValue(); } catch ( NumberFormatException nfe ) { throw ( new XMLtoWorldException ( "Bad number format at attribute id in spell node" ) ); } title = e.getAttribute("name"); //Entity parsing readPropListFromXML ( mundo , n ); //spell-specifics /* */ //spell minimal usage if ( e.hasAttribute("minUsage") ) { try { minimalUsage = Long.valueOf ( e.getAttribute("minUsage") ).longValue(); } catch ( NumberFormatException nfe ) { throw ( new XMLtoWorldException("Number format error at minimalUsage") ); } } else minimalUsage = 0; //default value //spell involved skills org.w3c.dom.NodeList nl1 = e.getElementsByTagName("InvolvedSkills"); if ( nl1.getLength() > 0 ) { org.w3c.dom.Element invsk = (org.w3c.dom.Element) nl1.item(0); org.w3c.dom.NodeList skillnodes = invsk.getElementsByTagName("Skill"); involvedSkills = new String[skillnodes.getLength()]; involvedSkillRelevance = new double [skillnodes.getLength()]; for ( int i = 0 ; i < skillnodes.getLength() ; i++ ) { org.w3c.dom.Element skillNode = (org.w3c.dom.Element) skillnodes.item(i); if ( !skillNode.hasAttribute("name") ) throw ( new XMLtoWorldException("Skill node lacking attribute name") ); if ( !skillNode.hasAttribute("relevance") ) //throw ( new XMLtoWorldException("Skill node lacking attribute relevance") ); involvedSkillRelevance[i] = 1.0; try { if ( skillNode.hasAttribute("relevance") ) involvedSkillRelevance[i] = Double.valueOf ( skillNode.getAttribute("relevance") ).doubleValue(); involvedSkills[i] = skillNode.getAttribute("name"); } catch ( NumberFormatException nfe ) { throw ( new XMLtoWorldException("Wrong number format for relevance attribute at weapon's attack skill node") ); } } } //cast probability org.w3c.dom.NodeList nl = e.getElementsByTagName("CastProbability"); if ( nl.getLength() > 0 ) { org.w3c.dom.Element atProb = (org.w3c.dom.Element) nl.item(0); if ( atProb.hasAttribute("steepness") ) { try { castProbabilitySteepness = Double.valueOf ( atProb.getAttribute("steepness") ).doubleValue(); } catch ( NumberFormatException nfe ) { throw ( new XMLtoWorldException("Number format error at attackProbabilitySteepness") ); } } else castProbabilitySteepness = 0; //default value } //cast time nl = e.getElementsByTagName("CastTime"); if ( nl.getLength() > 0 ) { org.w3c.dom.Element atProb = (org.w3c.dom.Element) nl.item(0); if ( atProb.hasAttribute("steepness") ) { try { castTimeSteepness = Double.valueOf ( atProb.getAttribute("steepness") ).doubleValue(); } catch ( NumberFormatException nfe ) { throw ( new XMLtoWorldException("Number format error at castTimeSteepness") ); } } else castTimeSteepness = 0; //default value if ( atProb.hasAttribute("starting") ) { try { castStartingTime = Double.valueOf ( atProb.getAttribute("starting") ).doubleValue(); } catch ( NumberFormatException nfe ) { throw ( new XMLtoWorldException("Number format error at castStartingTime") ); } } else castStartingTime = 30; //default value } //duration time nl = e.getElementsByTagName("Duration"); if ( nl.getLength() > 0 ) { org.w3c.dom.Element atProb = (org.w3c.dom.Element) nl.item(0); if ( atProb.hasAttribute("steepness") ) { try { durationTimeSteepness = Double.valueOf ( atProb.getAttribute("steepness") ).doubleValue(); } catch ( NumberFormatException nfe ) { throw ( new XMLtoWorldException("Number format error at durationTimeSteepness") ); } } else durationTimeSteepness = 0; //default value if ( atProb.hasAttribute("starting") ) { try { durationStartingTime = Double.valueOf ( atProb.getAttribute("starting") ).doubleValue(); } catch ( NumberFormatException nfe ) { throw ( new XMLtoWorldException("Number format error at durationStartingTime") ); } } else durationStartingTime = 0; //default value } //mana cost nl = e.getElementsByTagName("ManaCost"); if ( nl.getLength() > 0 ) { org.w3c.dom.Element atProb = (org.w3c.dom.Element) nl.item(0); if ( atProb.hasAttribute("steepness") ) { try { manaCostSteepness = Double.valueOf ( atProb.getAttribute("steepness") ).doubleValue(); } catch ( NumberFormatException nfe ) { throw ( new XMLtoWorldException("Number format error at manaCostSteepness") ); } } else manaCostSteepness = 0; //default value if ( atProb.hasAttribute("starting") ) { try { startingManaCost = Double.valueOf ( atProb.getAttribute("starting") ).doubleValue(); } catch ( NumberFormatException nfe ) { throw ( new XMLtoWorldException("Number format error at startingManaCost") ); } } else startingManaCost = 30; //default value } //intensity nl = e.getElementsByTagName("Intensity"); if ( nl.getLength() > 0 ) { org.w3c.dom.Element atProb = (org.w3c.dom.Element) nl.item(0); if ( atProb.hasAttribute("steepness") ) { try { intensitySteepness = Double.valueOf ( atProb.getAttribute("steepness") ).doubleValue(); } catch ( NumberFormatException nfe ) { throw ( new XMLtoWorldException("Number format error at intensitySteepness") ); } } else intensitySteepness = 0; //default value if ( atProb.hasAttribute("starting") ) { try { startingIntensity = Double.valueOf ( atProb.getAttribute("starting") ).doubleValue(); } catch ( NumberFormatException nfe ) { throw ( new XMLtoWorldException("Number format error at startingManaCost") ); } } else startingIntensity = 30; //default value } //spell effects nl = e.getElementsByTagName("EffectList"); if ( nl.getLength() > 0 ) { org.w3c.dom.Element effectListNode = (org.w3c.dom.Element) nl.item(0); effects = new EffectList ( mundo , effectListNode ); org.w3c.dom.NodeList nl2 = effectListNode.getElementsByTagName("EffectRef"); intensities = new Vector(); for ( int r = 0 ; r < nl2.getLength() ; r++ ) { org.w3c.dom.Element h = (org.w3c.dom.Element) nl2.item(r); if ( h.hasAttribute("intensity") ) intensities.add ( new Integer ( h.getAttribute("intensity") ) ); else intensities.add(new Integer(0)); } } //2012-03-29: now spells have reference names too. //singular reference names (respondToSing) respondToSing = Utility.loadNameListFromXML ( mundo , e , "SingularReferenceNames" , true ); //plural reference names (respondToPlur) respondToPlur = Utility.loadNameListFromXML ( mundo , e , "PluralReferenceNames" , true ); org.w3c.dom.NodeList codeNodes = e.getElementsByTagName ( "Code" ); if ( codeNodes.getLength() > 0 ) { try { itsCode = new ObjectCode ( mundo , codeNodes.item(0) ); } catch ( XMLtoWorldException ex ) { throw ( new XMLtoWorldException ( "Exception at Code node: " + ex.getMessage() ) ); } } //FINALLY... type-specifics!! /* if ( absentType.equalsIgnoreCase("effect") ) { ((Effect)this).readEffectSpecifics ( mundo , e ); } */ //poner bien la id if ( getID() < 10000000 ) idnumber += 50000000; //prefijo de spell. } /** * Nos dice si se da por aludido el objeto ante un comando (ej. "mirar piedra") -> se pasa "piedra") * * @param commandArgs Los argumentos del comando. * @param pluralOrSingular 1 si plural. * @return 0 si no se da por aludido, 1 si s�, y con qu� prioridad. (+ n� = - prioridad). La prioridad es el orden que ocupa el nombre que se corresponde con el comando dado en la lista de nombres. */ public int matchesCommand ( String commandArgs , boolean pluralOrSingular ) { List listaDeInteres; if ( pluralOrSingular ) // plural listaDeInteres = respondToPlur; else listaDeInteres = respondToSing; int modern = matchesCommand ( commandArgs , listaDeInteres , mundo.getCommandMatchingMode() ); if ( modern > 0 ) return modern; else return commandArgs.trim().equalsIgnoreCase(getUniqueName())?1:0; } public void incrementUsage ( Mobile m ) { for ( int i = 0 ; i < involvedSkills.length ; i++ ) { if ( m.getRandom().nextDouble() < involvedSkillRelevance[i] ) m.incSkill ( involvedSkills[i] ); } } public ObjectCode getAssociatedCode() { return itsCode; } /** * Obtain a list with the singular reference names of the mobile, in order. */ public List getSingularReferenceNames() { return (List) ((ArrayList)respondToSing).clone(); } /** * Obtain a list with the plural reference names of the mobile, in order. */ public List getPluralReferenceNames() { return (List) ((ArrayList)respondToPlur).clone(); } /** * Adds a new singular reference name, with lower priority than the existing reference names. */ public void addSingularReferenceName(String newName) { respondToSing.add(newName); mundo.getSpellChecker().addNewName(newName); } /** * Adds a new singular reference name, located at the given index that defines its priority. */ public void addSingularReferenceName(int index,String newName) { respondToSing.add(index,newName); mundo.getSpellChecker().addNewName(newName); } /** * Removes a singular reference name from the list of such names, if present. Returns whether it has actually been removed or not. * Note that this does not remove any word from the world's vocabulary used by the spell checker. * Call the rebuild() method in the world's spell checker if this effect is desired (but beware, that takes a nontrivial amount of time) */ public boolean removeSingularReferenceName(String oldName) { return respondToSing.remove(oldName); } /** * Adds a new plural reference name, with lower priority than the existing reference names. */ public void addPluralReferenceName(String newName) { respondToPlur.add(newName); mundo.getSpellChecker().addNewName(newName); } /** * Adds a new plural reference name, located at the given index that defines its priority. */ public void addPluralReferenceName(int index,String newName) { respondToPlur.add(index,newName); mundo.getSpellChecker().addNewName(newName); } /** * Removes a plural reference name from the list of such names, if present. Returns whether it has actually been removed or not. * Note that this does not remove any word from the world's vocabulary used by the spell checker. * Call the rebuild() method in the world's spell checker if this effect is desired (but beware, that takes a nontrivial amount of time) */ public boolean removePluralReferenceName(String oldName) { return respondToPlur.remove(oldName); } }