2023-08-22 10:00:21 -04:00
package prc.autodoc ;
import java.io.* ;
import java.util.* ;
import prc.autodoc.Main.SpellType ;
//import java.util.regex.*;
/* Static import in order to let me use the enum constants in switches */
import static prc.autodoc.Main.SpellType.* ;
import static prc.Main.* ;
import static prc.autodoc.Main.* ;
/ * *
* This class contains the methods for generation of the raw manual page contents .
*
* @author Ornedan
* /
public class EntryGeneration {
/ * *
* Handles creation of the skill pages .
* /
public static void doSkills ( ) {
String skillPath = contentPath + " skills " + fileSeparator ;
String name = null ,
text = null ,
path = null ,
icon = null ;
boolean errored ;
skills = new HashMap < Integer , GenericEntry > ( ) ;
Data_2da skills2da = twoDA . get ( " skills " ) ;
for ( int i = 0 ; i < skills2da . getEntryCount ( ) ; i + + ) {
errored = false ;
// Get the name of the skill and check if it's valid
name = tlk . get ( skills2da . getEntry ( " Name " , i ) ) ;
if ( verbose ) System . out . println ( " Generating entry data for " + name ) ;
if ( name . equals ( badStrRef ) ) {
err_pr . println ( " Error: Invalid name for skill " + i ) ;
errored = true ;
}
// Same for description
text = htmlizeTLK ( tlk . get ( skills2da . getEntry ( " Description " , i ) ) ) ;
// Again, check if we had a valid description
if ( tlk . get ( skills2da . getEntry ( " Description " , i ) ) . equals ( badStrRef ) ) {
err_pr . println ( " Error: Invalid description for skill " + i + " : " + name ) ;
errored = true ;
}
// And icon
icon = skills2da . getEntry ( " Icon " , i ) ;
if ( icon . equals ( " **** " ) ) {
err_pr . println ( " Error: Icon not defined for skill " + i + " : " + name ) ;
errored = true ;
}
icon = Icons . buildIcon ( icon ) ;
// Build the final path
path = skillPath + i + " .html " ;
// Check if we had any errors. If we did, and the error tolerance flag isn't up, skip generating this entry
if ( ! errored | | tolErr ) {
// Store a data structure represeting the skill into a hashmap
skills . put ( i , new GenericEntry ( name , text , icon , path , i ) ) ;
} else
err_pr . println ( " Error: Failed to generate entry for skill " + i + " : " + name ) ;
}
// Force a clean-up of dead objects. This will keep discarded objects from slowing down the program as it
// hits the memory limit
System . gc ( ) ;
}
/ * *
* Handles creation of the crafting itemprop pages .
* /
public static void doCrafting ( ) {
String craftPath = contentPath + " itemcrafting " + fileSeparator ;
String name = null ,
text = null ,
path = null ,
icon = null ;
boolean errored ;
craft_armour = new HashMap < Integer , GenericEntry > ( ) ;
craft_weapon = new HashMap < Integer , GenericEntry > ( ) ;
craft_ring = new HashMap < Integer , GenericEntry > ( ) ;
craft_wondrous = new HashMap < Integer , GenericEntry > ( ) ;
Data_2da craft_armour_2da = twoDA . get ( " craft_armour " ) ;
Data_2da craft_weapon_2da = twoDA . get ( " craft_weapon " ) ;
Data_2da craft_ring_2da = twoDA . get ( " craft_ring " ) ;
Data_2da craft_wondrous_2da = twoDA . get ( " craft_wondrous " ) ;
icon = " " ;
for ( int i = 0 ; i < craft_armour_2da . getEntryCount ( ) ; i + + ) {
errored = false ;
// Get the name of the skill and check if it's valid
name = tlk . get ( craft_armour_2da . getEntry ( " Name " , i ) ) ;
if ( verbose ) System . out . println ( " Generating entry data for " + name ) ;
if ( name . equals ( badStrRef ) ) {
err_pr . println ( " Error: Invalid name for property " + i ) ;
errored = true ;
}
// Same for description
text = htmlizeTLK ( tlk . get ( craft_armour_2da . getEntry ( " Description " , i ) ) ) ;
// Again, check if we had a valid description
if ( tlk . get ( craft_armour_2da . getEntry ( " Description " , i ) ) . equals ( badStrRef ) ) {
err_pr . println ( " Error: Invalid description for property " + i + " : " + name ) ;
errored = true ;
}
// Build the final path
path = craftPath + " armour " + i + " .html " ;
// Check if we had any errors. If we did, and the error tolerance flag isn't up, skip generating this entry
if ( ! errored | | tolErr ) {
// Store a data structure represeting the skill into a hashmap
craft_armour . put ( i , new GenericEntry ( name , text , icon , path , i ) ) ;
} else
err_pr . println ( " Error: Failed to generate entry for property " + i + " : " + name ) ;
}
for ( int i = 0 ; i < craft_weapon_2da . getEntryCount ( ) ; i + + ) {
errored = false ;
// Get the name of the skill and check if it's valid
name = tlk . get ( craft_weapon_2da . getEntry ( " Name " , i ) ) ;
if ( verbose ) System . out . println ( " Generating entry data for " + name ) ;
if ( name . equals ( badStrRef ) ) {
err_pr . println ( " Error: Invalid name for property " + i ) ;
errored = true ;
}
// Same for description
text = htmlizeTLK ( tlk . get ( craft_weapon_2da . getEntry ( " Description " , i ) ) ) ;
// Again, check if we had a valid description
if ( tlk . get ( craft_weapon_2da . getEntry ( " Description " , i ) ) . equals ( badStrRef ) ) {
err_pr . println ( " Error: Invalid description for property " + i + " : " + name ) ;
errored = true ;
}
// Build the final path
path = craftPath + " weapon " + i + " .html " ;
// Check if we had any errors. If we did, and the error tolerance flag isn't up, skip generating this entry
if ( ! errored | | tolErr ) {
// Store a data structure represeting the skill into a hashmap
craft_weapon . put ( i , new GenericEntry ( name , text , icon , path , i ) ) ;
} else
err_pr . println ( " Error: Failed to generate entry for property " + i + " : " + name ) ;
}
for ( int i = 0 ; i < craft_ring_2da . getEntryCount ( ) ; i + + ) {
errored = false ;
// Get the name of the skill and check if it's valid
name = tlk . get ( craft_ring_2da . getEntry ( " Name " , i ) ) ;
if ( verbose ) System . out . println ( " Generating entry data for " + name ) ;
if ( name . equals ( badStrRef ) ) {
err_pr . println ( " Error: Invalid name for property " + i ) ;
errored = true ;
}
// Same for description
text = htmlizeTLK ( tlk . get ( craft_ring_2da . getEntry ( " Description " , i ) ) ) ;
// Again, check if we had a valid description
if ( tlk . get ( craft_ring_2da . getEntry ( " Description " , i ) ) . equals ( badStrRef ) ) {
err_pr . println ( " Error: Invalid description for property " + i + " : " + name ) ;
errored = true ;
}
// Build the final path
path = craftPath + " ring " + i + " .html " ;
// Check if we had any errors. If we did, and the error tolerance flag isn't up, skip generating this entry
if ( ! errored | | tolErr ) {
// Store a data structure represeting the skill into a hashmap
craft_ring . put ( i , new GenericEntry ( name , text , icon , path , i ) ) ;
} else
err_pr . println ( " Error: Failed to generate entry for property " + i + " : " + name ) ;
}
for ( int i = 0 ; i < craft_wondrous_2da . getEntryCount ( ) ; i + + ) {
errored = false ;
// Get the name of the skill and check if it's valid
name = tlk . get ( craft_wondrous_2da . getEntry ( " Name " , i ) ) ;
if ( verbose ) System . out . println ( " Generating entry data for " + name ) ;
if ( name . equals ( badStrRef ) ) {
err_pr . println ( " Error: Invalid name for property " + i ) ;
errored = true ;
}
// Same for description
text = htmlizeTLK ( tlk . get ( craft_wondrous_2da . getEntry ( " Description " , i ) ) ) ;
// Again, check if we had a valid description
if ( tlk . get ( craft_wondrous_2da . getEntry ( " Description " , i ) ) . equals ( badStrRef ) ) {
err_pr . println ( " Error: Invalid description for property " + i + " : " + name ) ;
errored = true ;
}
// Build the final path
path = craftPath + " wondrous " + i + " .html " ;
// Check if we had any errors. If we did, and the error tolerance flag isn't up, skip generating this entry
if ( ! errored | | tolErr ) {
// Store a data structure represeting the skill into a hashmap
craft_wondrous . put ( i , new GenericEntry ( name , text , icon , path , i ) ) ;
} else
err_pr . println ( " Error: Failed to generate entry for property " + i + " : " + name ) ;
}
// Force a clean-up of dead objects. This will keep discarded objects from slowing down the program as it
// hits the memory limit
System . gc ( ) ;
}
/ * *
* Prints normal & epic spells and psionic powers .
* As of now , all of these are similar enough to share the same
* template , so they can be done here together .
* < p >
* The enumeration class used here is found at the end of the file
* /
public static void doSpells ( ) {
String spellPath = contentPath + " spells " + fileSeparator ,
epicPath = contentPath + " epic_spells " + fileSeparator ,
psiPath = contentPath + " psionic_powers " + fileSeparator ,
utterPath = contentPath + " utterances " + fileSeparator ,
invocPath = contentPath + " invocations " + fileSeparator ,
manPath = contentPath + " maneuvers " + fileSeparator ;
String path = null ,
name = null ,
text = null ,
icon = null ,
subradName = null ,
subradIcon = null ;
List < Tuple < String , String > > subradials = null ;
boolean errored ;
int subRadial ;
SpellType spelltype = NONE ;
spells = new HashMap < Integer , SpellEntry > ( ) ;
Data_2da spells2da = twoDA . get ( " spells " ) ;
for ( int i = 0 ; i < spells2da . getEntryCount ( ) ; i + + ) {
spelltype = NONE ;
errored = false ;
if ( isNormalSpell ( spells2da , i ) ) spelltype = NORMAL ;
else if ( isEpicSpell ( spells2da , i ) ) spelltype = EPIC ;
else if ( isPsionicPower ( spells2da , i ) ) spelltype = PSIONIC ;
else if ( isTruenameUtterance ( spells2da , i ) ) spelltype = UTTERANCE ;
else if ( isInvocation ( spells2da , i ) ) spelltype = INVOCATION ;
else if ( isManeuver ( spells2da , i ) ) spelltype = MANEUVER ;
if ( spelltype ! = NONE ) {
name = tlk . get ( spells2da . getEntry ( " Name " , i ) )
. replaceAll ( " / " , " / " ) ; // Let the UA insert line breaks if necessary
if ( verbose ) System . out . println ( " Generating entry data for " + name ) ;
// Check the name for validity
if ( name . equals ( badStrRef ) ) {
err_pr . println ( " Error: Invalid name for spell " + i ) ;
errored = true ;
}
// Handle the contents
text = htmlizeTLK ( tlk . get ( spells2da . getEntry ( " SpellDesc " , i ) ) ) ;
// Check the description validity
if ( tlk . get ( spells2da . getEntry ( " SpellDesc " , i ) ) . equals ( badStrRef ) ) {
err_pr . println ( " Error: Invalid description for spell " + i + " : " + name ) ;
errored = true ;
}
// Do the icon
icon = spells2da . getEntry ( " IconResRef " , i ) ;
if ( icon . equals ( " **** " ) ) {
err_pr . println ( " Error: Icon not defined for spell " + i + " : " + name ) ;
errored = true ;
}
icon = Icons . buildIcon ( icon ) ;
// Handle subradials, if any
subradials = null ;
// Assume that if there are any, the first slot is always non-****
if ( ! spells2da . getEntry ( " SubRadSpell1 " , i ) . equals ( " **** " ) ) {
subradials = new ArrayList < Tuple < String , String > > ( 5 ) ;
for ( int j = 1 ; j < = 5 ; j + + ) {
// Also assume that all the valid entries are in order, such that if Nth SubRadSpell entry
// is ****, all > N are also ****
if ( spells2da . getEntry ( " SubRadSpell " + j , i ) . equals ( " **** " ) )
break ;
try {
subRadial = Integer . parseInt ( spells2da . getEntry ( " SubRadSpell " + j , i ) ) ;
// Try name
subradName = tlk . get ( spells2da . getEntry ( " Name " , subRadial ) )
. replaceAll ( " / " , " / " ) ;
// Check the name for validity
if ( subradName . equals ( badStrRef ) ) {
err_pr . println ( " Error: Invalid Name entry for spell " + subRadial ) ;
errored = true ;
}
// Try icon
subradIcon = spells2da . getEntry ( " IconResRef " , subRadial ) ;
if ( subradIcon . equals ( " **** " ) ) {
err_pr . println ( " Error: Icon not defined for spell " + subRadial + " : " + subradName ) ;
errored = true ;
}
// Build list
subradials . add ( new Tuple < String , String > ( subradName , Icons . buildIcon ( subradIcon ) ) ) ;
} catch ( NumberFormatException e ) {
err_pr . println ( " Error: Spell " + i + " : " + name + " contains an invalid SubRadSpell " + j + " entry " ) ;
errored = true ;
}
}
}
// Build the path
switch ( spelltype ) {
case NORMAL :
path = spellPath + i + " .html " ;
break ;
case EPIC :
path = epicPath + i + " .html " ;
break ;
case PSIONIC :
path = psiPath + i + " .html " ;
break ;
case UTTERANCE :
path = utterPath + i + " .html " ;
break ;
case INVOCATION :
path = invocPath + i + " .html " ;
break ;
case MANEUVER :
path = manPath + i + " .html " ;
break ;
default :
throw new AssertionError ( " Unhandled spelltype: " + spelltype ) ;
}
// Check if we had any errors. If we did, and the error tolerance flag isn't up, skip generating this entry
if ( ! errored | | tolErr ) {
// Store a data structure represeting the entry into a hashmap
spells . put ( i , new SpellEntry ( name , text , icon , path , i , spelltype , subradials ) ) ;
} else
err_pr . println ( " Error: Failed to generate entry for spell " + i + " : " + name ) ;
}
}
System . gc ( ) ;
}
/ * *
* Creates a list of spells . 2da rows that should contain a psionic power ' s class - specific
* entry .
* /
2024-06-23 21:55:37 -05:00
public static void listPsionicPowers ( TwoDAStore twoDA ) {
2023-08-22 10:00:21 -04:00
// A map of power name to class-specific spells.2da entry
psiPowMap = new HashMap < String , Integer > ( ) ;
2024-06-23 21:55:37 -05:00
HashMap < String , Data_2da > potentials = twoDA . findAll ( " cls_psipw_ " ) ;
2023-08-22 10:00:21 -04:00
2024-06-23 21:55:37 -05:00
listAMSEntries ( potentials , psiPowMap ) ;
2023-08-22 10:00:21 -04:00
}
/ * *
* Creates a list of spells . 2da rows that should contain a truenaming utterance ' s
* /
2024-06-23 21:55:37 -05:00
public static void listTruenameUtterances ( TwoDAStore twoDA ) {
2023-08-22 10:00:21 -04:00
// A map of power name to class-specific spells.2da entry
utterMap = new HashMap < String , Integer > ( ) ;
// Load cls_*_utter.2da
2024-06-23 21:55:37 -05:00
HashMap < String , Data_2da > potentials = twoDA . findAll ( " _utter " ) ;
2023-08-22 10:00:21 -04:00
2024-06-23 21:55:37 -05:00
listAMSEntries ( potentials , utterMap ) ;
2023-08-22 10:00:21 -04:00
}
/ * *
* Creates a list of spells . 2da rows that should contain invocations
* /
2024-06-23 21:55:37 -05:00
public static void listInvocations ( TwoDAStore twoDA ) {
2023-08-22 10:00:21 -04:00
// A map of power name to class-specific spells.2da entry
invMap = new HashMap < String , Integer > ( ) ;
2024-06-23 21:55:37 -05:00
var potentials = twoDA . findAll ( " cls_inv_ " ) ;
2023-08-22 10:00:21 -04:00
2024-06-23 21:55:37 -05:00
listAMSEntries ( potentials , invMap ) ;
2023-08-22 10:00:21 -04:00
}
/ * *
* Creates a list of spells . 2da rows that should contain maneuvers
* /
2024-06-23 21:55:37 -05:00
public static void listManeuvers ( TwoDAStore twoDA ) {
2023-08-22 10:00:21 -04:00
// A map of power name to class-specific spells.2da entry
maneuverMap = new HashMap < String , Integer > ( ) ;
2024-06-23 21:55:37 -05:00
var potentials = twoDA . findAll ( " cls_move_ " ) ;
2023-08-22 10:00:21 -04:00
2024-06-23 21:55:37 -05:00
listAMSEntries ( potentials , maneuverMap ) ;
2023-08-22 10:00:21 -04:00
}
/ * *
* Does the actual list generation for listPsionicPowers ( ) and
* listTruenameUtterances ( ) .
*
* @param fileNames List of 2da files that contain the entries to be listed
* @param storeMap Map to store the entries in
* /
2024-06-23 21:55:37 -05:00
private static void listAMSEntries ( HashMap < String , Data_2da > list2das , Map < String , Integer > storeMap ) {
Data_2da spells2da = twoDA . get ( " spells " ) ;
2023-08-22 10:00:21 -04:00
// Parse the 2das
2024-06-23 21:55:37 -05:00
list2das . entrySet ( ) . stream ( ) . forEach (
list2da - > {
var value2da = list2da . getValue ( ) ;
for ( int i = 0 ; i < value2da . getEntryCount ( ) ; i + + ) {
2023-08-22 10:00:21 -04:00
// Column FeatID is used to determine if the row specifies the main entry of a power
2024-06-23 21:55:37 -05:00
if ( ! value2da . getEntry ( " FeatID " , i ) . equals ( " **** " ) ) {
2023-08-22 10:00:21 -04:00
try {
//look up spells.2da name of the realspellid if we don't have a name column
2024-06-23 21:55:37 -05:00
if ( value2da . getEntry ( " Name " , i ) = = null ) {
storeMap . put ( tlk . get ( spells2da . getEntry ( " Name " , value2da . getEntry ( " RealSpellID " , i ) ) ) , Integer . parseInt ( value2da . getEntry ( " RealSpellID " , i ) ) ) ;
2023-08-22 10:00:21 -04:00
} else {
2024-06-23 21:55:37 -05:00
storeMap . put ( tlk . get ( value2da . getEntry ( " Name " , i ) ) , Integer . parseInt ( value2da . getEntry ( " SpellID " , i ) ) ) ;
2023-08-22 10:00:21 -04:00
}
} catch ( NumberFormatException e ) {
2024-06-23 21:55:37 -05:00
err_pr . println ( " Error: Invalid SpellID entry in " + value2da . getName ( ) + " , line " + i ) ;
2023-08-22 10:00:21 -04:00
}
}
}
2024-06-23 21:55:37 -05:00
} ) ;
2023-08-22 10:00:21 -04:00
}
/ * *
* A small convenience method for wrapping all the normal spell checks into
* one .
*
* @param spells2da the Data_2da entry containing spells . 2da
* @param entryNum the line number to use for testing
* @return < code > true < / code > if any of the class spell level columns contain a number ,
* < code > false < / code > otherwise
* /
private static boolean isNormalSpell ( Data_2da spells2da , int entryNum ) {
if ( ! spells2da . getEntry ( " Bard " , entryNum ) . equals ( " **** " ) ) return true ;
if ( ! spells2da . getEntry ( " Cleric " , entryNum ) . equals ( " **** " ) ) return true ;
if ( ! spells2da . getEntry ( " Druid " , entryNum ) . equals ( " **** " ) ) return true ;
if ( ! spells2da . getEntry ( " Paladin " , entryNum ) . equals ( " **** " ) ) return true ;
if ( ! spells2da . getEntry ( " Ranger " , entryNum ) . equals ( " **** " ) ) return true ;
if ( ! spells2da . getEntry ( " Wiz_Sorc " , entryNum ) . equals ( " **** " ) ) return true ;
return false ;
}
/ * *
* A small convenience method for testing if the given entry contains a
* epic spell .
*
* @param spells2da the Data_2da entry containing spells . 2da
* @param entryNum the line number to use for testing
* @return < code > true < / code > if the impactscript name starts with strings specified in settings ,
* < code > false < / code > otherwise
* /
private static boolean isEpicSpell ( Data_2da spells2da , int entryNum ) {
for ( String check : settings . epicspellSignatures )
if ( spells2da . getEntry ( " ImpactScript " , entryNum ) . startsWith ( check ) ) return true ;
return false ;
}
/ * *
* A small convenience method for testing if the given entry contains a
* psionic power . This is determined by whether the power ' s id is
* in the psiPowMap Map .
*
* @param spells2da the Data_2da entry containing spells . 2da
* @param entryNum the line number to use for testing
* @return < code > true < / code > if entryNum in spells2da contains a psionic power ,
* < code > false < / code > otherwise
* /
private static boolean isPsionicPower ( Data_2da spells2da , int entryNum ) {
return psiPowMap . containsValue ( entryNum ) ;
}
/ * *
* A small convenience method for testing if the given entry contains a
* truenaming utterance . This is determined by whether the power ' s id is
* in the utterMap Map .
*
* @param spells2da the Data_2da entry containing spells . 2da
* @param entryNum the line number to use for testing
* @return < code > true < / code > if entryNum in spells2da contains an utterance ,
* < code > false < / code > otherwise
* /
private static boolean isTruenameUtterance ( Data_2da spells2da , int entryNum ) {
return utterMap . containsValue ( entryNum ) ;
}
/ * *
* A small convenience method for testing if the given entry contains an
* invocation . This is determined by whether the power ' s id is
* in the utterMap Map .
*
* @param spells2da the Data_2da entry containing spells . 2da
* @param entryNum the line number to use for testing
* @return < code > true < / code > if entryNum in spells2da contains an utterance ,
* < code > false < / code > otherwise
* /
private static boolean isInvocation ( Data_2da spells2da , int entryNum ) {
return invMap . containsValue ( entryNum ) ;
}
/ * *
* A small convenience method for testing if the given entry contains a
* maneuver . This is determined by whether the power ' s id is
* in the utterMap Map .
*
* @param spells2da the Data_2da entry containing spells . 2da
* @param entryNum the line number to use for testing
* @return < code > true < / code > if entryNum in spells2da contains an utterance ,
* < code > false < / code > otherwise
* /
private static boolean isManeuver ( Data_2da spells2da , int entryNum ) {
return maneuverMap . containsValue ( entryNum ) ;
}
/ * *
* Build the preliminary list of master feats , without the child feats
* linked in .
* /
public static void preliMasterFeats ( ) {
String mFeatPath = contentPath + " master_feats " + fileSeparator ;
String name = null ,
text = null ,
path = null ;
FeatEntry entry = null ;
boolean errored ;
masterFeats = new HashMap < Integer , FeatEntry > ( ) ;
Data_2da masterFeats2da = twoDA . get ( " masterfeats " ) ;
for ( int i = 0 ; i < masterFeats2da . getEntryCount ( ) ; i + + ) {
// Skip blank rows
if ( masterFeats2da . getEntry ( " LABEL " , i ) . equals ( " **** " ) ) continue ;
errored = false ;
// Get the name and validate it
name = tlk . get ( masterFeats2da . getEntry ( " STRREF " , i ) ) ;
if ( verbose ) System . out . println ( " Generating preliminary data for " + name ) ;
if ( name . equals ( badStrRef ) ) {
err_pr . println ( " Error: Invalid name for masterfeat " + i ) ;
errored = true ;
}
// Build the entry data
text = htmlizeTLK ( tlk . get ( masterFeats2da . getEntry ( " DESCRIPTION " , i ) ) ) ;
// Check the description validity
if ( tlk . get ( masterFeats2da . getEntry ( " DESCRIPTION " , i ) ) . equals ( badStrRef ) ) {
err_pr . println ( " Error: Invalid description for masterfeat " + i + " : " + name ) ;
errored = true ;
}
// Add in the icon
String icon = masterFeats2da . getEntry ( " ICON " , i ) ;
if ( icon . equals ( " **** " ) ) {
err_pr . println ( " Error: Icon not defined for masterfeat " + i + " : " + name ) ;
errored = true ;
}
icon = Icons . buildIcon ( icon ) ;
// Build the path
path = mFeatPath + i + " .html " ;
if ( ! errored | | tolErr ) {
// Store the entry to wait for further processing
// Masterfeats start as class feats and are converted into general feats if any child
// is a general feat
entry = new FeatEntry ( name , text , icon , path , i , false , true , null ) ;
masterFeats . put ( i , entry ) ;
} else
err_pr . println ( " Error: Failed to generate entry data for masterfeat " + i + " : " + name ) ;
}
System . gc ( ) ;
}
/ * *
* Build the preliminary list of feats , without master / successor / predecessor feats
* linked in .
* /
public static void preliFeats ( ) {
String featPath = contentPath + " feats " + fileSeparator ,
epicPath = contentPath + " epic_feats " + fileSeparator ,
classFeatPath = contentPath + " class_feats " + fileSeparator ,
classEpicPath = contentPath + " class_epic_feats " + fileSeparator ;
String name = null ,
text = null ,
icon = null ,
path = null ,
subradName = null ,
subradIcon = null ;
FeatEntry entry = null ;
List < Tuple < String , String > > subradials = null ;
boolean isEpic = false ,
isClassFeat = false ;
boolean errored ;
int featSpell ,
subRadial ;
feats = new HashMap < Integer , FeatEntry > ( ) ;
Data_2da feats2da = twoDA . get ( " feat " ) ;
Data_2da spells2da = twoDA . get ( " spells " ) ;
for ( int i = 0 ; i < feats2da . getEntryCount ( ) ; i + + ) {
// Skip blank rows and markers
if ( feats2da . getEntry ( " LABEL " , i ) . equals ( " **** " ) | |
feats2da . getEntry ( " FEAT " , i ) . equals ( " **** " ) )
continue ;
// Skip the ISC & Epic spell markers
if ( feats2da . getEntry ( " LABEL " , i ) . equals ( " ReservedForISCAndESS " ) ) continue ;
errored = false ;
// Get the name and validate it
name = tlk . get ( feats2da . getEntry ( " FEAT " , i ) ) ;
if ( verbose ) System . out . println ( " Generating preliminary data for " + name ) ;
if ( name . equals ( badStrRef ) ) {
err_pr . println ( " Error: Invalid name for feat " + i ) ;
errored = true ;
}
// Build the entry data
text = htmlizeTLK ( tlk . get ( feats2da . getEntry ( " DESCRIPTION " , i ) ) ) ;
// Check the description validity
if ( tlk . get ( feats2da . getEntry ( " DESCRIPTION " , i ) ) . equals ( badStrRef ) ) {
err_pr . println ( " Error: Invalid description for feat " + i + " : " + name ) ;
errored = true ;
}
// Add in the icon
icon = feats2da . getEntry ( " ICON " , i ) ;
if ( icon . equals ( " **** " ) ) {
err_pr . println ( " Error: Icon not defined for feat " + i + " : " + name ) ;
errored = true ;
}
icon = Icons . buildIcon ( icon ) ;
// Handle subradials, if any
subradials = null ;
if ( ! feats2da . getEntry ( " SPELLID " , i ) . equals ( " **** " ) ) {
try {
featSpell = Integer . parseInt ( feats2da . getEntry ( " SPELLID " , i ) ) ;
// Assume that if there are any, the first slot is always non-****
if ( ! spells2da . getEntry ( " SubRadSpell1 " , featSpell ) . equals ( " **** " ) ) {
subradials = new ArrayList < Tuple < String , String > > ( 5 ) ;
for ( int j = 1 ; j < = 5 ; j + + ) {
// Also assume that all the valid entries are in order, such that if Nth SubRadSpell entry
// is ****, all > N are also ****
if ( spells2da . getEntry ( " SubRadSpell " + j , featSpell ) . equals ( " **** " ) )
break ;
try {
subRadial = Integer . parseInt ( spells2da . getEntry ( " SubRadSpell " + j , featSpell ) ) ;
// Try name
subradName = tlk . get ( spells2da . getEntry ( " Name " , subRadial ) )
. replaceAll ( " / " , " / " ) ;
// Check the name for validity
if ( subradName . equals ( badStrRef ) ) {
err_pr . println ( " Error: Invalid Name entry for spell " + subRadial ) ;
errored = true ;
}
// Try icon
subradIcon = spells2da . getEntry ( " IconResRef " , subRadial ) ;
if ( subradIcon . equals ( " **** " ) ) {
err_pr . println ( " Error: Icon not defined for spell " + subRadial + " : " + subradName ) ;
errored = true ;
}
// Build list
subradials . add ( new Tuple < String , String > ( subradName , Icons . buildIcon ( subradIcon ) ) ) ;
} catch ( NumberFormatException e ) {
err_pr . println ( " Error: Spell " + featSpell + " : " + name + " contains an invalid SubRadSpell " + j + " entry " ) ;
errored = true ;
}
}
}
} catch ( NumberFormatException e ) {
err_pr . println ( " Error: Invalid SPELLID for feat " + i + " : " + name ) ;
errored = true ;
}
}
// Classification
isEpic = feats2da . getEntry ( " PreReqEpic " , i ) . equals ( " 1 " ) ;
isClassFeat = ! feats2da . getEntry ( " ALLCLASSESCANUSE " , i ) . equals ( " 1 " ) ;
// Get the path
if ( isEpic ) {
if ( isClassFeat ) path = classEpicPath + i + " .html " ;
else path = epicPath + i + " .html " ;
} else {
if ( isClassFeat ) path = classFeatPath + i + " .html " ;
else path = featPath + i + " .html " ;
}
if ( ! errored | | tolErr ) {
// Create the entry data structure
entry = new FeatEntry ( name , text , icon , path , i , isEpic , isClassFeat , subradials ) ;
// Store the entry to wait for further processing
feats . put ( i , entry ) ;
} else
err_pr . println ( " Error: Failed to generate entry data for feat " + i + " : " + name ) ;
}
System . gc ( ) ;
}
/ * *
* Builds the master - child , predecessor - successor and prerequisite links
* and modifies the entry texts accordingly .
* /
public static void linkFeats ( ) {
FeatEntry other = null ;
Data_2da feats2da = twoDA . get ( " feat " ) ;
boolean allChildrenEpic , allChildrenClassFeat ;
// Link normal feats to each other and to masterfeats
for ( FeatEntry check : feats . values ( ) ) {
if ( verbose ) System . out . println ( " Linking feat " + check . name ) ;
// Link to master
if ( ! feats2da . getEntry ( " MASTERFEAT " , check . entryNum ) . equals ( " **** " ) ) {
try {
other = masterFeats . get ( Integer . parseInt ( feats2da . getEntry ( " MASTERFEAT " , check . entryNum ) ) ) ;
other . childFeats . put ( check . name , check ) ;
check . master = other ;
if ( check . isEpic ) other . isEpic = true ;
if ( ! check . isClassFeat ) other . isClassFeat = false ;
} catch ( NumberFormatException e ) {
err_pr . println ( " Error: Feat " + check . entryNum + " : " + check . name + " contains an invalid MASTERFEAT entry " ) ;
} catch ( NullPointerException e ) {
err_pr . println ( " Error: Feat " + check . entryNum + " : " + check . name + " MASTERFEAT points to a nonexistent masterfeat entry " ) ;
}
}
// Handle prerequisites
buildPrerequisites ( check , feats2da ) ;
// Handle successor feat, if any
if ( ! feats2da . getEntry ( " SUCCESSOR " , check . entryNum ) . equals ( " **** " ) ) {
try {
other = feats . get ( Integer . parseInt ( feats2da . getEntry ( " SUCCESSOR " , check . entryNum ) ) ) ;
// Check for feats that have themselves as successor
if ( other = = check )
err_pr . println ( " Error: Feat " + check . entryNum + " : " + check . name + " has itself as successor " ) ;
other . isSuccessor = true ;
check . successor = other ;
} catch ( NumberFormatException e ) {
err_pr . println ( " Error: Feat " + check . entryNum + " : " + check . name + " contains an invalid SUCCESSOR entry " ) ;
} catch ( NullPointerException e ) {
err_pr . println ( " Error: Feat " + check . entryNum + " : " + check . name + " SUCCESSOR points to a nonexistent feat entry " ) ;
}
}
}
// Masterfeat categorisation
for ( FeatEntry check : masterFeats . values ( ) ) {
if ( verbose ) System . out . println ( " Linking masterfeat " + check . name ) ;
allChildrenEpic = allChildrenClassFeat = true ;
for ( FeatEntry child : check . childFeats . values ( ) ) {
if ( ! child . isEpic ) allChildrenEpic = false ;
if ( ! child . isClassFeat ) allChildrenClassFeat = false ;
}
check . allChildrenClassFeat = allChildrenClassFeat ;
check . allChildrenEpic = allChildrenEpic ;
}
System . gc ( ) ;
}
/ * *
* Links a feat and it ' s prerequisites .
* Separated from the linkFeats method for improved readability .
*
* @param check the feat entry to be examined
* @param feats2da the data structure representing feat . 2da
* /
private static void buildPrerequisites ( FeatEntry check , Data_2da feats2da ) {
FeatEntry andReq1 = null , andReq2 = null , orReq = null ;
String andReq1Num = feats2da . getEntry ( " PREREQFEAT1 " , check . entryNum ) ,
andReq2Num = feats2da . getEntry ( " PREREQFEAT2 " , check . entryNum ) ;
String [ ] orReqs = { feats2da . getEntry ( " OrReqFeat0 " , check . entryNum ) ,
feats2da . getEntry ( " OrReqFeat1 " , check . entryNum ) ,
feats2da . getEntry ( " OrReqFeat2 " , check . entryNum ) ,
feats2da . getEntry ( " OrReqFeat3 " , check . entryNum ) ,
feats2da . getEntry ( " OrReqFeat4 " , check . entryNum ) } ;
/* Handle AND prerequisite feats */
// Some paranoia about bad entries
if ( ! andReq1Num . equals ( " **** " ) ) {
try {
andReq1 = feats . get ( Integer . parseInt ( andReq1Num ) ) ;
if ( andReq1 = = null )
err_pr . println ( " Error: Feat " + check . entryNum + " : " + check . name + " PREREQFEAT1 points to a nonexistent feat entry " ) ;
else if ( andReq1 = = check )
err_pr . println ( " Error: Feat " + check . entryNum + " : " + check . name + " has itself as PREREQFEAT1 " ) ;
} catch ( NumberFormatException e ) {
err_pr . println ( " Error: Feat " + check . entryNum + " : " + check . name + " contains an invalid PREREQFEAT1 entry " ) ;
}
}
if ( ! andReq2Num . equals ( " **** " ) ) {
try {
andReq2 = feats . get ( Integer . parseInt ( andReq2Num ) ) ;
if ( andReq2 = = null )
err_pr . println ( " Error: Feat " + check . entryNum + " : " + check . name + " PREREQFEAT2 points to a nonexistent feat entry " ) ;
else if ( andReq2 = = check )
err_pr . println ( " Error: Feat " + check . entryNum + " : " + check . name + " has itself as PREREQFEAT2 " ) ;
} catch ( NumberFormatException e ) {
err_pr . println ( " Error: Feat " + check . entryNum + " : " + check . name + " contains an invalid PREREQFEAT2 entry " ) ;
}
}
// Check if we had at least one valid entry. If so, link
if ( andReq1 ! = null | | andReq2 ! = null ) {
if ( andReq1 ! = null ) {
check . andRequirements . put ( andReq1 . name , andReq1 ) ;
andReq1 . requiredForFeats . put ( check . name , check ) ;
}
if ( andReq2 ! = null ) {
check . andRequirements . put ( andReq2 . name , andReq2 ) ;
andReq2 . requiredForFeats . put ( check . name , check ) ;
}
}
/* Handle OR prerequisite feats */
// First, check if there are any
boolean hasOrReqs = false ;
for ( String orReqCheck : orReqs )
if ( ! orReqCheck . equals ( " **** " ) ) hasOrReqs = true ;
if ( hasOrReqs ) {
// Loop through each req and see if it's a valid link
for ( int i = 0 ; i < orReqs . length ; i + + ) {
if ( ! orReqs [ i ] . equals ( " **** " ) ) {
try {
orReq = feats . get ( Integer . parseInt ( orReqs [ i ] ) ) ;
} catch ( NumberFormatException e ) {
err_pr . println ( " Error: Feat " + check . entryNum + " : " + check . name + " contains an invalid OrReqFeat " + i + " entry " ) ;
}
if ( orReq ! = null ) {
if ( orReq = = check )
err_pr . println ( " Error: Feat " + check . entryNum + " : " + check . name + " has itself as OrReqFeat " + i ) ;
check . orRequirements . put ( orReq . name , orReq ) ;
orReq . requiredForFeats . put ( check . name , check ) ;
} else
err_pr . println ( " Error: Feat " + check . entryNum + " : " + check . name + " OrReqFeat " + i + " points to a nonexistent feat entry " ) ;
}
}
}
}
/ * *
* Handles creation of the domain pages .
* Kills page generation on bad strref for name .
* Other errors are logged and prevent page write
* /
public static void doDomains ( ) {
String domainPath = contentPath + " domains " + fileSeparator ;
String name = null ,
text = null ,
icon = null ,
path = null ;
List < SpellEntry > spellList = null ;
FeatEntry grantedFeat = null ;
SpellEntry grantedSpell = null ;
boolean errored ;
domains = new HashMap < Integer , DomainEntry > ( ) ;
Data_2da domains2da = twoDA . get ( " domains " ) ;
for ( int i = 0 ; i < domains2da . getEntryCount ( ) ; i + + ) {
// Skip blank rows
if ( domains2da . getEntry ( " LABEL " , i ) . equals ( " **** " ) ) continue ;
errored = false ;
// Get the name and validate it
name = tlk . get ( domains2da . getEntry ( " Name " , i ) ) ;
if ( verbose ) System . out . println ( " Printing page for " + name ) ;
if ( name . equals ( badStrRef ) ) {
err_pr . println ( " Error: Invalid name for domain " + i ) ;
errored = true ;
}
// Build the entry data
text = htmlizeTLK ( tlk . get ( domains2da . getEntry ( " Description " , i ) ) ) ;
// Check the description validity
if ( tlk . get ( domains2da . getEntry ( " Description " , i ) ) . equals ( badStrRef ) ) {
err_pr . println ( " Error: Invalid description for domain " + i + " : " + name ) ;
errored = true ;
}
// Add in the icon
icon = domains2da . getEntry ( " Icon " , i ) ;
if ( icon . equals ( " **** " ) ) {
err_pr . println ( " Error: Icon not defined for domain " + i + " : " + name ) ;
errored = true ;
}
icon = Icons . buildIcon ( icon ) ;
// Add a link to the granted feat
try {
grantedFeat = feats . get ( Integer . parseInt ( domains2da . getEntry ( " GrantedFeat " , i ) ) ) ;
} catch ( NumberFormatException e ) {
err_pr . println ( " Error: Invalid entry in GrantedFeat of domain " + i + " : " + name ) ;
errored = true ;
} catch ( NullPointerException e ) {
err_pr . println ( " Error: GrantedFeat entry for domain " + i + " : " + name + " points to non-existent feat: " + domains2da . getEntry ( " GrantedFeat " , i ) ) ;
errored = true ;
}
// Add links to the granted spells
spellList = new ArrayList < SpellEntry > ( ) ;
for ( int j = 1 ; j < = 9 ; j + + ) {
// Skip blanks
if ( domains2da . getEntry ( " Level_ " + j , i ) . equals ( " **** " ) ) continue ;
try {
grantedSpell = spells . get ( Integer . parseInt ( domains2da . getEntry ( " Level_ " + j , i ) ) ) ;
if ( grantedSpell ! = null )
spellList . add ( grantedSpell ) ;
else {
err_pr . println ( " Error: Level_ " + j + " entry for domain " + i + " : " + name + " points to non-existent spell: " + domains2da . getEntry ( " Level_ " + j , i ) ) ;
errored = true ;
}
} catch ( NumberFormatException e ) {
err_pr . println ( " Error: Invalid entry in Level_ " + j + " of domain " + i + " : " + name ) ;
errored = true ;
}
}
// Build path and print
path = domainPath + i + " .html " ;
if ( ! errored | | tolErr ) {
domains . put ( i , new DomainEntry ( name , text , icon , path , i , grantedFeat , spellList ) ) ;
} else
err_pr . println ( " Error: Failed to generate entry for domain " + i + " : " + name ) ;
}
System . gc ( ) ;
}
/ * *
* Handles creation of the race pages .
* /
public static void doRaces ( ) {
String racePath = contentPath + " races " + fileSeparator ;
String name = null ,
text = null ,
path = null ;
TreeMap < String , FeatEntry > featList = null ;
FeatEntry grantedFeat = null ;
Data_2da featTable = null ;
boolean errored ;
races = new HashMap < Integer , RaceEntry > ( ) ;
Data_2da racialtypes2da = twoDA . get ( " racialtypes " ) ;
for ( int i = 0 ; i < racialtypes2da . getEntryCount ( ) ; i + + ) {
// Skip non-player races
if ( ! racialtypes2da . getEntry ( " PlayerRace " , i ) . equals ( " 1 " ) ) continue ;
errored = false ;
try {
// Get the name and validate it
name = tlk . get ( racialtypes2da . getEntry ( " Name " , i ) ) ;
if ( verbose ) System . out . println ( " Printing page for " + name ) ;
if ( name . equals ( badStrRef ) ) {
err_pr . println ( " Error: Invalid name for race " + i ) ;
errored = true ;
}
// Build the entry data
text = htmlizeTLK ( tlk . get ( racialtypes2da . getEntry ( " Description " , i ) ) ) ;
// Check the description validity
if ( tlk . get ( racialtypes2da . getEntry ( " Description " , i ) ) . equals ( badStrRef ) ) {
err_pr . println ( " Error: Invalid description for race " + i + " : " + name ) ;
errored = true ;
}
// Add links to the racial feats
try {
featTable = twoDA . get ( racialtypes2da . getEntry ( " FeatsTable " , i ) ) ;
} catch ( TwoDAReadException e ) {
throw new PageGenerationException ( " Failed to read RACE_FEAT_*.2da for race " + i + " : " + name + " : \ n " + e ) ;
}
featList = new TreeMap < String , FeatEntry > ( ) ;
for ( int j = 0 ; j < featTable . getEntryCount ( ) ; j + + ) {
try {
grantedFeat = feats . get ( Integer . parseInt ( featTable . getEntry ( " FeatIndex " , j ) ) ) ;
featList . put ( grantedFeat . name , grantedFeat ) ;
} catch ( NumberFormatException e ) {
err_pr . println ( " Error: Invalid entry in FeatIndex line " + j + " of " + featTable . getName ( ) ) ;
errored = true ;
} catch ( NullPointerException e ) {
err_pr . println ( " Error: FeatIndex line " + j + " of " + featTable . getName ( ) + " points to non-existent feat: " + featTable . getEntry ( " FeatIndex " , j ) ) ;
errored = true ;
}
}
// Build path and print
path = racePath + i + " .html " ;
if ( ! errored | | tolErr ) {
races . put ( i , new RaceEntry ( name , text , path , i , featList ) ) ;
} else
throw new PageGenerationException ( " Error(s) encountered while creating page " ) ;
} catch ( PageGenerationException e ) {
err_pr . println ( " Error: Failed to print page for race " + i + " : " + name + " : \ n " + e ) ;
}
}
System . gc ( ) ;
}
/ * *
* Handles creation of the class pages .
* Subsections handled by several following methods .
* /
public static void doClasses ( ) {
String baseClassPath = contentPath + " base_classes " + fileSeparator ,
prestigeClassPath = contentPath + " prestige_classes " + fileSeparator ;
String name = null ,
text = null ,
icon = null ,
path = null ,
temp = null ;
List < String [ ] > babSav = null ;
Tuple < TreeMap < String , GenericEntry > , TreeMap < String , GenericEntry > > skillList = null ;
Tuple < List < Integer > , Tuple < List < TreeMap < String , FeatEntry > > , List < TreeMap < String , FeatEntry > > > > featList = null ;
List < Tuple < Tuple < String , String > , TreeMap < Integer , TreeMap < String , SpellEntry > > > > magics = null ;
boolean errored ;
classes = new HashMap < Integer , ClassEntry > ( ) ;
Data_2da classes2da = twoDA . get ( " classes " ) ;
for ( int i = 0 ; i < classes2da . getEntryCount ( ) ; i + + ) {
// Skip non-player classes
if ( ! classes2da . getEntry ( " PlayerClass " , i ) . equals ( " 1 " ) ) continue ;
errored = false ;
try {
name = tlk . get ( classes2da . getEntry ( " Name " , i ) ) ;
if ( verbose ) System . out . println ( " Printing page for " + name ) ;
if ( name . equals ( badStrRef ) ) {
err_pr . println ( " Error: Invalid name for class " + i ) ;
errored = true ;
}
// Build the entry data
text = htmlizeTLK ( tlk . get ( classes2da . getEntry ( " Description " , i ) ) ) ;
// Check the description validity
if ( tlk . get ( classes2da . getEntry ( " Description " , i ) ) . equals ( badStrRef ) ) {
err_pr . println ( " Error: Invalid description for class " + i + " : " + name ) ;
errored = true ;
}
// Add in the icon
icon = classes2da . getEntry ( " Icon " , i ) ;
if ( icon . equals ( " **** " ) ) {
err_pr . println ( " Error: Icon not defined for class " + i + " : " + name ) ;
errored = true ;
}
icon = Icons . buildIcon ( icon ) ;
// Add in the BAB and saving throws table
babSav = buildBabAndSaveList ( classes2da , i ) ;
// Add in the skills table
skillList = buildSkillList ( classes2da , i ) ;
// Add in the feat table
featList = buildClassFeatList ( classes2da , i ) ;
// Add in the spells / powers table
magics = buildClassMagicList ( classes2da , i ) ;
/ * Check whether this is a base or a prestige class . No prestige
* class should give exp penalty ( nor should any base class not give it ) ,
* so it gan be used as an indicator .
* /
temp = classes2da . getEntry ( " XPPenalty " , i ) ;
if ( ! ( temp . equals ( " 0 " ) | | temp . equals ( " 1 " ) ) ) {
if ( tolErr ) {
err_pr . println ( " Error: Invalid List XPPenalty in classes.2da on row " + i + " : " + temp ) ;
continue ;
} else
throw new PageGenerationException ( " Invalid XPPenalty entry in classes.2da on row " + i + " : " + temp ) ;
}
if ( temp . equals ( " 1 " ) )
path = baseClassPath + i + " .html " ;
else
path = prestigeClassPath + i + " .html " ;
if ( ! errored | | tolErr ) {
classes . put ( i , new ClassEntry ( name , text , icon , path , i , temp . equals ( " 1 " ) ,
babSav , skillList , featList , magics ) ) ;
} else
throw new PageGenerationException ( " Error(s) encountered while creating page " ) ;
} catch ( PageGenerationException e ) {
err_pr . println ( " Error: Failed to print page for class " + i + " : " + name + " : \ n " + e ) ;
}
}
System . gc ( ) ;
}
/ * *
* Constructs a list of arrays containing the BAB and saving throw values
* for the given class .
*
* @param classes2da data structure wrapping classes . 2da
* @param entryNum number of the entry to generate list for
* @return List < String [ ] > containing the values . BAB first , then saving throws ( Fort , Ref , Will ) .
* @throws PageGenerationException if there is an error while generating the list and error tolerance is off
* /
private static List < String [ ] > buildBabAndSaveList ( Data_2da classes2da , int entryNum ) {
Data_2da babTable = null ,
saveTable = null ;
try {
babTable = twoDA . get ( classes2da . getEntry ( " AttackBonusTable " , entryNum ) ) ;
} catch ( TwoDAReadException e ) {
throw new PageGenerationException ( " Failed to read CLS_ATK_*.2da for class " + entryNum + " : " + tlk . get ( classes2da . getEntry ( " Name " , entryNum ) ) + " : \ n " + e ) ;
}
try {
saveTable = twoDA . get ( classes2da . getEntry ( " SavingThrowTable " , entryNum ) ) ;
} catch ( TwoDAReadException e ) {
throw new PageGenerationException ( " Failed to read CLS_SAVTHR_*.2da for class " + entryNum + " : " + tlk . get ( classes2da . getEntry ( " Name " , entryNum ) ) + " : \ n " + e ) ;
}
/ * Determine maximum level to print bab & save values to
* The maximum level of the class , or the last non - epic level
* whichever is lower
* /
int maxToPrint = 0 , maxLevel = 0 , epicLevel = 0 ;
try {
maxLevel = Integer . parseInt ( classes2da . getEntry ( " MaxLevel " , entryNum ) ) ;
} catch ( NumberFormatException e ) {
if ( tolErr )
err_pr . println ( " Error: Invalid MaxLevel entry for class " + entryNum + " : " + tlk . get ( classes2da . getEntry ( " Name " , entryNum ) ) ) ;
else
throw new PageGenerationException ( " Invalid MaxLevel entry for class " + entryNum + " : " + tlk . get ( classes2da . getEntry ( " Name " , entryNum ) ) ) ;
}
try {
epicLevel = Integer . parseInt ( classes2da . getEntry ( " EpicLevel " , entryNum ) ) ;
} catch ( NumberFormatException e ) {
if ( tolErr )
err_pr . println ( " Error: Invalid EpicLevel entry for class " + entryNum + " : " + tlk . get ( classes2da . getEntry ( " Name " , entryNum ) ) ) ;
else
throw new PageGenerationException ( " Invalid EpicLevel entry for class " + entryNum + " : " + tlk . get ( classes2da . getEntry ( " Name " , entryNum ) ) ) ;
}
// base classes have special notation for the epic level limit
if ( epicLevel = = - 1 )
maxToPrint = 20 ;
else
maxToPrint = maxLevel > epicLevel ? epicLevel : maxLevel ;
// If the class has any pre-epic levels
List < String [ ] > toReturn = new ArrayList < String [ ] > ( maxToPrint ) ;
if ( maxToPrint > 0 ) {
// Start building the table
for ( int i = 0 ; i < maxToPrint ; i + + ) {
toReturn . add ( new String [ ] {
babTable . getEntry ( " BAB " , i ) ,
saveTable . getEntry ( " FortSave " , i ) ,
saveTable . getEntry ( " RefSave " , i ) ,
saveTable . getEntry ( " WillSave " , i )
} ) ;
}
}
return toReturn ;
}
/ * *
* Constructs a list of the class and cross - class skills of the
* given class
*
* @param classes2da data structure wrapping classes . 2da
* @param entryNum number of the entry to generate table for
* @return Tuple < TreeMap < String , GenericEntry > , TreeMap < String , GenericEntry > >
* containing the class and cross - class skills of the given class . The
* first tuple member contains the class skills , the second cross - class .
* The map key is the name of the skill .
* @throws PageGenerationException if there is an error while generating the list and error tolerance is off
* /
private static Tuple < TreeMap < String , GenericEntry > , TreeMap < String , GenericEntry > > buildSkillList ( Data_2da classes2da , int entryNum ) {
Data_2da skillTable = null ;
try {
skillTable = twoDA . get ( classes2da . getEntry ( " SkillsTable " , entryNum ) ) ;
} catch ( TwoDAReadException e ) {
throw new PageGenerationException ( " Failed to read CLS_SKILL_*.2da for class " + entryNum + " : " + tlk . get ( classes2da . getEntry ( " Name " , entryNum ) ) + " : \ n " + e ) ;
}
Tuple < TreeMap < String , GenericEntry > , TreeMap < String , GenericEntry > > toReturn =
new Tuple < TreeMap < String , GenericEntry > , TreeMap < String , GenericEntry > > (
new TreeMap < String , GenericEntry > ( ) , new TreeMap < String , GenericEntry > ( ) ) ;
String skillNum = null ;
GenericEntry skillEntry = null ;
for ( int i = 0 ; i < skillTable . getEntryCount ( ) ; i + + ) {
skillNum = skillTable . getEntry ( " ClassSkill " , i ) ;
// Yet more validity checking :P
if ( ! ( skillNum . equals ( " 0 " ) | | skillNum . equals ( " 1 " ) ) ) {
if ( tolErr ) {
err_pr . println ( " Error: Invalid ClassSkill entry in " + skillTable . getName ( ) + " on row " + i ) ;
continue ;
} else
throw new PageGenerationException ( " Invalid ClassSkill entry in " + skillTable . getName ( ) + " on row " + i ) ;
}
try {
skillEntry = skills . get ( Integer . parseInt ( skillTable . getEntry ( " SkillIndex " , i ) ) ) ;
} catch ( NumberFormatException e ) {
if ( tolErr ) {
err_pr . println ( " Error: Invalid SkillIndex entry in " + skillTable . getName ( ) + " on row " + i ) ;
continue ;
} else
throw new PageGenerationException ( " Invalid SkillIndex entry in " + skillTable . getName ( ) + " on row " + i ) ;
}
if ( skillEntry = = null ) {
if ( tolErr ) {
err_pr . println ( " Error: SkillIndex entry in " + skillTable . getName ( ) + " on row " + i + " points to non-existent skill " ) ;
continue ;
} else
throw new PageGenerationException ( " SkillIndex entry in " + skillTable . getName ( ) + " on row " + i + " points to non-existent skill " ) ;
}
if ( skillNum . equals ( " 1 " ) )
toReturn . e1 . put ( skillEntry . name , skillEntry ) ; // Class skill
else
toReturn . e2 . put ( skillEntry . name , skillEntry ) ; // Cross-class skill
}
return toReturn ;
}
/ * *
* Constructs a pair of granted feat , selectable feat lists for
* the given class .
*
* @param classes2da data structure wrapping classes . 2da
* @param entryNum number of the entry to generate list for
* @return Tuple < List < TreeMap < String , FeatEntry > > , List < TreeMap < String , FeatEntry > > > .
* The first list contains the granted feats , the second selectable feats .
* Each list consists of TreeMaps containing the feats that are related to
* the list indexth ( + 1 ) level , keyed by feat name .
* @throws PageGenerationException if there is an error while generating the lists and error tolerance is off
* /
private static Tuple < List < Integer > , Tuple < List < TreeMap < String , FeatEntry > > , List < TreeMap < String , FeatEntry > > > >
buildClassFeatList ( Data_2da classes2da , int entryNum ) {
Data_2da featTable = null ,
bonusFeatTable = null ;
ArrayList < TreeMap < String , FeatEntry > > grantedFeatList = new ArrayList < TreeMap < String , FeatEntry > > ( 0 ) ,
selectableFeatList = new ArrayList < TreeMap < String , FeatEntry > > ( 0 ) ;
ArrayList < Integer > bonusFeatCounts = new ArrayList < Integer > ( ) ;
HashSet < FeatEntry > masterFeatsUsed = new HashSet < FeatEntry > ( ) ;
String listNum = null ;
FeatEntry classFeat = null ;
int maxLevel , epicLevel , grantedLevel ;
// Attempt to load the class feats table
try {
featTable = twoDA . get ( classes2da . getEntry ( " FeatsTable " , entryNum ) ) ;
} catch ( TwoDAReadException e ) {
throw new PageGenerationException ( " Failed to read CLS_FEAT_*.2da for class " + entryNum + " : " + tlk . get ( classes2da . getEntry ( " Name " , entryNum ) ) + " : \ n " + e ) ;
}
// Attempt to load the class bonus feat slots table
try {
bonusFeatTable = twoDA . get ( classes2da . getEntry ( " BonusFeatsTable " , entryNum ) ) ;
} catch ( TwoDAReadException e ) {
throw new PageGenerationException ( " Failed to read CLS_BFEAT_*.2da for class " + entryNum + " : " + tlk . get ( classes2da . getEntry ( " Name " , entryNum ) ) + " : \ n " + e ) ;
}
// Attempt to read the class epic level
try {
epicLevel = Integer . parseInt ( classes2da . getEntry ( " EpicLevel " , entryNum ) ) ;
} catch ( NumberFormatException e ) {
throw new PageGenerationException ( " Invalid EpicLevel entry for class " + entryNum + " : " + tlk . get ( classes2da . getEntry ( " Name " , entryNum ) ) ) ;
}
// Attempt to read the class maximum level
try {
if ( epicLevel = = - 1 )
maxLevel = 40 ;
else
maxLevel = Integer . parseInt ( classes2da . getEntry ( " MaxLevel " , entryNum ) ) ;
} catch ( NumberFormatException e ) {
throw new PageGenerationException ( " Invalid MaxLevel entry for class " + entryNum + " : " + tlk . get ( classes2da . getEntry ( " Name " , entryNum ) ) ) ;
}
// Base classes have EpicLevel defined as -1, but become epic at L20
if ( epicLevel = = - 1 ) epicLevel = 20 ;
// Sanity check
else if ( epicLevel > maxLevel ) {
if ( tolErr ) {
err_pr . println ( " Error: EpicLevel value( " + epicLevel + " ) greater than MaxLevel value( " + maxLevel + " ) for class " + entryNum + " : " + tlk . get ( classes2da . getEntry ( " Name " , entryNum ) ) ) ;
epicLevel = maxLevel ;
} else
throw new PageGenerationException ( " EpicLevel value( " + epicLevel + " ) greater than MaxLevel value( " + maxLevel + " ) for class " + entryNum + " : " + tlk . get ( classes2da . getEntry ( " Name " , entryNum ) ) ) ;
}
// Init the lists
for ( int i = 0 ; i < maxLevel ; i + + ) grantedFeatList . add ( null ) ;
for ( int i = 0 ; i < maxLevel ; i + + ) selectableFeatList . add ( null ) ;
// Build a level-sorted list of feats
for ( int i = 0 ; i < featTable . getEntryCount ( ) ; i + + ) {
// Skip empty rows and comments
if ( featTable . getEntry ( " FeatLabel " , i ) . equals ( " **** " ) | |
featTable . getEntry ( " FeatIndex " , i ) . equals ( " **** " ) )
continue ;
// Read the list number and validate
listNum = featTable . getEntry ( " List " , i ) ;
if ( ! ( listNum . equals ( " 0 " ) | | listNum . equals ( " 1 " ) | | listNum . equals ( " 2 " ) | | listNum . equals ( " 3 " ) ) ) {
if ( tolErr ) {
err_pr . println ( " Error: Invalid List entry in " + featTable . getName ( ) + " on row " + i + " : " + listNum ) ;
continue ;
} else
throw new PageGenerationException ( " Invalid List entry in " + featTable . getName ( ) + " on row " + i + " : " + listNum ) ;
}
// Read the level granted on and validate
try {
grantedLevel = Integer . parseInt ( featTable . getEntry ( " GrantedOnLevel " , i ) ) ;
} catch ( NumberFormatException e ) {
if ( tolErr ) {
err_pr . println ( " Error: Invalid GrantedOnLevel entry in " + featTable . getName ( ) + " on row " + i + " : " + featTable . getEntry ( " GrantedOnLevel " , i ) ) ;
continue ;
} else
throw new PageGenerationException ( " Invalid GrantedOnLevel entry in " + featTable . getName ( ) + " on row " + i + " : " + featTable . getEntry ( " GrantedOnLevel " , i ) ) ;
}
// Complain about a semantic error
if ( listNum . equals ( " 3 " ) & & grantedLevel = = - 1 ) {
if ( tolErr ) {
err_pr . println ( " Error: List value '3' combined with GrantedOnLevel value '-1' in " + featTable . getName ( ) + " on row " + i ) ;
continue ;
} else
throw new PageGenerationException ( " List value '3' combined with GrantedOnLevel value '-1' in " + featTable . getName ( ) + " on row " + i ) ;
}
// Get the feat on this row and validate
try {
classFeat = feats . get ( Integer . parseInt ( featTable . getEntry ( " FeatIndex " , i ) ) ) ;
} catch ( NumberFormatException e ) {
if ( tolErr ) {
err_pr . println ( " Error: Invalid FeatIndex entry in " + featTable . getName ( ) + " on row " + i + " : " + featTable . getEntry ( " FeatIndex " , i ) ) ;
continue ;
} else
throw new PageGenerationException ( " Invalid FeatIndex entry in " + featTable . getName ( ) + " on row " + i + " : " + featTable . getEntry ( " FeatIndex " , i ) ) ;
}
if ( classFeat = = null ) {
if ( tolErr ) {
err_pr . println ( " Error: FeatIndex entry in " + featTable . getName ( ) + " on row " + i + " points to non-existent feat: " + featTable . getEntry ( " FeatIndex " , i ) ) ;
continue ;
} else
throw new PageGenerationException ( " FeatIndex entry in " + featTable . getName ( ) + " on row " + i + " points to non-existent feat: " + featTable . getEntry ( " FeatIndex " , i ) ) ;
}
// Skip feats that can never be gotten
if ( grantedLevel > 40 ) continue ;
if ( grantedLevel > maxLevel ) {
// This is never a fatal error. It's merely bad practice to place the feat outside reachable bounds, but not obviously so (ex, value of 99)
err_pr . println ( " Error: GrantedOnLevel entry in " + featTable . getName ( ) + " on row " + i + " is greater than the class's maximum level, but not obviously unreachable: " + grantedLevel + " vs. " + maxLevel ) ;
continue ;
}
// If the feat has a master, replace it with the master in the listing to prevent massive spammage
if ( classFeat . master ! = null ) {
// Only add masterfeats to the list once.
if ( masterFeatsUsed . contains ( classFeat . master ) ) continue ;
masterFeatsUsed . add ( classFeat . master ) ;
classFeat = classFeat . master ;
}
// Freely selectable feats become available at L1
if ( grantedLevel = = - 1 ) {
if ( classFeat . isEpic ) {
// Epic feats should be shown to become available on the level that is the first after the class's normal progression ends
// Sanity check here against bad class entries causing index violations
grantedLevel = Math . min ( epicLevel + 1 , maxLevel ) ;
} else
grantedLevel = 1 ;
}
grantedLevel - = 1 ; // Adjust to 0-based array index
// Differentiate by automatically granted or selectable
if ( listNum . equals ( " 3 " ) ) {
// Create the map if missing
if ( grantedFeatList . get ( grantedLevel ) = = null )
grantedFeatList . set ( grantedLevel , new TreeMap < String , FeatEntry > ( ) ) ;
// Add the feat to the map
grantedFeatList . get ( grantedLevel ) . put ( classFeat . name , classFeat ) ;
} else {
// Create the map if missing
if ( selectableFeatList . get ( grantedLevel ) = = null )
selectableFeatList . set ( grantedLevel , new TreeMap < String , FeatEntry > ( ) ) ;
// Add the feat to the map
selectableFeatList . get ( grantedLevel ) . put ( classFeat . name , classFeat ) ;
}
}
// Make sure there are enough entries in the bonus feat table
if ( bonusFeatTable . getEntryCount ( ) < maxLevel ) {
throw new PageGenerationException ( " Too few entries in class bonus feat table " + bonusFeatTable . getName ( ) + " : " + bonusFeatTable . getEntryCount ( ) + " . Need " + maxLevel ) ;
}
for ( int i = 0 ; i < maxLevel ; i + + ) {
try {
bonusFeatCounts . add ( Integer . parseInt ( bonusFeatTable . getEntry ( " Bonus " , i ) ) ) ;
} catch ( NumberFormatException e ) {
if ( tolErr ) {
err_pr . println ( " Error: Invalid Bonus entry in " + bonusFeatTable . getName ( ) + " on row " + i + " : " + bonusFeatTable . getEntry ( " Bonus " , i ) ) ;
continue ;
} else
throw new PageGenerationException ( " Invalid Bonus entry in " + bonusFeatTable . getName ( ) + " on row " + i + " : " + bonusFeatTable . getEntry ( " Bonus " , i ) ) ;
}
}
return new Tuple < List < Integer > , Tuple < List < TreeMap < String , FeatEntry > > , List < TreeMap < String , FeatEntry > > > > (
bonusFeatCounts ,
new Tuple < List < TreeMap < String , FeatEntry > > , List < TreeMap < String , FeatEntry > > > (
grantedFeatList ,
selectableFeatList ) ) ;
}
private static int getIntFrom2DAColumn ( Data_2da table , String column , int row ) {
return getIntFrom2DAColumn ( table , column , row , false ) ;
}
private static int getIntFrom2DAColumn ( Data_2da table , String column , int row , boolean mayBeInvalid ) {
try {
return Integer . parseInt ( table . getEntry ( column , row ) ) ;
} catch ( NumberFormatException e ) {
if ( mayBeInvalid ) {
// Caller indicated that it's okay if this value doesn't exist
return - 1 ;
}
if ( tolErr ) {
err_pr . println ( " Error: Invalid " + column + " entry in " + table . getName ( ) + " on row " + row + " : " + table . getEntry ( column , row ) ) ;
return - 1 ;
} else
throw new PageGenerationException ( " Invalid " + column + " entry in " + table . getName ( ) + " on row " + row + " : " + table . getEntry ( column , row ) ) ;
}
}
private static Map < Integer , String > baseClassIdsToSpellColumns = new HashMap < Integer , String > ( ) { {
put ( 1 , " Bard " ) ;
put ( 2 , " Cleric " ) ;
put ( 3 , " Druid " ) ;
put ( 6 , " Paladin " ) ;
put ( 7 , " Ranger " ) ;
put ( 9 , " Wiz_Sorc " ) ;
put ( 10 , " Wiz_Sorc " ) ;
} } ;
/ * *
* Constructs lists of the magics available to the given class .
* The entries are ordered by spell / power level
*
* @param classes2da data structure wrapping classes . 2da
* @param entryNum number of the entry to generate table for
* @return List < Tuple < Tuple < String , String > , TreeMap < Integer , TreeMap < String , SpellEntry > > > > .
* Each list entry contains one magic type . The first tuple member consists of the
* name of magic system , name of spell - equivalent pari . The second member contains
* the magic entries . The Integer - keyed TreeMaps contain the spells
* of each level . The integers are the spell levels .
* @throws PageGenerationException if there is an error while generating the list and error tolerance is off
* /
private static List < Tuple < Tuple < String , String > , TreeMap < Integer , TreeMap < String , SpellEntry > > > > buildClassMagicList ( Data_2da classes2da , int entryNum ) {
List < Tuple < Tuple < String , String > , TreeMap < Integer , TreeMap < String , SpellEntry > > > > toReturn =
new ArrayList < Tuple < Tuple < String , String > , TreeMap < Integer , TreeMap < String , SpellEntry > > > > ( ) ;
String classAbrev = null ;
Data_2da spellList = null ,
powerList = null ,
utterList = null ,
//invocations and maneuvers are a bit different because they don't have their own name column
invocList = null ,
maneuList = null ,
spells2da = twoDA . get ( " spells " ) ;
// Check for correctly formed table name
if ( ! classes2da . getEntry ( " FeatsTable " , entryNum ) . toLowerCase ( ) . startsWith ( " cls_feat_ " ) ) {
throw new PageGenerationException ( " Malformed FeatsTable entry for class " + entryNum + " : " + tlk . get ( classes2da . getEntry ( " Name " , entryNum ) ) ) ;
}
// Extract the class abbreviation
classAbrev = classes2da . getEntry ( " FeatsTable " , entryNum ) . toLowerCase ( ) . substring ( 9 ) ;
// Attempt to load the class and power 2das - If these fail, assume it was just due to non-existent file
try {
spellList = twoDA . get ( " cls_spcr_ " + classAbrev ) ;
} catch ( TwoDAReadException e ) { /* Ensure nullness */
powerList = null ;
}
try {
powerList = twoDA . get ( " cls_psipw_ " + classAbrev ) ;
} catch ( TwoDAReadException e ) { /* Ensure nullness */
powerList = null ;
}
try {
utterList = twoDA . get ( " cls_ " + classAbrev + " _utter " ) ;
} catch ( TwoDAReadException e ) { /* Ensure nullness */
utterList = null ;
}
try {
invocList = twoDA . get ( " cls_inv_ " + classAbrev ) ;
} catch ( TwoDAReadException e ) { /* Ensure nullness */
invocList = null ;
}
try {
maneuList = twoDA . get ( " cls_move_ " + classAbrev ) ;
} catch ( TwoDAReadException e ) { /* Ensure nullness */
maneuList = null ;
}
// Do spellbook
if ( spellList = = null & & baseClassIdsToSpellColumns . containsKey ( entryNum ) ) {
// There's no custom spellbook for this class, so pull the info from spells.2da
TreeMap < Integer , TreeMap < String , SpellEntry > > levelLists = new TreeMap < Integer , TreeMap < String , SpellEntry > > ( ) ;
SpellEntry spell = null ;
String classColumnName = baseClassIdsToSpellColumns . get ( entryNum ) ;
int level ;
for ( int i = 0 ; i < spells2da . getEntryCount ( ) ; i + + ) {
// Make sure the Level entry is a number
level = getIntFrom2DAColumn ( spells2da , classColumnName , i , true ) ;
if ( level = = - 1 ) continue ;
spell = spells . get ( i ) ;
// If no map for this level yet, fill it in
if ( ! levelLists . containsKey ( level ) )
levelLists . put ( level , new TreeMap < String , SpellEntry > ( ) ) ;
// Add the spell to the map
levelLists . get ( level )
. put ( spell . name , spell ) ;
}
toReturn . add ( new Tuple < Tuple < String , String > , TreeMap < Integer , TreeMap < String , SpellEntry > > > (
new Tuple < String , String > ( curLanguageData [ LANGDATA_SPELLBOOKTXT ] , curLanguageData [ LANGDATA_SPELLSTXT ] ) ,
levelLists ) ) ;
} else if ( spellList ! = null ) {
// Map of level numbers to maps of spell names to html links
TreeMap < Integer , TreeMap < String , SpellEntry > > levelLists = new TreeMap < Integer , TreeMap < String , SpellEntry > > ( ) ;
SpellEntry spell = null ;
int level ;
for ( int i = 0 ; i < spellList . getEntryCount ( ) ; i + + ) {
// Make sure the Level entry is a number
level = getIntFrom2DAColumn ( spellList , " Level " , i ) ;
if ( level = = - 1 ) continue ;
// Make sure the SpellID is valid
int spellId = getIntFrom2DAColumn ( spellList , " SpellID " , i ) ;
if ( spellId = = - 1 ) continue ;
spell = spells . get ( spellId ) ;
if ( spell = = null ) {
if ( tolErr ) {
err_pr . println ( " Error: SpellID entry in " + spellList . getName ( ) + " on row " + i + " points at nonexistent spell: " + spellList . getEntry ( " SpellID " , i ) ) ;
continue ;
} else
throw new PageGenerationException ( " SpellID entry in " + spellList . getName ( ) + " on row " + i + " points at nonexistent spell: " + spellList . getEntry ( " SpellID " , i ) ) ;
}
// If no map for this level yet, fill it in
if ( ! levelLists . containsKey ( level ) )
levelLists . put ( level , new TreeMap < String , SpellEntry > ( ) ) ;
// Add the spell to the map
levelLists . get ( level )
. put ( spell . name , spell ) ;
}
toReturn . add ( new Tuple < Tuple < String , String > , TreeMap < Integer , TreeMap < String , SpellEntry > > > (
new Tuple < String , String > ( curLanguageData [ LANGDATA_SPELLBOOKTXT ] , curLanguageData [ LANGDATA_SPELLSTXT ] ) ,
levelLists ) ) ;
}
// Do psionics
if ( powerList ! = null ) {
// Map of level numbers to maps of spell names to html links
TreeMap < Integer , TreeMap < String , SpellEntry > > levelLists = new TreeMap < Integer , TreeMap < String , SpellEntry > > ( ) ;
SpellEntry power = null ;
int level ;
for ( int i = 0 ; i < powerList . getEntryCount ( ) ; i + + ) {
// Skip rows that do not define a power
if ( powerList . getEntry ( " Level " , i ) . equals ( " **** " ) )
continue ;
level = getIntFrom2DAColumn ( powerList , " Level " , i ) ;
if ( level = = - 1 ) continue ;
// Make sure the SpellID is valid
int realSpellId = getIntFrom2DAColumn ( powerList , " RealSpellID " , i ) ;
if ( realSpellId < 0 ) continue ;
power = spells . get ( realSpellId ) ;
if ( power = = null ) {
if ( tolErr ) {
err_pr . println ( " Error: Unable to map Name entry in " + powerList . getName ( ) + " on row " + i + " to a spellEntry: " + tlk . get ( powerList . getEntry ( " Label " , i ) ) ) ;
continue ;
} else
throw new PageGenerationException ( " Unable to map Name entry in " + powerList . getName ( ) + " on row " + i + " to a spellEntry: " + tlk . get ( powerList . getEntry ( " Name " , i ) ) ) ;
}
// If no map for this level yet, fill it in
if ( ! levelLists . containsKey ( level ) )
levelLists . put ( level , new TreeMap < String , SpellEntry > ( ) ) ;
// Add the spell to the map
levelLists . get ( level )
. put ( power . name , power ) ;
}
toReturn . add ( new Tuple < Tuple < String , String > , TreeMap < Integer , TreeMap < String , SpellEntry > > > (
new Tuple < String , String > ( curLanguageData [ LANGDATA_PSIONICPOWERSTXT ] , curLanguageData [ LANGDATA_POWERTXT ] ) ,
levelLists ) ) ;
}
// Do truenaming
if ( utterList ! = null ) {
// Map of level numbers to maps of spell names to html links
TreeMap < Integer , TreeMap < String , SpellEntry > > levelLists = new TreeMap < Integer , TreeMap < String , SpellEntry > > ( ) ;
SpellEntry utterance = null ;
int level ;
for ( int i = 0 ; i < utterList . getEntryCount ( ) ; i + + ) {
// Skip rows that do not define a power
if ( utterList . getEntry ( " Level " , i ) . equals ( " **** " ) )
continue ;
// Make sure the Level entry is a number
try {
level = Integer . parseInt ( utterList . getEntry ( " Level " , i ) ) ;
} catch ( NumberFormatException e ) {
if ( tolErr ) {
err_pr . println ( " Error: Invalid Level entry in " + utterList . getName ( ) + " on row " + i + " : " + utterList . getEntry ( " Level " , i ) ) ;
continue ;
} else
throw new PageGenerationException ( " Invalid Level entry in " + utterList . getName ( ) + " on row " + i + " : " + utterList . getEntry ( " Level " , i ) ) ;
}
// Make sure the SpellID is valid
utterance = null ;
try {
// Attempt to get the spell entry via a mapping of power names to spellIDs
utterance = spells . get ( utterMap . get ( tlk . get ( Integer . parseInt ( utterList . getEntry ( " Name " , i ) ) ) ) ) ;
} catch ( NumberFormatException e ) {
if ( tolErr ) {
err_pr . println ( " Error: Invalid Name entry in " + utterList . getName ( ) + " on row " + i + " : " + utterList . getEntry ( " Name " , i ) ) ;
continue ;
} else
throw new PageGenerationException ( " Invalid Name entry in " + utterList . getName ( ) + " on row " + i + " : " + utterList . getEntry ( " Name " , i ) ) ;
}
if ( utterance = = null ) {
if ( tolErr ) {
err_pr . println ( " Error: Unable to map Name entry in " + utterList . getName ( ) + " on row " + i + " to a spellEntry: " + tlk . get ( utterList . getEntry ( " Name " , i ) ) ) ;
continue ;
} else
throw new PageGenerationException ( " Unable to map Name entry in " + utterList . getName ( ) + " on row " + i + " to a spellEntry: " + tlk . get ( utterList . getEntry ( " Name " , i ) ) ) ;
}
// If no map for this level yet, fill it in
if ( ! levelLists . containsKey ( level ) )
levelLists . put ( level , new TreeMap < String , SpellEntry > ( ) ) ;
// Add the spell to the map
levelLists . get ( level )
. put ( utterance . name , utterance ) ;
}
toReturn . add ( new Tuple < Tuple < String , String > , TreeMap < Integer , TreeMap < String , SpellEntry > > > (
new Tuple < String , String > ( curLanguageData [ LANGDATA_TRUENAMEUTTERANCETXT ] , curLanguageData [ LANGDATA_UTTERANCETXT ] ) ,
levelLists ) ) ;
}
// Do invocations
if ( invocList ! = null ) {
// Map of level numbers to maps of spell names to html links
TreeMap < Integer , TreeMap < String , SpellEntry > > levelLists = new TreeMap < Integer , TreeMap < String , SpellEntry > > ( ) ;
SpellEntry invocation = null ;
int level ;
for ( int i = 0 ; i < invocList . getEntryCount ( ) ; i + + ) {
// Skip rows that do not define a power
if ( invocList . getEntry ( " Level " , i ) . equals ( " **** " ) )
continue ;
// Make sure the Level entry is a number
try {
level = Integer . parseInt ( invocList . getEntry ( " Level " , i ) ) ;
} catch ( NumberFormatException e ) {
if ( tolErr ) {
err_pr . println ( " Error: Invalid Level entry in " + invocList . getName ( ) + " on row " + i + " : " + invocList . getEntry ( " Level " , i ) ) ;
continue ;
} else
throw new PageGenerationException ( " Invalid Level entry in " + invocList . getName ( ) + " on row " + i + " : " + invocList . getEntry ( " Level " , i ) ) ;
}
// Make sure the SpellID is valid
invocation = null ;
try {
// Look in spells.2da for name
invocation = spells . get ( invMap . get ( tlk . get ( spells2da . getEntry ( " Name " , invocList . getEntry ( " RealSpellID " , i ) ) ) ) ) ;
} catch ( NumberFormatException e ) {
if ( tolErr ) {
err_pr . println ( " Error: Invalid Name entry in " + invocList . getName ( ) + " on row " + i + " : " + invocList . getEntry ( " Name " , i ) ) ;
continue ;
} else
throw new PageGenerationException ( " Invalid Name entry in " + invocList . getName ( ) + " on row " + i + " : " + invocList . getEntry ( " Name " , i ) ) ;
}
if ( invocation = = null ) {
if ( tolErr ) {
err_pr . println ( " Error: Unable to map Name entry in " + invocList . getName ( ) + " on row " + i + " to a spellEntry: " + tlk . get ( invocList . getEntry ( " Name " , i ) ) ) ;
continue ;
} else
throw new PageGenerationException ( " Unable to map Name entry in " + invocList . getName ( ) + " on row " + i + " to a spellEntry: " + tlk . get ( invocList . getEntry ( " Name " , i ) ) ) ;
}
// If no map for this level yet, fill it in
if ( ! levelLists . containsKey ( level ) )
levelLists . put ( level , new TreeMap < String , SpellEntry > ( ) ) ;
// Add the spell to the map
levelLists . get ( level )
. put ( invocation . name , invocation ) ;
}
toReturn . add ( new Tuple < Tuple < String , String > , TreeMap < Integer , TreeMap < String , SpellEntry > > > (
new Tuple < String , String > ( curLanguageData [ LANGDATA_INVOCATIONTXT ] , curLanguageData [ LANGDATA_INVOCATIONTXT ] ) ,
levelLists ) ) ;
}
// Do maneuvers
if ( maneuList ! = null ) {
// Map of level numbers to maps of spell names to html links
TreeMap < Integer , TreeMap < String , SpellEntry > > levelLists = new TreeMap < Integer , TreeMap < String , SpellEntry > > ( ) ;
SpellEntry maneuver = null ;
int level ;
for ( int i = 0 ; i < maneuList . getEntryCount ( ) ; i + + ) {
// Skip rows that do not define a power
if ( maneuList . getEntry ( " Level " , i ) . equals ( " **** " ) )
continue ;
// Make sure the Level entry is a number
try {
level = Integer . parseInt ( maneuList . getEntry ( " Level " , i ) ) ;
} catch ( NumberFormatException e ) {
if ( tolErr ) {
err_pr . println ( " Error: Invalid Level entry in " + maneuList . getName ( ) + " on row " + i + " : " + maneuList . getEntry ( " Level " , i ) ) ;
continue ;
} else
throw new PageGenerationException ( " Invalid Level entry in " + maneuList . getName ( ) + " on row " + i + " : " + maneuList . getEntry ( " Level " , i ) ) ;
}
// Make sure the SpellID is valid
maneuver = null ;
try {
// Look in spells.2da for name
maneuver = spells . get ( maneuverMap . get ( tlk . get ( spells2da . getEntry ( " Name " , maneuList . getEntry ( " RealSpellID " , i ) ) ) ) ) ;
} catch ( NumberFormatException e ) {
if ( tolErr ) {
err_pr . println ( " Error: Invalid Name entry in " + maneuList . getName ( ) + " on row " + i + " : " + maneuList . getEntry ( " Name " , i ) ) ;
continue ;
} else
throw new PageGenerationException ( " Invalid Name entry in " + maneuList . getName ( ) + " on row " + i + " : " + maneuList . getEntry ( " Name " , i ) ) ;
}
if ( maneuver = = null ) {
if ( tolErr ) {
err_pr . println ( " Error: Unable to map Name entry in " + maneuList . getName ( ) + " on row " + i + " to a spellEntry: " + tlk . get ( maneuList . getEntry ( " Name " , i ) ) ) ;
continue ;
} else
throw new PageGenerationException ( " Unable to map Name entry in " + maneuList . getName ( ) + " on row " + i + " to a spellEntry: " + tlk . get ( maneuList . getEntry ( " Name " , i ) ) ) ;
}
// If no map for this level yet, fill it in
if ( ! levelLists . containsKey ( level ) )
levelLists . put ( level , new TreeMap < String , SpellEntry > ( ) ) ;
// Add the spell to the map
levelLists . get ( level )
. put ( maneuver . name , maneuver ) ;
}
toReturn . add ( new Tuple < Tuple < String , String > , TreeMap < Integer , TreeMap < String , SpellEntry > > > (
new Tuple < String , String > ( curLanguageData [ LANGDATA_MANEUVERTXT ] , curLanguageData [ LANGDATA_MANEUVERTXT ] ) ,
levelLists ) ) ;
}
return toReturn ;
}
}