use of pcgen.core.character.SpellBook in project pcgen by PCGen.
the class PlayerCharacter method addSpell.
/**
* @param acs
* is the CharacterSpell object containing the spell which is to
* be modified
* @param aFeatList
* is the list of feats to be added to the SpellInfo object
* @param classKey
* is the name of the class whose list of character spells will
* be modified
* @param bookName
* is the name of the book for the SpellInfo object
* @param spellLevel
* is the original (unadjusted) level of the spell not including
* feat adjustments
* @param adjSpellLevel
* is the adjustedLevel (including feat adjustments) of this
* spell, it may be higher if the user chooses a higher level.
*
* @return an empty string on successful completion, otherwise the return
* value indicates the reason the add function failed.
*/
public String addSpell(CharacterSpell acs, final List<Ability> aFeatList, final String classKey, final String bookName, final int adjSpellLevel, final int spellLevel) {
if (acs == null) {
return "Invalid parameter to add spell";
}
PCClass aClass = null;
final Spell aSpell = acs.getSpell();
if ((bookName == null) || (bookName.isEmpty())) {
return "Invalid spell list/book name.";
}
if (!hasSpellBook(bookName)) {
return "Could not find spell list/book " + bookName;
}
if (classKey != null) {
aClass = getClassKeyed(classKey);
if ((aClass == null) && (classKey.lastIndexOf('(') >= 0)) {
aClass = getClassKeyed(classKey.substring(0, classKey.lastIndexOf('(')).trim());
}
}
// If this is a spellbook, the class doesn't have to be one the PC has
// already.
SpellBook spellBook = getSpellBookByName(bookName);
if (aClass == null && spellBook.getType() == SpellBook.TYPE_SPELL_BOOK) {
aClass = Globals.getContext().getReferenceContext().silentlyGetConstructedCDOMObject(PCClass.class, classKey);
if ((aClass == null) && (classKey.lastIndexOf('(') >= 0)) {
aClass = Globals.getContext().getReferenceContext().silentlyGetConstructedCDOMObject(PCClass.class, classKey.substring(0, classKey.lastIndexOf('(')).trim());
}
}
if (aClass == null) {
return "No class keyed " + classKey;
}
if (!aClass.getSafe(ObjectKey.MEMORIZE_SPELLS) && !bookName.equals(Globals.getDefaultSpellBook())) {
return aClass.getDisplayName() + " can only add to " + Globals.getDefaultSpellBook();
}
// don't allow adding spells which are not qualified for.
if (!aSpell.qualifies(this, aSpell)) {
return "You do not qualify for " + acs.getSpell().getDisplayName() + ".";
}
// which can be the case for some spells, then allow it.
if (spellBook.getType() != SpellBook.TYPE_SPELL_BOOK && !acs.isSpecialtySpell(this) && SpellCountCalc.isProhibited(aSpell, aClass, this)) {
return acs.getSpell().getDisplayName() + " is prohibited.";
}
// Now let's see if they should be able to add this spell
// first check for known/cast/threshold
final int known = this.getSpellSupport(aClass).getKnownForLevel(spellLevel, this);
int specialKnown = 0;
final int cast = this.getSpellSupport(aClass).getCastForLevel(adjSpellLevel, bookName, true, true, this);
SpellCountCalc.memorizedSpellForLevelBook(this, aClass, adjSpellLevel, bookName);
final boolean isDefault = bookName.equals(Globals.getDefaultSpellBook());
if (isDefault) {
specialKnown = this.getSpellSupport(aClass).getSpecialtyKnownForLevel(spellLevel, this);
}
int numPages = 0;
// sk4p 13 Dec 2002
if (spellBook.getType() == SpellBook.TYPE_SPELL_BOOK) {
// If this is a spellbook rather than known spells
// or prepared spells, then let them add spells up to
// the page limit of the book.
// Explicitly should *not* set the dirty flag to true.
spellLevelTemp = spellLevel;
/*
* TODO Need to understand more about this context of formula
* resolution (in context of a spell??) in order to understand how
* to put this method into the Formula interface
*/
numPages = getVariableValue(acs, spellBook.getPageFormula().toString(), "").intValue();
// Check number of pages remaining in the book
if (numPages + spellBook.getNumPagesUsed() > spellBook.getNumPages()) {
return "There are not enough pages left to add this spell to the spell book.";
}
spellBook.setNumPagesUsed(numPages + spellBook.getNumPagesUsed());
spellBook.setNumSpells(spellBook.getNumSpells() + 1);
} else if (!aClass.getSafe(ObjectKey.MEMORIZE_SPELLS) && !availableSpells(adjSpellLevel, aClass, bookName, true, acs.isSpecialtySpell(this))) {
String ret;
int maxAllowed;
// If this were a specialty spell, would there be room?
if (!acs.isSpecialtySpell(this) && availableSpells(adjSpellLevel, aClass, bookName, true, true)) {
ret = "Your remaining slot(s) must be filled with your speciality.";
maxAllowed = known;
} else {
ret = "You can only learn " + (known + specialKnown) + " spells for level " + adjSpellLevel + " \nand there are no higher-level slots available.";
maxAllowed = known + specialKnown;
}
int memTot = SpellCountCalc.memorizedSpellForLevelBook(this, aClass, adjSpellLevel, bookName);
int spellDifference = maxAllowed - memTot;
if (spellDifference > 0) {
ret += "\n" + spellDifference + " spells from lower levels are using slots for this level.";
}
return ret;
} else if (aClass.getSafe(ObjectKey.MEMORIZE_SPELLS) && !isDefault && !availableSpells(adjSpellLevel, aClass, bookName, false, acs.isSpecialtySpell(this))) {
String ret;
int maxAllowed;
if (!acs.isSpecialtySpell(this) && availableSpells(adjSpellLevel, aClass, bookName, false, true)) {
ret = "Your remaining slot(s) must be filled with your speciality or domain.";
maxAllowed = this.getSpellSupport(aClass).getCastForLevel(adjSpellLevel, bookName, false, true, this);
} else if (acs.isSpecialtySpell(this) && availableSpells(adjSpellLevel, aClass, bookName, false, false)) {
ret = "Your remaining slot(s) must be filled with spells not from your speciality or domain.";
maxAllowed = this.getSpellSupport(aClass).getCastForLevel(adjSpellLevel, bookName, false, true, this);
} else {
ret = "You can only prepare " + cast + " spells for level " + adjSpellLevel + " \nand there are no higher-level slots available.";
maxAllowed = cast;
int memTot = SpellCountCalc.memorizedSpellForLevelBook(this, aClass, adjSpellLevel, bookName);
int spellDifference = maxAllowed - memTot;
if (spellDifference > 0) {
ret += "\n" + spellDifference + " spells from lower levels are using slots for this level.";
}
}
return ret;
}
// determine if this spell already exists
// for this character in this book at this level
SpellInfo si = null;
final List<CharacterSpell> acsList = getCharacterSpells(aClass, acs.getSpell(), bookName, adjSpellLevel);
if (!acsList.isEmpty()) {
for (int x = acsList.size() - 1; x >= 0; x--) {
final CharacterSpell c = acsList.get(x);
if (!c.equals(acs)) {
acsList.remove(x);
}
}
}
final boolean isEmpty = acsList.isEmpty();
if (!isEmpty) {
// use the passed in spell.
if (acsList.size() == 1) {
final CharacterSpell tcs = acsList.get(0);
si = tcs.getSpellInfoFor(bookName, adjSpellLevel, aFeatList);
} else {
si = acs.getSpellInfoFor(bookName, adjSpellLevel, aFeatList);
}
}
if (si != null) {
// otherwise increment the number of times memorised
if (isDefault) {
return "The Known Spells spellbook contains all spells of this level that you know. You cannot place spells in multiple times.";
}
si.setTimes(si.getTimes() + 1);
} else {
if (isEmpty && !containsCharacterSpell(aClass, acs)) {
addCharacterSpell(aClass, acs);
} else if (isEmpty) {
// Make sure that we are working on the same spell object, not just the same spell
for (CharacterSpell characterSpell : getCharacterSpells(aClass)) {
if (characterSpell.equals(acs)) {
acs = characterSpell;
}
}
}
si = acs.addInfo(spellLevel, adjSpellLevel, 1, bookName, aFeatList);
}
// Set number of pages on the spell
si.setNumPages(si.getNumPages() + numPages);
setDirty(true);
return "";
}
use of pcgen.core.character.SpellBook in project pcgen by PCGen.
the class SpellSupportFacadeImpl method setDefaultSpellBook.
/**
* Set the spell book to hold any new known spells.
* @param bookName The name of the new default spell book.
*/
@Override
public void setDefaultSpellBook(String bookName) {
SpellBook book = charDisplay.getSpellBookByName(bookName);
if (book == null || book.getType() != SpellBook.TYPE_SPELL_BOOK) {
return;
}
pc.setSpellBookNameToAutoAddKnown(bookName);
defaultSpellBook.set(bookName);
}
use of pcgen.core.character.SpellBook in project pcgen by PCGen.
the class PCGVer2Parser method parseSpellLine.
/*
* ###############################################################
* Character Spells Information methods
* ###############################################################
*/
private void parseSpellLine(final String line) {
final PCGTokenizer tokens;
try {
tokens = new PCGTokenizer(line);
} catch (PCGParseException pcgpex) {
final String message = "Illegal Spell line ignored: " + line + Constants.LINE_SEPARATOR + "Error: " + pcgpex.getMessage();
warnings.add(message);
return;
}
Spell aSpell = null;
PCClass aPCClass = null;
PObject source = null;
String spellBook = null;
int times = 1;
int spellLevel = 0;
int numPages = 0;
final List<Ability> metaFeats = new ArrayList<>();
int ppCost = -1;
for (final PCGElement element : tokens.getElements()) {
final String tag = element.getName();
if (IOConstants.TAG_SPELLNAME.equals(tag)) {
String spellName = EntityEncoder.decode(element.getText());
spellName = SpellMigration.getNewSpellKey(spellName, pcgenVersion, SettingsHandler.getGame().getName());
// either NULL (no spell) a Spell instance,
aSpell = Globals.getContext().getReferenceContext().silentlyGetConstructedCDOMObject(Spell.class, spellName);
if (aSpell == null) {
final String message = "Could not find spell named: " + spellName;
warnings.add(message);
return;
}
} else if (IOConstants.TAG_TIMES.equals(tag)) {
try {
times = Integer.parseInt(element.getText());
} catch (NumberFormatException nfe) {
// nothing we can do about it
}
} else if (IOConstants.TAG_CLASS.equals(tag)) {
final String classKey = EntityEncoder.decode(element.getText());
aPCClass = thePC.getClassKeyed(classKey);
if (aPCClass == null) {
final String message = "Invalid class specification: " + classKey;
warnings.add(message);
return;
}
} else if (IOConstants.TAG_SPELL_BOOK.equals(tag)) {
spellBook = EntityEncoder.decode(element.getText());
} else if (IOConstants.TAG_SPELLLEVEL.equals(tag)) {
try {
spellLevel = Integer.parseInt(element.getText());
} catch (NumberFormatException nfe) {
// nothing we can do about it
}
} else if (IOConstants.TAG_SPELLPPCOST.equals(tag)) {
try {
ppCost = Integer.parseInt(element.getText());
} catch (NumberFormatException nfe) {
// nothing we can do about it
}
} else if (IOConstants.TAG_SPELLNUMPAGES.equals(tag)) {
try {
numPages = Integer.parseInt(element.getText());
} catch (NumberFormatException nfe) {
// nothing we can do about it
}
} else if (IOConstants.TAG_SOURCE.equals(tag)) {
String typeName = Constants.EMPTY_STRING;
String objectKey = Constants.EMPTY_STRING;
for (final PCGElement child : element.getChildren()) {
final String childTag = child.getName();
if (IOConstants.TAG_TYPE.equals(childTag)) {
typeName = child.getText().toUpperCase();
} else if (IOConstants.TAG_NAME.equals(childTag)) {
objectKey = child.getText();
}
}
if (IOConstants.TAG_DOMAIN.equals(typeName)) {
Domain domain = Globals.getContext().getReferenceContext().silentlyGetConstructedCDOMObject(DOMAIN_CLASS, objectKey);
ClassSource cs = thePC.getDomainSource(domain);
if (cs == null) {
final String message = "Could not find domain: " + objectKey;
warnings.add(message);
return;
}
source = domain;
} else {
// it's either the class, sub-class or a cast-as class
// first see if it's the class
ClassSpellList csl = Globals.getContext().getReferenceContext().silentlyGetConstructedCDOMObject(ClassSpellList.class, objectKey);
if (((aPCClass != null) && objectKey.equals(aPCClass.getKeyName())) || (aPCClass != null && thePC.getSpellLists(aPCClass).contains(csl))) {
source = aPCClass;
} else {
// see if PC has the class
source = thePC.getClassKeyed(objectKey);
}
}
} else if (IOConstants.TAG_FEATLIST.equals(tag)) {
for (PCGElement child : element.getChildren()) {
final String featKey = EntityEncoder.decode(child.getText());
final Ability anAbility = Globals.getContext().getReferenceContext().silentlyGetConstructedCDOMObject(Ability.class, AbilityCategory.FEAT, featKey);
if (anAbility != null) {
metaFeats.add(anAbility);
}
}
}
}
if ((aPCClass == null) || (spellBook == null)) {
final String message = "Illegal Spell line ignored: " + line;
warnings.add(message);
return;
}
/*
* this can only happen if the source type was NOT DOMAIN!
*/
if (source == null) {
source = aPCClass;
}
// if (obj instanceof List)
// {
// // find the instance of Spell in this class
// // best suited to this spell
// for (final Spell spell : (ArrayList<Spell>) obj)
// {
// // valid spell has a non-negative spell level
// if ((spell != null)
// && (SpellLevel.getFirstLevelForKey(spell,
// thePC.getSpellLists(source), thePC) >= 0))
// {
// aSpell = spell;
// break;
// }
// }
// if (aSpell == null)
// {
// Logging.errorPrint("Could not resolve spell " + obj.toString());
// }
// }
// if (aSpell == null)
// {
// final String message =
// "Could not find spell named: " + String.valueOf(obj);
// warnings.add(message);
//
// return;
// }
// just to make sure the spellbook is present
thePC.addSpellBook(spellBook);
final SpellBook book = thePC.getSpellBookByName(spellBook);
thePC.calculateKnownSpellsForClassLevel(aPCClass);
final Integer[] spellLevels = SpellLevel.levelForKey(aSpell, thePC.getSpellLists(source), thePC);
boolean found = false;
for (int sindex = 0; sindex < spellLevels.length; ++sindex) {
final int level = spellLevels[sindex];
final int metmagicLevels = totalAddedLevelsFromMetamagic(metaFeats);
if (spellLevel > 0 && spellLevel != (level + metmagicLevels)) {
// Skip spell in class lists that does not match level the character knows it.
continue;
}
if (level < 0) {
Collection<CDOMReference<Spell>> mods = source.getListMods(Spell.SPELLS);
if (mods == null) {
continue;
}
for (CDOMReference<Spell> ref : mods) {
Collection<Spell> refSpells = ref.getContainedObjects();
Collection<AssociatedPrereqObject> assocs = source.getListAssociations(Spell.SPELLS, ref);
for (Spell sp : refSpells) {
if (aSpell.getKeyName().equals(sp.getKeyName())) {
for (AssociatedPrereqObject apo : assocs) {
String sb = apo.getAssociation(AssociationKey.SPELLBOOK);
if (spellBook.equals(sb)) {
found = true;
break;
}
}
}
}
}
continue;
}
found = true;
// do not load auto knownspells into default spellbook
if (spellBook.equals(Globals.getDefaultSpellBook()) && thePC.getSpellSupport(aPCClass).isAutoKnownSpell(aSpell, level, false, thePC) && thePC.getAutoSpells()) {
continue;
}
CharacterSpell aCharacterSpell = thePC.getCharacterSpellForSpell(aPCClass, aSpell, source);
// so we'll need to add it to the list
if (aCharacterSpell == null) {
aCharacterSpell = new CharacterSpell(source, aSpell);
aCharacterSpell.addInfo(level, times, spellBook);
thePC.addCharacterSpell(aPCClass, aCharacterSpell);
}
SpellInfo aSpellInfo = null;
if (source.getKeyName().equals(aPCClass.getKeyName()) || !spellBook.equals(Globals.getDefaultSpellBook())) {
aSpellInfo = aCharacterSpell.getSpellInfoFor(spellBook, spellLevel);
// metaFeats list have to do with this?
if ((aSpellInfo == null) || !metaFeats.isEmpty()) {
aSpellInfo = aCharacterSpell.addInfo(spellLevel, times, spellBook);
}
}
if (aSpellInfo != null) {
if (!metaFeats.isEmpty()) {
aSpellInfo.addFeatsToList(metaFeats);
}
aSpellInfo.setActualPPCost(ppCost);
aSpellInfo.setNumPages(numPages);
book.setNumPagesUsed(book.getNumPagesUsed() + numPages);
book.setNumSpells(book.getNumSpells() + 1);
}
}
if (!found) {
final String message = "Could not find spell " + aSpell.getDisplayName() + " in " + shortClassName(source) + " " + source.getDisplayName();
warnings.add(message);
}
}
use of pcgen.core.character.SpellBook in project pcgen by PCGen.
the class SpellSupportFacadeImpl method buildKnownPreparedNodes.
/**
* Construct the list of spells the character knows, has prepared or has in
* a spell book.
*/
private void buildKnownPreparedNodes() {
allKnownSpellNodes.clearContents();
knownSpellNodes.clearContents();
bookSpellNodes.clearContents();
preparedSpellNodes.clearContents();
// Ensure spell information is up to date
pc.getSpellList();
// Scan character classes for spell classes
List<PCClass> classList = getCharactersSpellcastingClasses();
List<PObject> pobjList = new ArrayList<>(classList);
// Include spells from race etc
pobjList.add(charDisplay.getRace());
// Look at each spell on each spellcasting class
for (PObject pcClass : pobjList) {
buildKnownPreparedSpellsForCDOMObject(pcClass);
}
spellBooks.clear();
spellBookNames.clearContents();
for (SpellBook spellBook : charDisplay.getSpellBooks()) {
if (spellBook.getType() == SpellBook.TYPE_PREPARED_LIST) {
DummySpellNodeImpl spellListNode = new DummySpellNodeImpl(getRootNode(spellBook.getName()));
preparedSpellLists.add(spellListNode);
addDummyNodeIfSpellListEmpty(spellBook.getName());
} else if (spellBook.getType() == SpellBook.TYPE_SPELL_BOOK) {
DummySpellNodeImpl spellListNode = new DummySpellNodeImpl(getRootNode(spellBook.getName()));
spellBooks.add(spellListNode);
addDummyNodeIfSpellBookEmpty(spellBook.getName());
spellBookNames.addElement(spellBook.getName());
}
}
}
use of pcgen.core.character.SpellBook in project pcgen by PCGen.
the class ActiveSpellsFacet method process.
/**
* Currently used as a global reset for the spell list, since
* ActiveSpellsFacet does not currently listen to all scenarios which can
* alter Spells granted to a Player Character.
*
* Use of this method outside this facet is discouraged, as the long term
* goal is to get all of the processing for Spells into this Facet.
* Therefore, use of this global reset indicates incomplete implementation
* of Spells processing in this facet, and should be an indication that
* additional work is required in order to enhance the capability of this
* facet to appropriately update the Spells for a Player Character.
*
* @param id
* The CharID identifying the Player Character that requires a
* reset on the list of spells granted to the Player Character.
*/
public void process(CharID id) {
Race race = raceFacet.get(id);
removeAll(id, race);
PlayerCharacter pc = trackingFacet.getPC(id);
for (SpellLikeAbility sla : spellsFacet.getQualifiedSet(id)) {
Formula times = sla.getCastTimes();
int resolvedTimes = formulaResolvingFacet.resolve(id, times, sla.getQualifiedKey()).intValue();
String book = sla.getSpellBook();
final CharacterSpell cs = new CharacterSpell(race, sla.getSpell());
cs.setFixedCasterLevel(sla.getFixedCasterLevel());
SpellInfo si = cs.addInfo(0, resolvedTimes, book);
si.setTimeUnit(sla.getCastTimeUnit());
si.setFixedDC(sla.getDC());
pc.addSpellBook(new SpellBook(book, SpellBook.TYPE_INNATE_SPELLS));
add(id, cs, race);
}
}
Aggregations