use of pcgen.core.character.CharacterSpell 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.CharacterSpell in project pcgen by PCGen.
the class SpellSupportForPCClass method getCastForLevel.
public int getCastForLevel(int spellLevel, String bookName, boolean includeAdj, boolean limitByStat, PlayerCharacter aPC) {
int pcLevel = aPC.getLevel(source);
int total = 0;
int stat = 0;
final String classKeyName = "CLASS." + source.getKeyName();
final String levelSpellLevel = ";LEVEL." + spellLevel;
final String allSpellLevel = ";LEVEL.All";
pcLevel += (int) aPC.getTotalBonusTo("PCLEVEL", source.getKeyName());
pcLevel += (int) aPC.getTotalBonusTo("PCLEVEL", "TYPE." + source.getSpellType());
if (getNumFromCastList(pcLevel, spellLevel, aPC) < 0) {
// certain feats
return (int) aPC.getTotalBonusTo("SPELLCAST", classKeyName + levelSpellLevel);
}
total += (int) aPC.getTotalBonusTo("SPELLCAST", classKeyName + levelSpellLevel);
total += (int) aPC.getTotalBonusTo("SPELLCAST", "TYPE." + source.getSpellType() + levelSpellLevel);
total += (int) aPC.getTotalBonusTo("SPELLCAST", "CLASS.Any" + levelSpellLevel);
total += (int) aPC.getTotalBonusTo("SPELLCAST", classKeyName + allSpellLevel);
total += (int) aPC.getTotalBonusTo("SPELLCAST", "TYPE." + source.getSpellType() + allSpellLevel);
total += (int) aPC.getTotalBonusTo("SPELLCAST", "CLASS.Any" + allSpellLevel);
PCStat aStat = source.bonusSpellStat();
String statString = Constants.NONE;
if (aStat != null) {
stat = aPC.getTotalStatFor(aStat);
statString = aStat.getKeyName();
}
final int bonusStat = (int) aPC.getTotalBonusTo("STAT", "CAST." + statString) + (int) aPC.getTotalBonusTo("STAT", "BASESPELLSTAT") + (int) aPC.getTotalBonusTo("STAT", "BASESPELLSTAT;CLASS=" + source.getKeyName());
if (limitByStat) {
PCStat ss = source.baseSpellStat();
if (ss != null) {
final int maxSpellLevel = aPC.getVariableValue("MAXLEVELSTAT=" + ss.getKeyName(), "").intValue();
if ((maxSpellLevel + bonusStat) < spellLevel) {
return total;
}
}
}
stat += bonusStat;
// Now we decide whether to adjust the number of slots down
// the road by adding specialty slots.
// Reworked to consider the fact that a lower-level
// specialty spell can go into this level of specialty slot
//
int adj = 0;
if (includeAdj && !bookName.equals(Globals.getDefaultSpellBook()) && (aPC.hasAssocs(source, AssociationKey.SPECIALTY) || aPC.hasDomains())) {
// we might wind up using THIS level's slots for them.
for (int ix = 0; ix <= spellLevel; ++ix) {
Collection<CharacterSpell> aList = aPC.getCharacterSpells(source, ix);
Collection<Spell> bList = new ArrayList<>();
if (!aList.isEmpty()) {
// getNumFromCastList above would have returned -1
if ((ix > 0) && "DIVINE".equalsIgnoreCase(source.getSpellType())) {
for (Domain d : aPC.getDomainSet()) {
if (source.getKeyName().equals(aPC.getDomainSource(d).getPcclass().getKeyName())) {
bList = aPC.getSpellsIn(d.get(ObjectKey.DOMAIN_SPELLLIST), ix);
}
}
}
for (CharacterSpell cs : aList) {
int x = -1;
if (!bList.isEmpty()) {
if (bList.contains(cs.getSpell())) {
x = 0;
}
} else {
x = cs.getInfoIndexFor(aPC, Constants.EMPTY_STRING, ix, 1);
}
if (x > -1) {
PCClass target = source;
String subClassKey = aPC.getSubClassName(source);
if (subClassKey != null && (!subClassKey.isEmpty()) && !subClassKey.equals(Constants.NONE)) {
target = source.getSubClassKeyed(subClassKey);
}
adj = aPC.getSpellSupport(target).getSpecialtyKnownForLevel(spellLevel, aPC);
break;
}
}
}
if (adj > 0) {
break;
}
}
// end of looping up to this level looking for specialty spells that
// can be cast
}
// end of deciding whether there are specialty slots to distribute
int mult = (int) aPC.getTotalBonusTo("SPELLCASTMULT", classKeyName + levelSpellLevel);
mult += (int) aPC.getTotalBonusTo("SPELLCASTMULT", "TYPE." + source.getSpellType() + levelSpellLevel);
if (mult < 1) {
mult = 1;
}
final int t = getNumFromCastList(pcLevel, spellLevel, aPC);
total += ((t * mult) + adj);
BonusSpellInfo bsi = Globals.getContext().getReferenceContext().silentlyGetConstructedCDOMObject(BonusSpellInfo.class, String.valueOf(spellLevel));
if ((bsi != null) && bsi.isValid()) {
int base = bsi.getStatScore();
if (stat >= base) {
int range = bsi.getStatRange();
total += Math.max(0, (stat - base + range) / range);
}
}
return total;
}
use of pcgen.core.character.CharacterSpell in project pcgen by PCGen.
the class PlayerCharacter method processSpellBookRemoval.
private void processSpellBookRemoval(String aName) {
spellBookFacet.removeBookNamed(id, aName);
setDirty(true);
for (PCClass pcClass : getClassSet()) {
for (CharacterSpell cs : getCharacterSpells(pcClass, aName)) {
cs.removeSpellInfo(cs.getSpellInfoFor(aName, -1));
}
}
}
use of pcgen.core.character.CharacterSpell in project pcgen by PCGen.
the class SpellSupportFacadeImpl method buildAvailableNodes.
/**
* Construct the list of available spells for the character.
*/
private void buildAvailableNodes() {
availableSpellNodes.clearContents();
// Scan character classes for spell classes
List<PCClass> classList = getCharactersSpellcastingClasses();
// Look at each spell on each spellcasting class
for (PCClass pcClass : classList) {
DoubleKeyMapToList<SpellFacade, String, SpellNode> existingSpells = buildExistingSpellMap(availableSpellNodes, pcClass);
for (Spell spell : pc.getAllSpellsInLists(charDisplay.getSpellLists(pcClass))) {
// Create SpellNodeImpl for each spell
CharacterSpell charSpell = new CharacterSpell(pcClass, spell);
SpellFacadeImplem spellImplem = new SpellFacadeImplem(pc, spell, charSpell, null);
HashMapToList<CDOMList<Spell>, Integer> levelInfo = pc.getSpellLevelInfo(spell);
for (CDOMList<Spell> spellList : charDisplay.getSpellLists(pcClass)) {
List<Integer> levels = levelInfo.getListFor(spellList);
if (levels != null) {
for (Integer level : levels) {
SpellNodeImpl node = new SpellNodeImpl(spellImplem, pcClass, String.valueOf(level), null);
if (!existingSpells.containsInList(spellImplem, node.getSpellLevel(), node)) {
// Add to list
availableSpellNodes.addElement(node);
}
}
}
}
}
}
}
use of pcgen.core.character.CharacterSpell in project pcgen by PCGen.
the class SpellSupportFacadeImpl method buildKnownPreparedSpellsForCDOMObject.
private void buildKnownPreparedSpellsForCDOMObject(CDOMObject pObject) {
Collection<? extends CharacterSpell> sp = charDisplay.getCharacterSpells(pObject);
List<CharacterSpell> cSpells = new ArrayList<>(sp);
// Add in the spells granted by objects
pc.addBonusKnownSpellsToList(pObject, cSpells);
PCClass pcClass = (PCClass) (pObject instanceof PCClass ? pObject : null);
for (CharacterSpell charSpell : cSpells) {
for (SpellInfo spellInfo : charSpell.getInfoList()) {
// Create SpellNodeImpl for each spell
String book = spellInfo.getBook();
boolean isKnown = Globals.getDefaultSpellBook().equals(book);
SpellFacadeImplem spellImplem = new SpellFacadeImplem(pc, charSpell.getSpell(), charSpell, spellInfo);
SpellNodeImpl node;
if (pcClass != null) {
node = new SpellNodeImpl(spellImplem, pcClass, String.valueOf(spellInfo.getActualLevel()), getRootNode(book));
} else {
node = new SpellNodeImpl(spellImplem, String.valueOf(spellInfo.getActualLevel()), getRootNode(book));
}
if (spellInfo.getTimes() > 1) {
node.addCount(spellInfo.getTimes() - 1);
}
boolean isSpellBook = charDisplay.getSpellBookByName(book).getType() == SpellBook.TYPE_SPELL_BOOK;
// Add to list
if (isKnown) {
allKnownSpellNodes.addElement(node);
knownSpellNodes.addElement(node);
} else if (isSpellBook) {
bookSpellNodes.addElement(node);
} else if (pObject instanceof Race) {
allKnownSpellNodes.addElement(node);
} else {
preparedSpellNodes.addElement(node);
}
}
}
}
Aggregations