/*
* NPCGenerator.java
* Copyright 2006 (C) Aaron Divinsky <boomer70@yahoo.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Current Ver: $Revision$
*/
package pcgen.core.npcgen;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import pcgen.base.util.RandomUtil;
import pcgen.base.util.WeightedCollection;
import pcgen.cdom.base.Constants;
import pcgen.cdom.content.CNAbilityFactory;
import pcgen.cdom.content.RollMethod;
import pcgen.cdom.enumeration.FactKey;
import pcgen.cdom.enumeration.Gender;
import pcgen.cdom.enumeration.ListKey;
import pcgen.cdom.enumeration.Nature;
import pcgen.cdom.enumeration.ObjectKey;
import pcgen.cdom.helper.ClassSource;
import pcgen.core.Ability;
import pcgen.core.AbilityCategory;
import pcgen.core.AbilityUtilities;
import pcgen.core.Deity;
import pcgen.core.Domain;
import pcgen.core.GameMode;
import pcgen.core.Globals;
import pcgen.core.PCAlignment;
import pcgen.cdom.enumeration.PCAttribute;
import pcgen.core.PCClass;
import pcgen.core.PCStat;
import pcgen.core.PlayerCharacter;
import pcgen.core.Race;
import pcgen.core.RuleConstants;
import pcgen.core.SettingsHandler;
import pcgen.core.Skill;
import pcgen.core.SystemCollections;
import pcgen.core.analysis.DomainApplication;
import pcgen.core.analysis.SkillRankControl;
import pcgen.core.analysis.SubClassApplication;
import pcgen.core.character.CharacterSpell;
import pcgen.core.pclevelinfo.PCLevelInfo;
import pcgen.core.spell.Spell;
import pcgen.gui2.UIPropertyContext;
import pcgen.util.Logging;
import pcgen.util.chooser.ChooserFactory;
import pcgen.util.enumeration.Visibility;
/**
* This class implements the NPC generator. It is a singleton object and
* therefore should not be created locally.
*
* @author boomer70
*
*/
public final class NPCGenerator
{
private static final NPCGenerator theInstance = new NPCGenerator();
private Configuration theConfiguration = null;
// Rule options
private int theSubSkillWeightAdd = 10;
private NPCGenerator()
{
// Private so this can't be constructed.
}
/**
* Gets the generator instance.
*
* @return The <tt>NPCGenerator</tt> instance.
*/
public static NPCGenerator getInst()
{
theInstance.setConfiguration(SettingsHandler.getGame());
return theInstance;
}
private void setConfiguration( final GameMode aGameMode )
{
theConfiguration = Configuration.get(aGameMode);
}
public static int getSubSkillWeightAdd()
{
return getInst().theSubSkillWeightAdd;
}
/**
* Returns the options for alignment.
*
* @return A <tt>List</tt> of AlignGeneratorOption
*/
public List<AlignGeneratorOption> getAlignmentOptions()
{
return theConfiguration.getAlignmentOptions();
}
/**
* Returns the options for races.
*
* @return A <tt>List</tt> of RaceGeneratorOption
*/
public List<RaceGeneratorOption> getCustomRaceOptions()
{
return theConfiguration.getRaceOptions();
}
/**
* Returns the options for genders.
*
* @return A <tt>List</tt> of GenderGeneratorOption
*/
public List<GenderGeneratorOption> getCustomGenderOptions()
{
return theConfiguration.getGenderOptions();
}
/**
* Returns the options for classes.
*
* @return A <tt>List</tt> of ClassGeneratorOption
*/
public List<ClassGeneratorOption> getCustomClassOptions()
{
return theConfiguration.getClassOptions();
}
/**
* Returns the options for levels.
*
* @return A <tt>List</tt> of LevelGeneratorOption
*/
public List<LevelGeneratorOption> getCustomLevelOptions()
{
return theConfiguration.getLevelOptions();
}
private WeightedCollection<SkillChoice> getSkillWeights(final PCClass aClass,
final PlayerCharacter aPC)
{
WeightedCollection<SkillChoice> WeightedCollection = theConfiguration.getSkillWeights(aClass.getKeyName());
if (WeightedCollection == null)
{
WeightedCollection = new WeightedCollection<>();
// User has not specified a weighting for skills for this class
// Assume class skills are picked uniformly and cross-class skills
// are 1/8 as likely to be selected.
for ( Skill skill : Globals.getContext().getReferenceContext().getConstructedCDOMObjects(Skill.class) )
{
if ( skill.getSafe(ObjectKey.VISIBILITY) == Visibility.DEFAULT )
{
if (aPC.isClassSkill(aClass, skill))
{
WeightedCollection.add(new SkillChoice(skill.getKeyName()), 8);
}
else if (!skill.getSafe(ObjectKey.EXCLUSIVE))
{
WeightedCollection.add(new SkillChoice(skill.getKeyName()), 1);
}
}
}
}
return WeightedCollection;
}
private void selectSkills(final PlayerCharacter aPC, final WeightedCollection<SkillChoice> skillList,
final PCClass aClass, final int level)
{
// Select a potential skill
PCLevelInfo levelInfo = null;
int curLevel = 0;
for ( PCLevelInfo li : aPC.getLevelInfo() )
{
if (li.getClassKeyName().equals(aClass.getKeyName()))
{
curLevel++;
}
if (curLevel == level)
{
levelInfo = li;
break;
}
}
if (levelInfo == null)
{
return;
}
int skillPts = levelInfo.getSkillPointsRemaining();
Logging.debugPrint( "NPCGenerator: Selecting " + skillPts + " skill points for " + aClass + "/" + level ); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
Logging.debugPrint( "NPCGenerator: Initial skillList is " + skillList ); //$NON-NLS-1$
while (skillPts > 0)
{
final SkillChoice choice = skillList.getRandomValue();
final Skill skill = choice.getSkill();
Logging.debugPrint( "NPCGenerator: Selected " + skill ); //$NON-NLS-1$
if (skill == null)
{
Logging.debugPrint("NPCGenerator: Skill not found"); //$NON-NLS-1$
continue;
}
final int cost = aPC.getSkillCostForClass(skill, aClass).getCost();
double ranks = 1.0 / cost;
Logging.debugPrint( "NPCGenerator: Adding " + (int)ranks + "ranks" ); //$NON-NLS-1$ //$NON-NLS-2$
if (!Globals.checkRule(RuleConstants.SKILLMAX))
{
// If we are not told to ignore rank maxes we need to make sure
// we can add this rank to this skill.
double maxRanks = aPC.getMaxRank(skill, aClass).
doubleValue();
double pcRanks = aPC.getRank(skill).doubleValue();
if (pcRanks + ranks > maxRanks)
{
Logging.debugPrint("NPCGenerator: Skill already at max."); //$NON-NLS-1$
// Check that there are some skills we can advance in
boolean ranksLeft = false;
for (SkillChoice skillChoice : skillList)
{
Skill chkSkill = skillChoice.getSkill();
if (chkSkill != null)
{
if (aPC.getRank(chkSkill).doubleValue() < aPC.getMaxRank(chkSkill, aClass).
doubleValue())
{
ranksLeft = true;
break;
}
}
}
if (!ranksLeft)
{
Logging.errorPrint("Unable to spend all skill points.");
break;
}
continue;
}
}
SkillRankControl.modRanks(ranks, aClass, false, aPC, skill);
// Add weight to skills we select to try and encourage us to select
// them again.
skillList.add(choice, 4/cost);
skillPts--;
levelInfo.setSkillPointsRemaining(skillPts);
Logging.debugPrint( "NPCGenerator: Skill list now " + skillList ); //$NON-NLS-1$
}
}
private PCAlignment getAlignment(final AlignGeneratorOption option)
{
if (option == null)
{
return null;
}
return option.getList().getRandomValue();
}
private Race getRace(final RaceGeneratorOption option)
{
return option.getList().getRandomValue();
}
private Gender getGender(final GenderGeneratorOption option)
{
return option.getList().getRandomValue();
}
private PCClass getClass(final ClassGeneratorOption option)
{
return option.getList().getRandomValue();
}
private int getLevel(final LevelGeneratorOption option)
{
return option.getList().getRandomValue();
}
private List<PCStat> getStatWeights(PlayerCharacter pc, final PCClass aClass)
{
final WeightedCollection<PCStat> stats = new WeightedCollection<>(
theConfiguration.getStatWeights(aClass.getKeyName()));
final List<PCStat> ret = new ArrayList<>();
for (int i = 0; i < pc.getDisplay().getStatCount(); i++)
{
final PCStat stat = stats.getRandomValue();
ret.add(stat);
stats.remove(stat);
}
return ret;
}
private void generateStats(final PlayerCharacter aPC, final PCClass aClass, final RollMethod aRollMethod)
{
final List<PCStat> statOrder = getStatWeights(aPC, aClass);
Logging.debugPrint( "NPCGenerator: Stat order is " + statOrder ); //$NON-NLS-1$
aPC.rollStats(Constants.CHARACTER_STAT_METHOD_ROLLED, statOrder, aRollMethod, true);
for (PCStat stat : aPC.getStatSet())
{
Logging.debugPrint( "NPCGenerator: Setting stat " + stat.getKeyName()
+ " to " + aPC.getStat(stat) ); //$NON-NLS-1$//$NON-NLS-2$
aPC.setStat(stat, aPC.getStat(stat));
}
}
private WeightedCollection<Ability> getFeatWeights(final PCClass aClass)
{
WeightedCollection<Ability> weightedCollection =
theConfiguration.getAbilityWeights(aClass.getKeyName(), AbilityCategory.FEAT);
if (weightedCollection == null)
{
weightedCollection = new WeightedCollection<>();
// User has not specified a weighting for feats for this class
// Assume General feats are 5 times as likely to be selected as
// any other type
for (Ability ability : Globals.getContext().getReferenceContext().getManufacturer(
Ability.class, AbilityCategory.FEAT).getAllObjects())
{
int weight = 1;
if (ability.getSafe(ObjectKey.VISIBILITY) != Visibility.DEFAULT)
{
continue;
}
if (ability.isType("GENERAL")) //$NON-NLS-1$
{
weight = 5;
}
weightedCollection.add(ability, weight);
}
}
return weightedCollection;
}
private void selectFeats(final PlayerCharacter aPC, final WeightedCollection<Ability> aFeatList)
{
while ((int)aPC.getRemainingFeatPoolPoints() > 0)
{
final Ability ability = aFeatList.getRandomValue();
if (!ability.qualifies(aPC, ability))
{
// We will leave the feat because we may qualify later.
continue;
}
AbilityUtilities.driveChooseAndAdd(CNAbilityFactory.getCNAbility(AbilityCategory.FEAT, Nature.NORMAL, ability), aPC, true);
}
}
private void selectDeity( final PlayerCharacter aPC, final PCClass aClass )
{
// Copy the list since we may modify it
final WeightedCollection<Deity> deities = new WeightedCollection<>(theConfiguration.getDeityWeights(aClass.getKeyName()));
boolean selected = false;
while (!deities.isEmpty())
{
final Deity deity = deities.getRandomValue();
if ( aPC.canSelectDeity(deity))
{
aPC.setDeity(deity);
selected = true;
break;
}
deities.remove(deity);
}
if (!selected )
{
Logging.errorPrintLocalised("NPCGen.Errors.CantSelectDeity"); //$NON-NLS-1$
}
}
private void selectDomains( final PlayerCharacter aPC, final PCClass aClass )
{
final WeightedCollection<Domain> domains = theConfiguration.getDomainWeights(aPC.getDeity().getKeyName(), aClass.getKeyName());
for (Iterator<Domain> iterator = domains.iterator(); iterator.hasNext();)
{
Domain domain = iterator.next();
if (!domain.qualifies(aPC, domain))
{
iterator.remove();
}
}
if (domains.isEmpty())
{
return;
}
while (aPC.getDomainCount() < aPC.getMaxCharacterDomains())
{
final Domain domain = domains.getRandomValue();
//Can't add twice, so have to select another...
if (aPC.hasDomain(domain))
{
continue;
}
// space remains for another domain, so add it
aPC.addDomain(domain, new ClassSource(aClass));
DomainApplication.applyDomain(aPC, domain);
aPC.calcActiveBonuses();
}
}
private WeightedCollection<Spell> getKnownSpellWeights(PlayerCharacter pc, final PCClass aClass, final int aLevel )
{
WeightedCollection<Spell> WeightedCollection = theConfiguration.getKnownSpellWeights(pc, aClass.getKeyName(), aLevel);
if (WeightedCollection == null)
{
WeightedCollection = new WeightedCollection<>();
for (final Spell spell : pc.getSpellsIn(aClass.get(ObjectKey.CLASS_SPELLLIST),
aLevel))
{
WeightedCollection.add(spell, 1);
}
}
return WeightedCollection;
}
private WeightedCollection<Spell> getPreparedSpellWeights(PlayerCharacter pc, final PCClass aClass, final int aLevel )
{
WeightedCollection<Spell> WeightedCollection = theConfiguration.getPreparedSpellWeights(aClass.getKeyName(), aLevel, pc);
if (WeightedCollection == null)
{
WeightedCollection = new WeightedCollection<>();
for (final Spell spell : pc.getSpellsIn(aClass.get(ObjectKey.CLASS_SPELLLIST),
aLevel))
{
WeightedCollection.add(spell, 1);
}
}
return WeightedCollection;
}
private void selectDomainSpell( final PlayerCharacter aPC, final PCClass aClass, final int aLevel )
{
if (!aPC.hasDomains())
{
return;
}
final WeightedCollection<Domain> domains = new WeightedCollection<>();
for (Domain d : aPC.getDomainSet())
{
// if any domains have this class as a source
// and is a valid domain, add them
if (aClass.equals(aPC.getDomainSource(d).getPcclass()))
{
domains.add(d);
}
}
final Domain domain = domains.getRandomValue();
final WeightedCollection<Spell> domainSpells =
new WeightedCollection<>(aPC.getSpellsIn(domain.get(ObjectKey.DOMAIN_SPELLLIST),
aLevel));
selectSpell( aPC, aClass, domain, "Prepared Spells", domainSpells, aLevel ); //$NON-NLS-1$
}
private void selectSpell( final PlayerCharacter aPC, final PCClass aClass, final Domain aDomain, final String aBookName, final WeightedCollection<Spell> aSpellList, final int aLevel )
{
boolean added = false;
while ( !added )
{
final Spell spell = aSpellList.getRandomValue();
// TODO - How do I check if this spell is prohibiited?
final CharacterSpell cs;
if ( aDomain != null )
{
cs = new CharacterSpell( aDomain, spell );
}
else
{
cs = new CharacterSpell( aClass, spell );
}
final String aString = aPC.addSpell(cs, new ArrayList<>(), aClass.getKeyName(),
aBookName, aLevel, aLevel);
if (!aString.isEmpty())
{
Logging.debugPrint("Add spell failed: " + aString); //$NON-NLS-1$
}
else
{
added = true;
}
}
}
private void selectSubClass( final PlayerCharacter aPC, final PCClass aClass )
{
WeightedCollection<String> subClasses = theConfiguration.getSubClassWeights( aClass.getKeyName() );
if (subClasses != null && !subClasses.isEmpty())
{
SubClassApplication.setSubClassKey(aPC, aClass, subClasses
.getRandomValue());
}
}
/**
* Generate a new NPC
*
* @param aPC The PlayerCharacter to fill in options for
* @param align Alignment options to choose from
* @param aRace Race options to choose from
* @param aGender Gender options to choose from
* @param classList <tt>List</tt> of class options to choose from
* @param levels <tt>List</tt> of level choices
* @param aRollMethod the RollMethod to use for stats
*/
public void generate( final PlayerCharacter aPC,
final AlignGeneratorOption align,
final RaceGeneratorOption aRace,
final GenderGeneratorOption aGender,
final List<ClassGeneratorOption> classList,
final List<LevelGeneratorOption> levels,
final RollMethod aRollMethod)
{
// Force a more quiet process
ChooserFactory.pushChooserClassname(
"pcgen.util.chooser.RandomChooser"); //$NON-NLS-1$
boolean tempShowHP = SettingsHandler.getShowHPDialogAtLevelUp();
SettingsHandler.setShowHPDialogAtLevelUp(false);
int tempChoicePref = UIPropertyContext.getSingleChoiceAction();
UIPropertyContext.setSingleChoiceAction(Constants.
CHOOSER_SINGLE_CHOICE_METHOD_SELECT_EXIT);
try
{
final int MAX_RETRIES = 5;
for ( int i = 0; i < MAX_RETRIES; i++ )
{
PCAlignment randAlign = getAlignment( align );
if (randAlign != null)
{
Logging
.debugPrint("NPCGenerator: Selected " + randAlign + " for alignment " + align); //$NON-NLS-1$//$NON-NLS-2$
aPC.setAlignment(randAlign);
}
final Race r = getRace(aRace);
if (r == null)
{
Logging.debugPrint( "NPCGenerator: Got null race. Retrying." ); //$NON-NLS-1$
continue;
}
Logging.debugPrint( "NPCGenerator: Selected " + r + " for race " + aRace ); //$NON-NLS-1$ //$NON-NLS-2$
if (r.qualifies(aPC, r))
{
Logging.debugPrint( "NPCGenerator: PC qualifies for race " + r ); //$NON-NLS-1$
aPC.setRace(r);
break;
}
}
if ( aPC.getRace() == Globals.s_EMPTYRACE )
{
Logging.errorPrint("Unable to select race");
return;
}
final Gender gender = getGender( aGender );
Logging.debugPrint( "NPCGenerator: Selecting " + gender + " for gender " + aGender ); //$NON-NLS-1$ //$NON-NLS-2$
aPC.setGender(gender);
boolean doneRacialClasses = false;
for (int i = 0; i < classList.size(); i++)
{
int numLevels = getLevel(levels.get(i));
Logging.debugPrint( "NPCGenerator: Selecting " + numLevels + " for level " + levels.get(i) ); //$NON-NLS-1$ //$NON-NLS-2$
PCClass aClass = null;
if ( !doneRacialClasses && aPC.hasClass())
{
aClass = aPC.getClassList().get(0);
numLevels = aPC.getLevel(aClass);
doneRacialClasses = true;
i--;
}
else
{
doneRacialClasses = true;
for ( ; ; )
{
aClass = getClass(classList.get(i));
if (aClass == null)
{
break;
}
if (aClass.getSafe(ObjectKey.VISIBILITY).equals(Visibility.DEFAULT)
&& aClass.qualifies(aPC, aClass))
{
Logging.debugPrint( "NPCGenerator: Selecting " + aClass + " for class " + classList.get(i) ); //$NON-NLS-1$ //$NON-NLS-2$
break;
}
// TODO Remove a failed class from the list.
Logging.errorPrint("Counld not add a level of " + aClass);
aClass = null;
break;
}
}
if (aClass == null)
{
continue;
}
final PCClass classCopy = aClass.clone();
if ( classCopy.containsListFor(ListKey.SUB_CLASS) )
{
selectSubClass(aPC, classCopy);
}
if (i == 0)
{
generateStats(aPC, classCopy, aRollMethod);
selectDeity(aPC, classCopy);
}
int highestSpellLevel = aPC.getSpellSupport(aClass).getHighestLevelSpell(aPC);
final int[] selectedSpells = new int[highestSpellLevel + 1];
for ( int k = 0; k < highestSpellLevel; k++ ) { selectedSpells[k] = 0; }
final int[] bonusSpells = new int[highestSpellLevel + 1];
for ( int k = 0; k < highestSpellLevel; k++ ) { bonusSpells[k] = 0; }
// Make a copy of the list because we are going to modify it.
WeightedCollection<SkillChoice> skillList = new WeightedCollection<>(getSkillWeights(classCopy, aPC));
WeightedCollection<Ability> featList = new WeightedCollection<>(getFeatWeights(classCopy));
for (int j = 0; j < numLevels; j++)
{
if ( i >= 0 )
{
aPC.incrementClassLevel(1, classCopy, true);
}
final PCClass pcClass = aPC.getClassKeyed(classCopy.getKeyName());
selectSkills(aPC, skillList, pcClass, j + 1);
selectFeats(aPC, featList);
selectDomains( aPC, pcClass );
if (pcClass.get(FactKey.valueOf("SpellType")) != null)
{
// This is a spellcasting class. We may have to select
// spells of some sort (known or prepared).
if ( aPC.getSpellSupport(pcClass).hasKnownList() || aPC.getSpellSupport(pcClass).hasKnownSpells(aPC) )
{
Logging.debugPrint("NPCGenerator: known spells to select"); //$NON-NLS-1$
for (int lvl = 0; lvl <= highestSpellLevel; ++lvl)
{
if (aPC.availableSpells(lvl, pcClass, Globals.getDefaultSpellBook(), true, true))
{
final int a = aPC.getSpellSupport(pcClass).getKnownForLevel(lvl, aPC);
//final int bonus = aPC.getSpellSupport(pcClass).getSpecialtyKnownForLevel(lvl, aPC);
Logging.debugPrint("NPCGenerator: " + a + "known spells to select"); //$NON-NLS-1$ //$NON-NLS-2$
final WeightedCollection<Spell> spellChoices = getKnownSpellWeights(aPC, pcClass, lvl);
final int numToSelect = a - selectedSpells[lvl];
for ( int sp = 0; sp < numToSelect; sp ++ )
{
selectSpell( aPC, pcClass, null, Globals.getDefaultSpellBook(), spellChoices, lvl );
selectedSpells[lvl]++;
}
}
}
}
else
{
// Prepared spells?
Logging.debugPrint("NPCGenerator: prepared spells to select"); //$NON-NLS-1$
aPC.addSpellBook("Prepared Spells");
for (int lvl = 0; lvl <= highestSpellLevel; ++lvl)
{
final int castTot = aPC.getSpellSupport(pcClass).getCastForLevel(lvl, "Prepared Spells", true, true, aPC);
final int castNon = aPC.getSpellSupport(pcClass).getCastForLevel(lvl, "Prepared Spells", false, true, aPC);
final int castSpec = castTot - castNon;
Logging.debugPrint("NPCGenerator: " + castTot + "+" + castSpec + " prepared spells to select"); //$NON-NLS-1$ //$NON-NLS-2$
if ( castSpec - bonusSpells[lvl] > 0 )
{
selectDomainSpell( aPC, pcClass, lvl );
bonusSpells[lvl]++;
}
if (castTot > 0)
{
final WeightedCollection<Spell> spellChoices = getPreparedSpellWeights(aPC, pcClass, lvl);
final int numToSelect = castNon - selectedSpells[lvl];
for ( int sp = 0; sp < numToSelect; sp ++ )
{
selectSpell( aPC, pcClass, null, "Prepared Spells", spellChoices, lvl );
selectedSpells[lvl]++;
}
}
}
}
}
}
}
final String randBioString = "EYES.HAIR.SKIN.HT.WT.AGE."; //$NON-NLS-1$
aPC.getBioSet().randomize(randBioString, aPC);
final List<String> globalHairStyleList = SystemCollections.getUnmodifiableHairStyleList();
aPC.setPCAttribute(PCAttribute.HAIRSTYLE, globalHairStyleList.get(RandomUtil.getRandomInt(globalHairStyleList.size())));
final List<String> speechList = SystemCollections.getUnmodifiableSpeechList();
aPC.setPCAttribute(PCAttribute.SPEECHTENDENCY, speechList.get(RandomUtil.getRandomInt(speechList.size())));
final List<String> globalPhobiaList = SystemCollections.getUnmodifiablePhobiaList();
aPC.setPCAttribute(PCAttribute.PHOBIAS, globalPhobiaList.get(RandomUtil.getRandomInt(globalPhobiaList.size())));
final List<String> globalInterestsList = SystemCollections.getUnmodifiableInterestsList();
aPC.setPCAttribute(PCAttribute.INTERESTS, globalInterestsList.get(RandomUtil.getRandomInt(globalInterestsList.size())));
final List<String> globalPhraseList = SystemCollections.getUnmodifiablePhraseList();
aPC.setPCAttribute(PCAttribute.CATCHPHRASE, globalPhraseList.get(RandomUtil.getRandomInt(globalPhraseList.size())));
final List<String> globalTraitList = SystemCollections.getUnmodifiableTraitList();
// TODO: it is possible for trait1 == trait2
aPC.setPCAttribute(PCAttribute.PERSONALITY1, globalTraitList.get(RandomUtil.getRandomInt(globalTraitList.size())));
aPC.setPCAttribute(PCAttribute.PERSONALITY2, globalTraitList.get(RandomUtil.getRandomInt(globalTraitList.size())));
final List<String> globalCityList = SystemCollections.getUnmodifiableCityList();
aPC.setPCAttribute(PCAttribute.RESIDENCE, globalCityList.get(RandomUtil.getRandomInt(globalCityList.size())));
final List<String> globalLocationList = SystemCollections.getUnmodifiableLocationList();
aPC.setPCAttribute(PCAttribute.LOCATION, globalLocationList.get(RandomUtil.getRandomInt(globalLocationList.size())));
final List<String> globalBirthplaceList = SystemCollections.getUnmodifiableBirthplaceList();
aPC.setPCAttribute(PCAttribute.BIRTHPLACE, globalBirthplaceList.get(RandomUtil.getRandomInt(globalBirthplaceList.size())));
//TODO: Link in with the doomsday book name generator
// final Names nameGen = Names.getInstance();
// nameGen.init(aNameChoice, aPC);
// aPC.setName(nameGen.getRandomName());
}
catch (Exception e)
{
Logging.errorPrint("Problem generation NPC", e);
}
finally
{
SettingsHandler.setShowHPDialogAtLevelUp(tempShowHP);
UIPropertyContext.setSingleChoiceAction(tempChoicePref);
ChooserFactory.popChooserClassname();
}
}
}