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.
340 lines
16 KiB
Java
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);
|
|
}
|
|
}
|