Snake's Pit of ASM (and Other Stuff)

Debug Character Viewer

How’s a QoL debug ASM C hack sound today?
Avenir
Loop through character IDs, viewing their default classes and potential classes from the promotion branch table! Also includes the option to try to read from the triple promotion hack list. This is intended to make debugging animations and palettes a whole lot easier.
Down press increments the character ID, and up press decrements. (There’s an optional MaxCharacterID definition to prevent reading bad character data.)
Right press first loops through usable weapon animations (including the unarmed animation), then tries to read other potential classes.
B press ends the viewer. (I know that there’s weirdness with the active unit upon return, but don’t- don’t worry 'bout it.)
This is what I added to my unit menu:

MenuCommand(ClassDisplayNameText, 0, GreenText, 0x4F449, ClassDisplayUMEffect)

Get the hack from the link in the OP. Just add your unit menu entry and #include ClassDisplay.event. I’ve had a weird things happen with the platform not sliding in on VBA, but it seems to work fine on mGBA for me.

EDIT: Oops mixed up MaxClassID with MaxCharacterID. Definition has been changed to be MaxCharacterID as it should be since we’re iterating through characters. Sorry about that.

Have a good day!

20 Likes

I found a minor bug with the Base Conversations patch.

I have set the opening event like this:

BeginningScene:
-Some Events-
CALL $591FD8//Call the prep screen
SETVAL 0x2 0x35//Black BG
CALL $9EE2E8
CenterTutorialTextBox
TUTORIALTEXTBOXSTART
TEXTSHOW 0xB55//Yes/No Text
TEXTEND
-Conditional Event-
CALL $9EE2C4//Remove BG
EVBIT_T 0x7
ENDA

So, the events after the prep screen call should run after you press start in the prep screen. If you don’t check a base conversation the events work as intended:
1

But after you check a conversation the game proceeds to run the events after the prep screen over the prep screen.
2

Alternatively I could put the events in a Turn 1 event, but after the prep screen it fades to the map, and I want to avoid that.
3

And I don’t know why those random tiles appear in the black screen.

1 Like

Huh yeah I’ve only tried putting events in a turn 1 event to achieve what you’re trying to. Can I see the parameters for your specific conversation?

It happens with any conversation.

Chapter(Ch9)
	Entry
		bg(0x10)
		music(EncountersAndReunions)
		name(JugdralInheritorsTitle)
		text(JugdralInheritorsText)
		importance(1)
		eventID(0x21)
	Entry
		bg(0x0A)
		music(Sorrow)
		name(KnightVowTitle)
		text(KnightVowText)
		importance(1)
		eventID(0x22)
	Entry
		bg(0x12)
		music(OneHeart)
		name(PheraeLineageTitle)
		text(PheraeLineageText)
		importance(1)
		eventID(0x23)
	Entry
		bg(0x10)
		music(CatsAtPlay)
		name(WhateverForTheCommanderTitle)
		text(WhateverForTheCommanderText)
		importance(1)
		eventID(0x24)
1 Like

Huh I’ll look into that for you.

1 Like

Today I’m proud to release something I’ve been teasing unreleased for far too long out of fear of it being buggy, but today I’m confident enough for a casual release.

TableManager

TableManager.exe is a new CSV assembler for EA buildfiles. Although similar in functionality to c2ea, TableManager is more versatile and friendlier. Download the exe and source code from my link in the OP!

Features and details

The primary feature of TableManager over c2ea is being able to combine multiple “ROM tables” that are indexed together into a single CSV file. Distinguishing where everything needs to be written to is accomplished by linking Nightmare modules to CSVs similarly to c2ea.

While c2ea assumes that you want to write the data in your table into the ROM just as it appears to you, TableManager allows you to organize rows and columns as you see fit in your CSV without necessarily affecting what you write to the ROM.

  • Sort columns in any way you like! TableManager compares the string found in the top cell of a column to a field found in a Nightmare module linked to the table in order to figure out where it needs to be written to. This means that column order is completely arbitrary, and you can even mix columns from multiple “ROM tables” together in a single CSV! I think the best example of this is the magic tables with the FE8 str/mag split. Isn’t it annoying having to have separate CSVs for those? Now you can have magic data right alongside other character/class data as if it was a vanilla stat, and TableManager sorts out where you want it all to be written! You may also leave empty columns.
  • Sort rows in any way you like! Or not if you want that! TableManager allows for writing each ow sequentially to ROM just like c2ea does, but it also offers a feature that treats the far left cell of any row to act as an index that you want that row to represent. This can be a value or an EA definition. With this option turned on, you would expect each row to be written with an ORG TableLabel + (FarLeftCellOfRow)*(EntrySize) format. The ultimate implications of this are that you don’t need to know the values of custom indices to work with them in CSVs, and you can reorder rows in any way you like! You may also leave empty rows.

Run the .exe with the -h flag for more info on command line parameters.
To use, you must pass in a table options file and an intended output file like so:

echo: | (TableManager.exe TableOptions.s TableInstaller.event --error_on_missing)

This is the command I use in my batch to assemble my CSVs. (The extra flag is something I like to use. Again use -h in the command line for more flags.)

So what is this table options file I refer to?

This file is a file that TableManager reads in order to understand how you want each CSV to behave. Here’s how it works.

  • Each CSV that you want to be assembled needs to be referenced by a CSV (FilepathToCSV) header.
  • Within each CSV header, you may link an arbitrary number of Nightmare modules with TABLE (LabelForThisROMTable) (FilepathToNMM) (OptionalAddingOffset) (Optional INLINE or FIXED to override the CSV INLINE setting for this TABLE only). The optional adding offset is to allow tables that are indexed together but with an offset to be combined into a single CSV file. This is most specifically included for the item usability/effect/target switch case tables. For example, I use TABLE ItemUsabilityTable Items/ItemUsabilityTable.nmm -0x4B for the item usability table because each index in the ItemUsabilityTable is 0x4B behind the ItemTable.
  • State whether you want this table to be written to freespace or not with INLINE (True or False) If False, the assembler will try to write to the vanilla offset in the Nightmare module.
  • For behavior in writing all rows sequentially ignoring the far left column, use WRITE_SEQUENTIALLY (True or False). If not writing sequentially, 0-space will be allocated using FILL in EA for the entire table, and entries will be filled in as the CSV describes them.
  • MAX_ENTRIES (MaxNumEntries) declares how much space you want to allocate for a table if you’re not writing sequentially. This is ignored if sequential writing is enabled.

Check the .py source code for a more detailed explanation on what each code does.

A few other notes:

  • TableManager supports negative numbers!
  • TableManager also supports data sizes that are not 1 byte, 2 bytes, or 4 bytes. Simply put the size of your data as you like in the NMM field, and TableManager will handle it. Note that this data is still little-endianed!
  • If TableManager doesn’t think you’re trying to write data to some offset (say you never reference ItemTable+0x35 in your NMM. This is unknown data off the top of my head I think), 0 will be written. The previous feature was included in order for easier writing of unknown data of irregular length.
  • TableManager has no automatic repointing feature. I recommend using my Repointers helper file here.
  • A cell left blank is assumed to be 0. TableManager will attempt to not write data and allow FILL to 0 out data if it thinks that 0 will be written for an entire row if it exists. This is done for the item usability/effect/target/etc tables being weird and any other tables with an adding offset.
  • Any table with an adding offset has an innate ASSERT when writing each row to ensure that every index that’s being written to exists. For example, if you try to give an iron sword (item ID 0 or 1 or something?) an item usability pointer, EA will give you an error because this assertion will fail because that entry in that switch table doesn’t exist. You probably don’t need to worry about this, and it should never cause problems for you. It’s just a failsafe.
  • For each cell in the far left column, only the first “chunk” of data when separating by whitespace is read. This means if I have 0x0A Manakete in the left cell of my row, TableManager reads this as 0x0A. This can be used to have sorts of comments on what a row is if you’d like to use hex indices.
  • In the table options file, make comments with @.

Rather than have BORING examples here, I invite you to check out working examples of TableManager in my Avenir Github: https://github.com/Snakey11/Legends-of-Avenir/tree/master/CSV/Tables

I don’t pretend to be a good Python programmer, so I probably made plenty of mistakes. I thiiiink I handled most errors I would expect, but please please let me know if you encounter weird traceback errors, if you see unintended behavior, if you have suggestions, or if something was confusing!

Have a happy Halloween! :skull:

11 Likes

Made a quick QoL hack for dialogue for all you avatar hacks out there. :fist:

Gendered Text

This hack lets you add some text control codes (definable in textprocess definitions) that inject gendered pronouns into text (i.e. He, She, Him, Her, His, Hers, he, she, him, her, his, hers). Each control code is associated with a pronoun pair, and an event ID is used to determine which to inject. (I kinda hijacked how [Tact] works.)

This isn’t entirely necessary for doing this since you could just have separate text IDs for different cases or use TEXTSHOW2 shenanigans but ew. This should be cleaner and quicker.

This isn’t limited to gendered pronouns really. I intentionally made the system modular for more general use.

Download from the link in the OP!
Lemme know if you encounter any issues. I don’t think this should conflict with anything.

12 Likes

By request, I’ve updated TableManager to be able to handle specific vanilla tables being considered inline or fixed, overriding the pattern of the CSV declaration. For example, say you wanted your vanilla character tables to be written to their original offsets but your custom character-indexed tables to be written to free space. You could do the following:

CSV Characters/CharacterTable.csv @ Things indexed by character.
	TABLE CharacterTable Characters/CharacterTable.nmm
	TABLE MagCharTable Characters/CharacterMagicTable.nmm INLINE
	TABLE CharLevelUpSkillTable Characters/CharacterLevelUpTable.nmm INLINE
	TABLE PersonalSkillTable Characters/PersonalSkillTable.nmm INLINE
	TABLE EscapeQuoteTable Characters/EscapeQuoteTable.nmm INLINE
	INLINE False @ The default inlining is false here.
	WRITE_SEQUENTIALLY False
	MAX_ENTRIES 256

You can accomplish the exact same thing with:

CSV Characters/CharacterTable.csv @ Things indexed by character.
	TABLE CharacterTable Characters/CharacterTable.nmm FIXED
	TABLE MagCharTable Characters/CharacterMagicTable.nmm
	TABLE CharLevelUpSkillTable Characters/CharacterLevelUpTable.nmm
	TABLE PersonalSkillTable Characters/PersonalSkillTable.nmm
	TABLE EscapeQuoteTable Characters/EscapeQuoteTable.nmm
	INLINE True @ The default here is inline.
	WRITE_SEQUENTIALLY False
	MAX_ENTRIES 256

So to use this new functionality, just optionally append either INLINE or FIXED to make an exception to the CSV INLINE setting. I’m updating the original post’s documentation, and let me know if there are any issues!

4 Likes

I’ve been sitting on this for way too long, but I’m finally putting a little time to releasing it.

Support Rework Rework

That’s right. My own Support Rework from about 2 years ago is such a mess of me sneezing asm code around that I felt it necessary to rewrite it in C. While the older version was a little buggy, the new version has been behaving nicely in my own working project. Anyway, I’m proud to finally release it.

Features include all from the original version plus:

  • EIGHT unique support levels. That’s right. Eight. Should anyone use eight support levels? I don’t know I’m gonna wizard’s creed my way out of that one. If you don’t want that many you can have fewer. (I only use three lmao)
  • Enhanced stat screen drawing. I’ve gotten better at custom drawing routines, and I’ve made the stat screen look nicer. This also includes @HyperGammaSpaces’s chadly support R text system which looks GREAT. Thank you, Gamma, for developing that!
  • This is a C hack! Old support rework was all written in thumb, making it pretty error prone. This source should look much cleaner.
  • The hack is fairly separate from base conversations now. They work well together, and if you want base conversations, use the version that comes with Support Rework Rework. Otherwise, no sweat!
  • The utility macros (for modifying unit support data via events) should be easier to use now.
  • There is support for the Skill System talk bubble to appear if there is a viewable support convo in a chapter.
  • Probably more stuff? tbh I “finished” this a while ago and just now touched it up for release. Shoutouts to @Alaric for nudging me to release this!
I suppose that the instructions for using this hack will be similar to before, but I'll reiterate them

So you wanna use Support Rework Rework. Great! These instructions are going to be long not because the system should be difficult to use but moreso because I want to provide as much documentation on how to use it as I can.
You’re going to want to download the entire Support Rework Rework folder from my link in the OP and include SupportRework.event in your buildfile.
The first thing that this file does is include SupportReworkDefinitions.event, a file that defines various macros and such. Some of these macros are used in events to control support data. If necessary depending on the layout of your buildfile, you can include this file earlier in your buildfile.
This definitions file defines values for your various support levels. The example levels given are

#define CSupport 0
#define CPSupport 1
#define BMSupport 2
#define BSupport 3
#define BPSupport 4
#define AMSupport 5
#define ASupport 6
#define APSupport 7

representing C support, C+ support, B- support, etcetera. Change these as you see fit. These are just silly examples. Just make the IDs between 0 and MaxSupportLevelVal inclusive and unique. Speaking of MaxSupportLevelVal, define that to be your highest support level. I’ll get to the rest of the things in this file later. You don’t need to touch them.
Back in SupportRework.event, the SupportLevelNameTable is a table indexed by support level (remember the definitions we just discussed?) with each entry as a pointer to a string that represents that support level. You shouldn’t need to touch anything else in this file.

I’ll talk about externalish hacks next.
Firstly, Support Rework Rework supports easy integration with Base Convos. Refer to my post about that hack for instructions on how to use it. SupportReworkDefinitions.event defines a single additional base conversation macro, baseSupport(Character1,Character2,Level), where the first two parameters are the character IDs of the two supporters, and Level is the support level the conversation should try to raise to. If the current support level is not one less than the current level (or nonexistent if the level to raise to is 0), then the conversation will not be viewable. If you DON’T want to use base convos, that’s perfectly fine. Just comment out the #include "CBaseConvos/BaseConvos.event" line in SupportRework.event.
The next external hack that’s relevant is my prep screen hack which makes editing the prep screen main menu a lot less painful. I discuss what needs to be done there in the base conversation hack post. (It’s just incorporating a CSV and making an edit iirc). This is only relevant if you are using base conversations. Otherwise just ignore this.
Finally, a small hack to the CHARASM event handler is included that makes CharacterBasedEvents-based support conversations work. It just makes it such that a character event usability routine returning 2 makes the conversation usable without making any other checks.

Support Rework Rework supports support-by-support defining of stat bonuses just like the older version but hopefully in a cleaner way. You’ll define the values you want in SupportReworkBonusTable.event.
This is a (nonindexed) list of support entries. Start an entry with the entry(char1,char2) macro, then fill in your support level bonuses with the support(level,... stats) macro. There are a couple examples in the file to start you off.

Uh what else… all that’s left is controlling supports via events I think.
Okay there are three ways to control support data:

  • Adding base conversations to prep screens to allow players to increase supports there
  • Adding “talk conversation-like” support conversations to allow players to increase support levels mid-chapter
  • Manually changing support data via events

For base conversations, you can simply use the aforementioned baseSupport macro, and a default event will be used. This is the simplest way to do this. If you’d like, you can also use a custom event for extra modularity. Writing custom base conversation events is discussed in that hack’s post. To make it a “support” convo, you can just call one of the event macros that I will discuss shortly. For the usability of the conversation to match generic support convos, use usability(SupportBaseConvoUsability|1), characters(Character1,Character2), and supportLevel(Level). You’ll also likely want textGetter(SupportBaseConvoMenuTextGetter|1). If you know what you’re doing, feel free to make your own custom alternatives to these generic functions!
Next, talk conversations. I’ve designed the system to make support conversations function just like mid-chapter talk conversations. Whenever you want the player to be able to increase a support, use one of the SupportConvo macros defined in SupportReworkDefinitions.event in CharacterBasedEvents. If you provide just a text ID for the last parameter, a generic event will be used to play the text then increase the support level. If you choose to pass in an event, it will play that event instead. This should be pretty straightforward.
Finally, you can pretty easily change support data. Macros to do so are also in SupportReworkDefinitions.event for use in event scripts with descriptions. I think these are pretty straightforward as well. I think these should work pretty well, but let me know if you hit unexpected behavior with these. IncreaseSupport and SetSupport will make a new support if one doesn’t already exist. If you try to add a support with max supports or if you try to increase a max level support, nothing should happen.

Small note: Any character ID of 0xFF is interpreted by the system as representing whoever is in the first character struct. This was useful to me, and I left it in because why not.

Oh. Last thing is stat screen stuff. I’ve hooked into the vanilla function that draws supports to… well draw the new supports. In your modular stat screen, you should just need to call that vanilla function (it should be in MSS defs) to draw supports. What I use for page 4 RText is included. (Thanks again to gamma for the support RText system!)

That should be about it I think. Let me know if anything weird happens, and have a nice day!

17 Likes

Thank you so much Snakey for your amazing ASM hacks!

1 Like

I’m gonna release a tool that I forgot to release.

Don’t like manually defining arbitrary custom IDs? Just want your definitions to be sequential and unique? No? Yes? Don’t care? Then put your hands together for :drum::drum::drum::drum::drum:

Enumerate

This is a small tool that takes lists of strings that you want to be defined and gives them all unique, sequential IDs. I find this especially useful for defining a lot of custom items, classes, characters, animations, etc where I really don’t care what the ID is, but I do care that the ID isn’t shared with some other item, class, character, animation, etc.

This tool should be easy to use I hope

This was written in Python, and as always get it from the link in the OP.

For an example on how I abuse extensively use this script, feel free to check out my working project github: https://github.com/Snakey11/Legends-of-Avenir/tree/master/Definitions

This was designed to play nicely with TableManager, my CSV assembler, which doesn’t care what IDs actually are. What I find quite nice to do (I’m totally not biased I didn’t create this system or anything) is let Enumerate hand out arbitrary IDs to all my shit, have TableManager work exclusively with definitions, then use definitions elsewhere. I think this is a much cleaner method than manually defining IDs personally.

The command line arguments for this are:

enumerate (inputfile) (outputfile) (optional -c flag to delete output flag on error)

The input file I’ll get to in a sec. The output file will contain a huge list of #define YourDefinition 0xDefinitionIDs that you can simply include.
Now the input file is a file where you list things you want to be definitions.

I'll just give a straightforward example as to how it should look.
Jasper 0x01
Derek
Val
Helmer
Baret
Walter
Vicar

This inputted will output

#define Jasper 0x01
#define Derek 0x02
#define Val 0x03
#define Helmer 0x04
#define Baret 0x05
#define Walter 0x06
#define Vicar 0x07

That’s the basic idea.

You can restart numbering like
Jasper 0x01
Derek
Val
Helmer 0x10
Baret
Walter
Vicar

and this will produce

#define Jasper 0x01
#define Derek 0x02
#define Val 0x03
#define Helmer 0x10
#define Baret 0x11
#define Walter 0x12
#define Vicar 0x13

There are a couple other directives I’ve got for ya. You can .include files to start a new numbering system. If you .avoid value1 value2, numbering will… well avoid assigning definitions between those values inclusive.

An example with avoid:
.avoid 0x03 0x05
Jasper 0x01
Derek
Val
Helmer

This will produce

#define Jasper 0x01
#define Derek 0x02
#define Val 0x06
#define Helmer 0x07

You can line comment with @ or // just the same. If you have overlapping IDs within the same file, the program will throw an error. Whitespace lines are ignored.

I think that’s about it. It’s a small script, and I’m open to suggestions for improvements and additions.

Hope this makes someone’s life easier. Enjoy! :+1:

Fast edit: I’ve also just reorganized my opening post now that there are more links accumulating there.

6 Likes

TSA is SO SCARY like what even IS it? How do I deal with it? What do you MEAN width and height - 1? Flips? What’re those? Ew icky. Why is the palette even bitpacked in there if BgMap_ApplyTsa lets you use different palettes wtf?!?
Yeah that’s how I feel about TSA too. It’s weird and confusing and a pain unless you have…

tmx2tsa

That’s right. You can use Tiled to generate TSA now. This is a long time coming, and I’m excited to release it. It’s really easy; all you need is an image of the tilesheet you’re basing your TSA on and tmx2tsa.exe. It’s that simple.

How to: Make epic custom tile-based UI

Okay so this is an executable with the following command line signature:

tmx2tsa (input.tmx) (ouput.dmp) (optional -p palette ID, default 1 since that seems to be what most UI uses) (optional -c filepath to compress.exe if you want it to be compressed)

After running, just #incbin the dmp!

When opening a new .tmx file to start, be sure that your tile width and height are both 8.

So normally we use Tiled in hacking for chapter maps, right? The "tiles" we use for chapter maps are NOT the same as "tiles" in the general sense.
  • In general for the GBA, a tile is a square of 8x8 pixels that is rendered to tile VRAM and displayed on a BG map by tile ID. TSA is for ordering these tiles.
  • In the chapter map sense, a tile is a square of 2x2 tiles (so 16x16 pixels), so when creating chapters, your settings are 16x16 per tile.

Just be sure that you have 8x8 per tile when using this tool. I hope this clears up confusion.

Anyway, I think the only other setting that matters in here is the Orthogonal setting. Set your width and height accordingly. Each other setting is arbitrary.

Next, I’m going to assume that you have an image of the tile sheet that you want to use. If someone requests it, I can include how I get this image.

Import this tile sheet as a tileset again with 8x8 tiles. From then on, it’s just like making a chapter map! PROTIP: On my Tiled setup at least, press X and Y on your keyboard to horizontally and vertically flip the tile you’re currently placing! Another fun thing is that Tiled updates your tile sheet in real time if you’re making edits to it, so this works well for fancy custom tiles as well. Don’t leave empty space I guess… I think that’s it. Once you’re finished with your tiles, run tmx2tsa on it, and your TSA is good to go!

Wait so what exactly is TSA again?

Someone please correct me if I’m wrong in this section.
I’ve been told that TSA stands for Tile Squaroid Assembly. Weird but whatever everyone calls it TSA. It is a ROM data structure that tells the game what tiles go where. Generally that’s really it.

It seems to often be used for UI, so let’s take that as an example. Say you want to display a UI blue box. First, you would load the tile sheet that you would use in tmx2tsa to tile VRAM and load the palette. (I believe most of the time there’s a single function that does both of these for you.) Your graphics are loaded in the form of tiles, but now you need something that actually renders the tiles. That’s where TSA comes in. I have the C structure signature of TSA as

struct Tile
{
	u16 tileID : 10;
	u16 horizontalFlip : 1;
	u16 verticalFlip : 1;
	u16 paletteID : 4;
};

struct TSA
{
	u8 width, height;
	Tile tiles[];
};

The structure begins with a width byte and a height byte. The structure represents a rectangle on the screen where you want to render the tiles. The X and Y positions are completely irrelevant to the structure itself, but width and height are… but these are actually width-1 and height-1. Don’t ask me why. The rest of the structure is an array of tiles (each tile is a short). The tiles run from left to right and bottom to top. Again don’t ask me why. Each tile is bitpacked where the bottom 10 bits refer to the tile ID, the next bit is for horizontal flip, the next is for vertical flip, then the top 4 bits are for palette ID.

See how this could be a headache to deal with?

You would then call BgMap_ApplyTsa to refer to your TSA and set your tile references to the BG map. (Actually you’d almost certainly set your tile references to the BG map buffer then have to enable BG sync but whatever.)

I’ve most recently made a little bit of custom UI for world map things. If you’d like an example on how I use it, I invite you to check there. Here’s what I was able to whip up for an edit to the world map text box!

Hope to see a bunch of new custom TSA out there! Have a nice day :wave:

14 Likes

I’ll believe it when I see it for myself. :stuck_out_tongue:
But seriously, this is very cool. Will massively help with UI development.

But I do have a question, and feel free to correct me if I’m being obtuse and didn’t see it somewhere. But does this tool generate compressed TSA, or uncompressed TSA?

I’m glad you asked. It does both! Without the -c parameter, the executable will output uncompressed TSA. If you do pass -c file/path/to/compress.exe, the output will be lz77 compressed.

3 Likes

Predictor

It’s a buildfile-friendly average statter. This program will take data straight from your class and character CSVs and predict a unit’s stats at a certain class and level.

Here are some example screenshots of the script in action


This is running a “wrapper” batch script that actually calls the executable. I’m running from the command line, but you can also double click the batch script just the same.
The program will prompt you for some input information and output the average stats.

The prompting should be pretty user friendly I hope.

Plenty of extra options to play with including handling promotions.

You can also have the program run a number of simulations instead of generating average stats. The program will report the average stats of all simulated units and report standard deviations, letting you know how much certain stats on individual units vary!

You can also use argparse flags to skip the prompt. If you don’t include necessary parameters, you’ll be prompted for them. Use ./predict -h to see these flags!

How to: Actually know how your units will turn out instead of just sneezing stats around

As always, get everything you need from my download link in the OP.

Like I said, the program reads data straight from your class and character CSVs, so it needs some way to be able to know where your CSVs are and what columns and rows to grab stats from. This is performed through an options file which requires one-time setup.
Notice that I’ve been using a wrapper batch script instead of directly calling the executable. That’s because the first parameter of the executable is the filepath to this options file. The batch file then passes in its own parameters into the program.

The default filename for the options file is (in the same directory as the batch script and program) PredictorOptions.s. This can be changed in the batch script. With this options file, whitespace is ignored, and you can line comment with @. Your lines should look like the following:

Filepath/To/CharacterTable.csv
Filepath/To/ClassTable.csv
Stat,Names,For,Reporting,Separated,By,Commas
Character,Bases,Columns,Names
Character,Growths,Columns,Names
BaseLevelColumnNameForCharacter
Class,Bases,Columns,Names
Class,Growths,Columns,Names
Class,Maxes,Columns,Names
Class,Promotion,Bonuses,Columns,Names

A column name of just 0 will represent a column of all 0 data! This is useful… for example… how characters have no movement stats? Use ‘0’ for the character movement base and growth column names in that case. The order of the stats is arbitrary but should be kept consistent.
An example options file is included.

Now you can use the script. Here are the option flags (and how to view them):


There are only three required inputs: The character name, the starting class, and the target level. The character’s starting level field in the character table is used for starting level. If you do not supply these required inputs, you’ll be prompted.
Any character and class IDs are searched by row name. If I want to refer to Jasper and Cavalier, I can do this only because my row names for that character and that class are exactly Jasper and Cavalier. This is designed to play nicely with TableManager.
The rest of the parameters are optional (and if you are prompted for any required parameters, you’ll be prompted for these as well).
--TARGET_CLASS (class name) indicates that you want to promote to the defined class. Set a promotion level with --PROMOTION_LEVEL (level). Default is 10.
--TARGET_LEVEL (level) is the ending simulation level (promoted if a promotion is desired).
If --SIMULATION_COUNT (count) is not defined, average statting as you’d expect will be used. If it IS defined, then the defined number of simulated units will be generated, and their average stats will be displayed along with standard deviations for each stat.
--round rounds average stats.
If --generic is used, then the unit is treated as generic. Class growths are used instead of character growths.
--table_help outputs help for the table options file, and --verbose is really for debug.

I’ve written this mostly for myself, and this is how I would want my own tool to behave. I’m open to feedback and requests, though. I hope someone makes good use of this. Have a nice day!

Edit: Thanks to @Huichelaar for reporting: I’ve also fixed a bug in TableManager associated with improper output with certain options.

7 Likes

Okay one more thing and I’ll stop spamming my own thread. :stuck_out_tongue:

Before I’d been using a single Google Drive folder to contain all of my shit, but this time with tmx2tsa's release, there’d been a problem. Google thought it was a virus! (Bold of Google to figure I’d be able to write malicious code :roll_eyes:) I’m honestly not sure why this script was stopped and not any others? tmx2tsa is by far my shortest piece of software, and all of the other .exes (as far as I know) uploaded fine.

I didn’t see a solution that involved sticking with Google (lmao the request staff revision link was a 404), so I’ve switched to Github. The new repository link is in the OP, so go ahead and use that for now on. I’ll be scouring the thread and removing any links elsewhere as well.

I didn’t and don’t intend to embed viruses in my few .exes that I’ve released. Use them, and they won’t hurt your computer. If you’re not comfortable with that, it’s perfectly okay. I include the source, and you can run the script or compile it yourself if you like.

tldr Google is mean to me :pensive: Have a nice day!

8 Likes

2 Likes

Would it be possible to define a custom terminator to lists using TableManager? I’ve run into lists which put values in fields --which are otherwise 0 in regular entries-- in their vanilla terminators.

Of course it’s possible to specify these fields in the nightmare module. In the csv file, one could leave them as 0 for all entries, and put in the terminator value (often 0xFF or something similar) for the terminator. It just fills the spreadsheet with not so interesting data.

1 Like

That’s a good idea! What do you think a good format would be on the user side for declaring what the terminator should be for relevant lists?

Bit ugly, but maybe something like the last line in this?

CSV Character/BattleQuotes.csv
  TABLE BattleQuoteTableLABEL Character/BattleQuotes.nmm
  INLINE True
  WRITE_SEQUENTIALLY True
  MAX_ENTRIES 256
  TERMINATOR FFFF0000000000000000000000000000

Where each two hex digits represents a byte. It’d be nice to have full control over what the terminator looks like, because vanilla terminators can differ.

1 Like