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);
|
||
|
}
|
||
|
}
|