PRC8/nwn/nwnprc/DocGen/trunk/prc/utils/ScrollGen.java
Jaysyn904 5914ed2ab5 Updated Release Archive
Updated Release Archive.  Fixed Mage-killer prereqs.  Removed old LETO & ConvoCC related files.  Added organized spell scroll store.  Fixed Gloura spellbook. Various TLK fixes.  Reorganized Repo.  Removed invalid user folders. Added DocGen back in.
2023-08-22 10:00:21 -04:00

340 lines
16 KiB
Java

package prc.utils;
import prc.autodoc.*;
import prc.autodoc.Main.TwoDAStore;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import static prc.Main.err_pr;
import static prc.Main.verbose;
/**
* Creates scrolls based on iprp_spells.2da and spells.2da.
*
* @author Ornedan
*/
public class ScrollGen {
/**
* Ye olde maine methode.
*
* @param args The arguments
* @throws IOException Just toss any exceptions encountered
*/
public static void main(String[] args) throws IOException {
if (args.length == 0) readMe();
String twoDAPath = null;
String outPath = null;
// parse args
for (String param : args) {//2dadir outdir | [--help]
// Parameter parseage
if (param.startsWith("-")) {
if (param.equals("--help")) readMe();
else {
for (char c : param.substring(1).toCharArray()) {
switch (c) {
default:
System.out.println("Unknown parameter: " + c);
readMe();
}
}
}
} else {
// It's a pathname
if (twoDAPath == null)
twoDAPath = param;
else if (outPath == null)
outPath = param;
else {
System.out.println("Unknown parameter: " + param);
readMe();
}
}
}
// Load data
TwoDAStore twoDA = new TwoDAStore(twoDAPath);
doScrollGen(twoDA, twoDAPath, outPath);
}
/**
* Performs the actual scroll generation. Made public for the purposes of BuildScrollHack.
*
* @param twoDA A TwoDAStore for loading 2da data from
* @param twoDAPath Path where the 2da files are located. For resaving
* @param outPath Path to directory to store the xml files in
* @throws IOException Just tossed back up
*/
public static void doScrollGen(TwoDAStore twoDA, String twoDAPath, String outPath) throws IOException {
Data_2da spells = twoDA.get("spells"),
des_crft_scroll = twoDA.get("des_crft_scroll"),
des_crft_spells = twoDA.get("des_crft_spells"),
iprp_spells = twoDA.get("iprp_spells");
// For each spells.2da entry, find the iprp_spells.2da entry with lowest CasterLvl value
Map<Integer, Tuple<Integer, Integer>> lowestIndex = new HashMap<Integer, Tuple<Integer, Integer>>(); // Map of spells.2da index -> (iprp_spells.2da index, CasterLvl value)
int spellID, casterLvl;
for (int i = 0; i < iprp_spells.getEntryCount(); i++) {
if (!iprp_spells.getEntry("SpellIndex", i).equals("****")) { // Only lines that are connected to spells.2da are scanned
spellID = Integer.parseInt(iprp_spells.getEntry("SpellIndex", i));
casterLvl = Integer.parseInt(iprp_spells.getEntry("CasterLvl", i));
if (lowestIndex.get(spellID) == null || lowestIndex.get(spellID).e2 > casterLvl)
lowestIndex.put(spellID, new Tuple<Integer, Integer>(i, casterLvl));
}
}
/// For each scroll to make, find which spellcasting classes should be able to use that scroll
// Map of spells.2da index -> (iprp_spells.2da index, list of classes.2da index)
Map<Integer, Tuple<Integer, Set<Integer>>> scrolls = new HashMap<Integer, Tuple<Integer, Set<Integer>>>();
for (int spellsIndex : lowestIndex.keySet()) {
int iprpIndex = lowestIndex.get(spellsIndex).e1;
Set<Integer> classList = getClassesForSpell(twoDA, spellsIndex).keySet();
if (classList.size() > 0)
scrolls.put(spellsIndex, new Tuple<Integer, Set<Integer>>(iprpIndex, classList));
}
// Do the scrolls
for (int spellsIndex : scrolls.keySet()) {
String scrollName = "prc_scr_" + scrolls.get(spellsIndex).e1.toString();
String scrollXml = doScroll(spells, scrollName, spellsIndex, scrolls.get(spellsIndex).e1, scrolls.get(spellsIndex).e2);
// Print the scroll
printScroll(outPath, scrollName, scrollXml);
// Update des_crft_scrolls accordingly
Set<Integer> classList = scrolls.get(spellsIndex).e2;
if (classList.contains(1))
setScroll(des_crft_scroll, spells, spellsIndex, "Bard", scrollName);
if (classList.contains(9) || classList.contains(10))
setScroll(des_crft_scroll, spells, spellsIndex, "Wiz_Sorc", scrollName);
if (classList.contains(2))
setScroll(des_crft_scroll, spells, spellsIndex, "Cleric", scrollName);
if (classList.contains(3))
setScroll(des_crft_scroll, spells, spellsIndex, "Druid", scrollName);
if (classList.contains(6))
setScroll(des_crft_scroll, spells, spellsIndex, "Paladin", scrollName);
if (classList.contains(7))
setScroll(des_crft_scroll, spells, spellsIndex, "Ranger", scrollName);
}
// Save updated des_crft_scrolls.2da
des_crft_scroll.save2da(twoDAPath, true, true);
}
public static Map<Integer, Integer> getClassesForSpell(TwoDAStore twoDA, int spellsIndex)
{
Data_2da spells = twoDA.get("spells");
Map<Integer, Integer> classList = new TreeMap<Integer, Integer>();
// BAD
// Hardcoded classes.2da indexes
String bardSpell = spells.getEntry("Bard", spellsIndex);
String clericSpell = spells.getEntry("Cleric", spellsIndex);
String druidSpell = spells.getEntry("Druid", spellsIndex);
String paladinSpell = spells.getEntry("Paladin", spellsIndex);
String rangerSpell = spells.getEntry("Ranger", spellsIndex);
String wizSpell = spells.getEntry("Wiz_Sorc", spellsIndex);
if (!bardSpell.equals("****"))
classList.put(1, Integer.parseInt(bardSpell));
if (!clericSpell.equals("****"))
classList.put(2, Integer.parseInt(clericSpell));
if (!druidSpell.equals("****"))
classList.put(3, Integer.parseInt(druidSpell));
if (!paladinSpell.equals("****"))
classList.put(6, Integer.parseInt(paladinSpell));
if (!rangerSpell.equals("****"))
classList.put(7, Integer.parseInt(rangerSpell));
if (!wizSpell.equals("****")) {
classList.put(9, Integer.parseInt(wizSpell));
classList.put(10, Integer.parseInt(wizSpell));
}
/// For each spell, find which new spellbooks have that spell
// Map of spells.2da index -> (map of classes.2da index -> spell level)
Map<Integer, Map<Integer, Integer>> newSpellBooks = getNewSpellbooksClasses(twoDA, spells);
// New spellbooks
Map<Integer, Integer> newSpellBookEntries = newSpellBooks.get(spellsIndex);
if (newSpellBookEntries != null)
for(Integer classId : newSpellBookEntries.keySet()) {
classList.put(classId, newSpellBookEntries.get(classId));
}
return classList;
}
private static Map<Integer, Map<Integer, Integer>> newSpellbookCache = null;
private static Map<Integer, Map<Integer, Integer>> getNewSpellbooksClasses(TwoDAStore twoDA, Data_2da spells) {
if (newSpellbookCache != null) return newSpellbookCache;
// Map of spells.2da index -> list of classes.2da index
HashMap<Integer, Map<Integer, Integer>> toReturn = new HashMap<Integer, Map<Integer, Integer>>();
String classAbrev = null;
Data_2da classes = twoDA.get("classes"),
spellList = null;
Integer spellId;
for (int classId = 0; classId < classes.getEntryCount(); classId++) {
if (!classes.getEntry("PlayerClass", classId).equals("1")) continue;
// Extract the class abbreviation
classAbrev = classes.getEntry("FeatsTable", classId).toLowerCase().substring(9);
// Attempt to load the spellbook 2da
try {
spellList = twoDA.get("cls_spcr_" + classAbrev);
} catch (TwoDAReadException e) {
continue;
}
for (int i = 0; i < spellList.getEntryCount(); i++) {
String spellEntry = spellList.getEntry("SpellID", i);
String spellLevelEntry = spellList.getEntry("Level", i);
if (!spellEntry.equals("****")) {
spellId = Integer.parseInt(spellEntry);
int spellLevel = Integer.parseInt(spellLevelEntry);
if (toReturn.get(spellId) == null) {
toReturn.put(spellId, new TreeMap<Integer, Integer>());
}
toReturn.get(spellId).put(classId, spellLevel);
}
}
}
newSpellbookCache = toReturn;
return toReturn;
}
private static void setScroll(Data_2da des_crft_scroll, Data_2da spells, int spellsIndex, String column, String scrollName) {
// Set the main entry
des_crft_scroll.setEntry(column, spellsIndex, scrollName);
// Set each subradial's entry
for (int i = 1; i <= 5; i++)
if (!spells.getEntry("SubRadSpell" + i, spellsIndex).equals("****")) {
des_crft_scroll.setEntry(column, Integer.parseInt(spells.getEntry("SubRadSpell" + i, spellsIndex)), scrollName);
}
}
private static final String xmlPrefix =
"<gff name=\"~~~Name~~~.uti\" type=\"UTI \" version=\"V3.2\" >" + "\n" +
" <struct id=\"-1\" >" + "\n" +
" <element name=\"TemplateResRef\" type=\"11\" value=\"~~~Name~~~\" />" + "\n" +
" <element name=\"BaseItem\" type=\"5\" value=\"75\" />" + "\n" +
" <element name=\"LocalizedName\" type=\"12\" value=\"~~~TLKName~~~\" />" + "\n" +
" <element name=\"Description\" type=\"12\" value=\"-1\" />" + "\n" +
" <element name=\"DescIdentified\" type=\"12\" value=\"~~~TLKDesc~~~\" />" + "\n" +
" <element name=\"Tag\" type=\"10\" value=\"~~~Tag~~~\" />" + "\n" +
" <element name=\"Charges\" type=\"0\" value=\"0\" />" + "\n" +
" <element name=\"Cost\" type=\"4\" value=\"0\" />" + "\n" +
" <element name=\"Stolen\" type=\"0\" value=\"0\" />" + "\n" +
" <element name=\"StackSize\" type=\"2\" value=\"1\" />" + "\n" +
" <element name=\"Plot\" type=\"0\" value=\"0\" />" + "\n" +
" <element name=\"AddCost\" type=\"4\" value=\"0\" />" + "\n" +
" <element name=\"Identified\" type=\"0\" value=\"1\" />" + "\n" +
" <element name=\"Cursed\" type=\"0\" value=\"0\" />" + "\n" +
" <element name=\"ModelPart1\" type=\"0\" value=\"1\" />" + "\n" +
" <element name=\"PropertiesList\" type=\"15\" >" + "\n" +
" <struct id=\"0\" >" + "\n" +
" <element name=\"PropertyName\" type=\"2\" value=\"15\" />" + "\n" +
" <element name=\"Subtype\" type=\"2\" value=\"~~~IPIndex~~~\" />" + "\n" +
" <element name=\"CostTable\" type=\"0\" value=\"3\" />" + "\n" +
" <element name=\"CostValue\" type=\"2\" value=\"1\" />" + "\n" +
" <element name=\"Param1\" type=\"0\" value=\"255\" />" + "\n" +
" <element name=\"Param1Value\" type=\"0\" value=\"0\" />" + "\n" +
" <element name=\"ChanceAppear\" type=\"0\" value=\"100\" />" + "\n" +
" </struct>" + "\n";
private static final String xmlClass =
" <struct id=\"0\" >" + "\n" +
" <element name=\"PropertyName\" type=\"2\" value=\"63\" />" + "\n" +
" <element name=\"Subtype\" type=\"2\" value=\"~~~Class~~~\" />" + "\n" +
" <element name=\"CostTable\" type=\"0\" value=\"0\" />" + "\n" +
" <element name=\"CostValue\" type=\"2\" value=\"0\" />" + "\n" +
" <element name=\"Param1\" type=\"0\" value=\"255\" />" + "\n" +
" <element name=\"Param1Value\" type=\"0\" value=\"0\" />" + "\n" +
" <element name=\"ChanceAppear\" type=\"0\" value=\"100\" />" + "\n" +
" </struct>" + "\n";
private static final String xmlSuffix =
" </element>" + "\n" +
" <element name=\"PaletteID\" type=\"0\" value=\"26\" />" + "\n" +
" <element name=\"Comment\" type=\"10\" value=\"1\" />" + "\n" +
" </struct>" + "\n" +
"</gff>";
private static String doScroll(Data_2da spells, String name, int spellsIndex, int iprpIndex, Set<Integer> classes) {
// Determine TLK references
int tlkName = Integer.parseInt(spells.getEntry("Name", spellsIndex));
int tlkDesc = Integer.parseInt(spells.getEntry("SpellDesc", spellsIndex));
// Build the string
String toReturn = xmlPrefix.replaceAll("~~~Name~~~", name)
.replaceAll("~~~Tag~~~", name.toUpperCase())
.replaceAll("~~~IPIndex~~~", "" + iprpIndex)
.replaceAll("~~~TLKName~~~", "" + tlkName)
.replaceAll("~~~TLKDesc~~~", "" + tlkDesc);
for (Integer classIndex : classes)
toReturn += xmlClass.replaceAll("~~~Class~~~", classIndex.toString());
toReturn += xmlSuffix;
return toReturn;
}
private static void printScroll(String outDir, String scrollName, String scrollXml) {
String path = outDir + File.separator + scrollName + ".uti.xml";
try {
File target = new File(path);
// Clean up old version if necessary
if (target.exists()) {
if (verbose) System.out.println("Deleting previous version of " + path);
target.delete();
}
target.createNewFile();
// Creater the writer and print
FileWriter writer = new FileWriter(target, false);
writer.write(scrollXml);
// Clean up
writer.flush();
writer.close();
} catch (IOException e) {
err_pr.println("Error: IOException when printing " + path + ":\n" + e);
}
}
/**
* Prints the use instructions for this program and kills execution.
*/
private static void readMe() {
// 0 1 2 3 4 5 6 7 8
// 12345678901234567890123456789012345678901234567890123456789012345678901234567890
System.out.println("Usage:\n" +
" java -jar prc.jar scrollgen 2dadir outdir | [--help]\n" +
"\n" +
"2dadir Path to a directory containing 2da files\n" +
"outdir Path to the directory to save the new scroll xml files in\n" +
"\n" +
"--help prints this info you are reading\n" +
"\n" +
"\n" +
"A tool for automatically creating spell scrolls based on iprp_spells.2da\n" +
"and spells.2da. Generates pspeed's modpacker -compatible XML\n" +
"Also updates des_crft_scrolls with the new scroll resrefs.\n"
);
System.exit(0);
}
}