Show Posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.

Messages - Tirlititi

Pages: [1] 2 3 ... 24
No, the .hws wouldn't hold that change.
That's a real problem for me as well as I would largely prefer to have only 1 file for the mod, but the nature of the change is just not embeddable in a .hws... so I decided that I would distribute the C# code as well (generating the mod then requires Memoria or dnSpy in addition to HW).
Using Memoria, it's simply a .cs file to share in addition to the .hws (and then I think that you need to run Memoria's script compiler, but that's easy).

In any case, the default way to share a mod is not to share the .hws (+ maybe C# code) but to share the generated Steam files (p0data, DLL...).
Even if it creates mod compatibility problems, that's the easiest way to do it, both for the modder and the user.

Hello Clem Fandango. Sorry I didn't see your message before.

It is not possible to do that without modding the battle engine. It can be done either with Albeoris's Memoria tool or with dnSpy, but not directly with HW (or only with added difficulties for no reason). In both case, it's a bit of C# coding.

Using Memoria, you need to change the script of Phoenix Downs (inside "Scripts/Sources/Battle/0072_ItemPhoenixScript.cs"):
Code: [Select]
public void Perform()
if (!_v.Target.CanBeRevived())

if (_v.Target.IsZombie)
_v.Context.Flags |= BattleCalcFlags.Miss; // Add this line
return; // Add this line
// if ((_v.Target.CurrentHp = (UInt16)(GameRandom.Next8() % 10)) == 0) // Remove this line
// _v.Target.Kill(); // Remove this line
else if (_v.Target.CheckIsPlayer())
if (_v.Target.IsUnderStatus(BattleStatus.Death))
_v.Target.CurrentHp = (UInt16)(1 + GameRandom.Next8() % 10);


Using dnSpy, you need to change the method "btl_calc::CalcMain" and more specifically the "case 72":
Code: [Select]
if (Status.checkCurStat(target, 64u) || btl_util.CheckEnemyCategory(target, ENEMY.ENEMY_CATEGORY_UNDEAD))
calc_VAR.flags |= 1; // Add this line
// Remove the following lines, "if ((target.cur.hp = (ushort)(Comn.random8() % 10)) == 0)" and "SetEnforceHP0"
else if (btl_calc.CheckNotPlayerMiss(calc_VAR))
// etc... no change there

No it's not. If it misses, that surely means that you don't have any more ethers :o
It's not a free MP recovery.

No, sorry.

Unfortunatly, this is mainly hard-coded. You can change it using the tool dnSpy but not Hades Workshop only.
In dnSpy, open the Assembly-CSharp.dll, search for the following classes and methods and edit a few lines:

1) In the class "ModelFactory" and its method "CreateModel", there's this block:
Code: [Select]
if (ModelFactory.garnetShortHairTable.Contains(text))
ushort num = BitConverter.ToUInt16(FF9StateSystem.EventState.gEventGlobal, 0);
bool flag = num >= 10300;
if (flag)
// etc... Disable "long_hair" components
// etc... Disable "short_hair" components
Replace the "if (flag)" line by "if (flag && isBattle)".

2) In the class "EventEngine" and its method "DoEventCode" (that's a very long one), there is a "case EBin.event_code_binary.MODEL":
Code: [Select]
case EBin.event_code_binary.MODEL:
posObj.model = (ushort)this.getv2();
Obj expr_1EA4 = this.gExec;
expr_1EA4.flags |= 1;
posObj.eye = (short)(-4 * this.getv1());
if (this.gMode == 1)
string text = FF9BattleDB.GEO[(int)posObj.model];
posObj.go = ModelFactory.CreateModel(text, false);
GeoTexAnim.addTexAnim(posObj.go, text);
if (ModelFactory.garnetShortHairTable.Contains(text))
posObj.garnet = true;
ushort num7 = BitConverter.ToUInt16(FF9StateSystem.EventState.gEventGlobal, 0);
posObj.shortHair = (num7 >= 10300);
// etc...
Change the line "posObj.shortHair = (num7 >= 10300);" to "posObj.shortHair = false;".

There might be a third method to change, the method "addTexAnim" of the class "GeoTexAnim", but I am not sure if it is important or if it would mess up with the battle model.
And there would be a 4th one, "ff9play::FF9Play_GetSerialID", if you wanted to change her model in battles as well.

First: that's absolutly impossible.
If the loading bar is not showing for the World Maps, it means that HW has bugs while loading the WM datas from the game. As long as the datas required are not loaded, the .hws file is barely checked (it's only checked to see the available sections, which works fine since you see them before confirming).
So either the loading bar shows up, or you have a file modded without you knowing it (most probable). The WM datas are mainly in the p0data3.bin archive.

Second: You should be able to open HW on the Moguri Mod except for the DLL changes.
In order to do that, you need a folder with:
1) A "FF9_Launcher.exe", whichever one (even a dummy file of 0kb would work as long as it's correctly named),
2) The archive files of the Moguri Mod (all the p0dataX.bin, the "mainData", "resources.assets" and all the "sharedassetsX.assets", the "levelX" shouldn't be needed except if you want to use the Unity Assets Viewer tool),
3) The "Assembly-CSharp.dll" of the normal game, unmodded (at least not modded by Memoria or the Moguri Mod).

Put all these files in sub-folders mimicking the directory of the game, select the "FF9_Launcher.exe" with HW and it should be alright.
When saving, choose the options "Spreadsheets" + "Unity Archives": the archives of Moguri should be generated correctly and there will be .csv files that can be read by Memoria.
The p0dataX.bin should go in the Moguri Mod's folder (that's where the modded archives are), something like "C:\Program Files\Moguri Mod\StreamingAssets".
The .csv files should also go in a Moguri Mod's subfolder, like "C:\Program Files\Moguri Mod\StreamingAssets\Data\Battle\Actions.csv".
The resources/sharedassets should go in the Steam's file folder, as usual.

I hope that it helps. Tell me if I'm wrong somewhere ^^'

SV_Target does not work like that.

"set SV_Target = 1" -> Only the 1st player's character is targeted (usually Zidane).
"set SV_Target = 2" -> Only the 2nd player's character is targeted.
"set SV_Target = 4" -> Only the 3rd player's character is targeted.
"set SV_Target = 8" -> Only the 4th player's character is targeted.
"set SV_Target = 16" -> Only the 1st enemy is targeted.
"set SV_Target = 32" -> Only the 2nd enemy is targeted.
"set SV_Target = 64" -> Only the 3rd enemy is targeted.
"set SV_Target = 128" -> Only the 4th enemy is targeted.
And then, you add numbers to have multiple targets. The whole player's party is thus targeted with "set SV_Target = 15" and not 5 (if you watch more carefully, you'll see that only two characters are affected when you use 5).
However, because the number of characters/enemies may vary, it is very unusual to use the numbers directly; oftentimes, "SV_PlayerTeam" or "SV_EnemyTeam" are used instead of "15" or "240" respectively. The "NotMatching" or "Matching" rules out characters not matching some conditions (the "32" means "Reflect": again, select that number and see how it converts to the "Status List B" which can be seen on the left of the window, inside the "Battle" panel). For single-targeted spells, a "RandomInTeam" is used.

The number used as an argument for the "Attack" corresponds to the attack in the attack list. Look at what's displayed on the right.

I am the one who put the AI script on ffwikia. They are indeed a bit reworked there to make it more clear.

Firstly, the example you gave do not compare the same things. You are not in the same function or at the same point of the function.
Secondly, "VAR_LocInt24_9" is a variable but since it has no name inside the game's code, it is given an obtruse name by default. "Loc" specifies that it's a local variable, "Int24" specifies that it's a signed number using 24 bits (it can represent large numbers, unlike "Int8" that can represent only numbers from -128 to 127; that's binary computation stuff but it's not mandatory to understand it though it makes things easier).
You should totally use "File -> Open Mod -> LocalVariableSettings.hws" as it will give names to these variables according to their usage inside the AI scripts (the work is not done yet with the Field scripts).

Finally, I suggest to see first another AI script to understand how things are done. Necron's AI is not the simplest by a large margin (I mean, Necron's AI is simple but not the AI of his dummies who actually do most of the job).
I recommend looking at the AI script of Tantarian: you should be able to grab how things work and it also uses a feature shared with Necron, the "Stand Animation" alternative.

And don't hesitate to look around; there's a list of function codes available and ~60% of the screen is dedicated to give more information than the raw script text (in particular, the "257" that you spotted should be read as a "Status List A" value and means "Petrify | Death", Petrify or Death).

For the PSX version, it is always safe to open a non-modded version of the game, save mods as .hws and then overwrite binary files.

Here is how it works:
- Save Mod (.hws files) registers the modifications done with the tool since you opened the .bin file; it doesn't know anything about previous modifications you could have done before.
- Export as PPF does a comparison between the file on your hard drive and the modifications done with the tool since you opened the .bin file; it produces a patch that is only good for the file you had on your hard drive at the time you created it (hence the need to use that on non-modded versions).
- Overwrite binary files changes the file on your hard drive but also resets the modification track (if I remember correctly... it may not be the case anymore), that's why it should be used after saving a .hws, or else you'll lost the capability to update your mod after closing the tool.

Also, .hws files can be used to move your mod from a disc to another (instead of repeating the same modifications 4 times).

There can be bugs when opening a modded PSX game file and even if there is no bug by chance, the tool has no way to remember what were the modifications you did on a file before, so it won't mark some parts as modified (for exporting PPF for instance) even though they are not the vanilla (non-modded) datas anymore.

Hold everything!
I found a bug when loading HWS files containing Spell Animations. It corrupts the saves done after that.

If you loaded HWS files containing Spell Animations and then saved HWS again with the version 0.41, you must do that, using the version 0.41b:
- Open the HWS with your latest changes (saved with v0.41) without the Spell Animations (it should crash anyway if you load them),
- Open a previous HWS with Spell Animations changes (saved with <= v040b) if you have a backup or do the Spell Animations changes again.
- Save as HWS; that's fine.

If you don't have a backup, you can also send me your (corrupted) HWS file and I will fix it.

Sorry for the troubles...

Update to v0.41:
- Added a Randomizer tool, including the slightly modified features of Meteor On FFIX (which was only working on the PSX US version) and a way to randomize the enemies' spells (unfairness is guaranteed),
- Added more flexibility and compatibility with other mods or modding tools:
--- You should now be able to open and append changes to Assembly-CSharp.dll mods created by other means (Sound Fix, dnSpy-edited DLL, etc...) as long as they don't change the structure of some key things (the datas read by Hades Workshop, such as the spells (AA_DATA)...),
--- Since Albeoris's Memoria change the structure of these key things, you still can't open the Memoria-modded DLL; however, you can now generate .csv files that are suitable to Memoria for most of the modifications. There's a couple of modifications that requires a bit of dnSpy hacking to complete the merging of the mod though: the Magic Sword requirement spells and the Battles' battleground changes (both of them are not editable yet using only Memoria),
--- You may directly generate the assets instead of the full-fledged archive files; this way, assets can be imported into archives modded in different manners and easily merge mods as long as they don't change the same assets (but do change the same archives),
--- Fixed different bugs with the PSX versions of the game; Hades Workshop should fully work on them again (also, the Randomizer works on the PSX version, minus the possibility to have non-updated spell names or descriptions due to the lack of space),
- Fixed a few other bugs (the one pointed by DavidBest1996, the very large numbers bugging in the Script editor, a "switch case + do-while loop" decompilation error...) and improved a few things.

I am still on a break with FF9 modding. I will surely just update my mod Alternate Fantasy to have compatibility with other mods, but then I'll return to not making things.

Note that Gael-Honorez-sb added CMake files to generate Hades Workshop projects using CMake on GitHub. I always have troubles with CMake (but with Visual Studio as well anyway) but it will surely ease the work of people trying to use the tool's source code. Let him be thanked for that.

@ToraCarol: Maybe it has something to do with the dialog itself? What text opcodes are you using?

Sorry for not answering. I am working back a bit on HW in view of releasing a new version. I'll mainly try to fix compatibility issues (Albeoris's Memoria, Moguri Mod, Audio fix and other DLL editing mods...). I'll use the occasion to fix a couple of bugs too.

Thanks a bunch DavidBest1996 for telling me about that one; it's fixed and the next version will be working better on PSX files again.

Good job so far TheHobgoblin ^^
You can see examples of NPC scripts here, there and there. The important steps are: add an NPC entry (or use one of the free "player" entries that are after all the others and usually have no functions at first), add an "Init" and a "SpeakBTN" function, setup the model+position in the "Init" and use "SetPartyReserve( xxx )" and "Party( 4, 0 )" in the "SpeakBTN".

@masagrator: Yes, that's a problem happening because of the Steam version.
Basically, they used a weird alpha channel for rendering the edges of the tiles while it was a "completly opaque/completly transparent" alternative in the PSX format.
The shift on the carpet (wow, I had a hard time seeing it even with your mark...) surely comes from the way they upscaled the images for the Steam version (because it's not a tile edge).
Fragosso knows better than me this kind of things...

@ToraCarol: There should not be any "return" line if the first "if" block that you show.
The problem with the jump animation is more likely to be the one described here. You need to use this jump animation before in the script to let the game load it (using "SetRightAnimation( 5041 )" just before "SetRightAnimation( 5224 )" is enough).
Emulating a "Change disc" screen requires modding the C# code and adding new assets (textures) to the game. I don't know if it can work, sorry.
You may more easily add a "Would you like to save?" screen, as it can simply be emulated with a dialog box... but I can understand that it's not what you want.

I don't see the problem with your screenshot.
Which kind of image do you want to "generate" (= extract from the game?) ?

What is the current script that you got?
There are two possibilities: either the loop enters a case where the script "returns" (which stops for good the script making her following Zidane) or you forget to set the safety variable back to its normal state (that's if the cutscene safety relies on a variable).

@shikulja: I don't understand what you want to do.
- Do you want to open the PSX files that were patched using RGR?
- Do you want to use a russian translation that is available for the Steam version and turn it to a PSX mod?
In the first case, it should work... maybe Hades Workshop bugs since a couple of versions so you should try with older versions of HW.
In the second case, it's not possible as I didn't write a script for converting a "Steam formatted text" to a "PSX formatted text".

@masagrator: So, I guess that you went to "File -> Preferences" to solve the first problem, which is what is needed to display and export modded backgrounds properly.
In order to import backgrounds, you need to go to the "Tools -> Background Editor". Here is a tutorial for using it.

Misc. Tools / Re: [FFX] Skill editor - Ronso (v0.3.1.0)
« on: 2019-06-01 19:48:10 »
The fact that it can be done doesn't mean that it would be easy. It can be way harder to do it for FFX because, indeed, of different data structures.

However, Husbjörn said that he plans to do something similar if he has time:
I have some new ideas for a more in-depth kind of editor actually (as well as have managed to figure out a working way to alter enemy data of the Steam version at load time), just not the time needed to realize it. Hopefully one day...
To me, that looks the closest you can get to a game patcher that actually uses RAM modifications (and it would be goddamn useful). It's not the same kind of tools normally.

Please, let your respect for the modder take the shape of patience :)

That seems correct.

1) You're using the Steam version, right? It doesn't work easily on the PSX version (though it should work if the animations are the standard ones).
2) Check that you added this function to the correct entry: it should be the 7th entry after Zidane's entry (for instance, if Zidane is the entry 11, Blank should be the entry 18). In particular, this "Blank_Init" function should be displayed below Zidane's functions.
3) In the "Edit Entries" window, check that Blank/Amarant's entry is properly set to a "model entry (2)". They are sometimes defaulted to "255" when they are not used.

What did you put in Blank's Init function?
It should look like the example I gave here for Beatrix (you need to change the position, the model ID and its animation IDs and add the "DefinePlayerCharacter" line).

@ToraCarol: It's not really possible to do that because not all the characters have the correct animations (like opening a chest or climbing a ladder).
"InitObject( 251 )" works only if there are script functions for the character. For instance, if Eiko is the first character, then the script must have a "Eiko_Init" function attached to Eiko's entry for "InitObject( 251 )" to work.
You can see in the "Edit Entries" window that there is always an entry for the player characters. It's just that they usually don't have any function linked to them.

In order for the player to control a character, it's something different: you need to use "DefinePlayerCharacter()" in the entry that you want the player to take control of (usually, it's in the "Init" function but it can be in another function if, for instance, the control is not given as soon as the characters are placed).

You don't bother me; it's just that I stopped modding so I don't answer right away anymore.

That's because the .hws is in the separate download.
For the latest release, I separated the mod itself and its source files, to avoid confusion when installing it.

Yes, it is necessary to use dnSpy (or mod the source code using Memoria, but that's C# all the same).

When I write about editing C# code (or CIL code), I write about classes and method with the format "class::method". That means that you need to find the class in the list shown by dnSpy and then find the method inside that class. Namely, the classes of interest here are "btl_init" and "BattlePlayerCharacter".

@Incinerator: I don't think that splines are imported correctly!
Triangularize the mesh before importing it. I think splines are not supported because there is none in the non-modded game and I didn't know how to handle them. If someone is expert in Unity serialized 3D models, he could explain how to handle splines.

There is no hard-coded polygon limit so I guess the problem doesn't come from there. There is no reason for the engine to discard big models if they are valid.

@ToraCarol: Yes, that's the main point of my previous message.

Here is how it works. It's pretty simple:
1) In the class "btl_init", the array "model_id" contains the list of the player character's models:
Code: [Select]
Zidane (2 of them),
Dagger (4 of them),
Steiner (2 of them),
Eiko (2 of them),
Blank (2 of them),
Zidane Trance (that's the 20th, at index 19 and there are 2 of them just like the regular),
Vivi Trance,
up to Amarant Trance.
Which makes a list of size 19 (regular) + 14 (trance) = 33 models.
These are different model names but I think that they mostly use the same mesh files and the different names are meant to link to different animations (but I'm not sure, that's what I remember about). Anyway, the duplicates are because of the different fighting stances when using, for example, thief daggers or thief swords. Dagger also has her long/short hair versions.
About Steiner, I think the difference has to do with a flag that can be activated in the "Battle" and "BattleEx" script codes, but it's a dummied feature and never actually used.

2) The methods "BattlePlayerCharacter::CreateTranceModel" and "btl_init::SetBattleModel" setup the models at the start of the battle. They create both a regular and a trance model/skin for those who have it and hide the trance version. The condition is simply "if ((int)(serialNumber + 19) < btl_init.model_id.Length) // add a trance version" for both methods.
Note that there is a duplicate of the regular model list, "BattlePlayerCharacter::PlayerModelFileName" for some impractical reason. The lists used are thus:
- "BattlePlayerCharacter::PlayerModelFileName" for the regular model and "btl_init::model_id" for its skin (geotexanim),
- "btl_init::model_id" for the trance model and its skin.

3) When creating a trance model, the animations are entirely taken from the regular model so you don't need to care about it (but the trance model has to have exactly the same bone hierarchy as the regular one). I suppose that there is no problem in taking the same "geotexanim" as the regular one or even don't use geotexanim at all (I don't really see a texture animation on battle models, except the trance glowing but that's handled automatically elsewhere).

4) In HW's "Party -> Stats" panel, there's some setting to do in order to have a proper trance command.

5) Characters have a trance gauge if they are not setup with category "temporary character" in the script code initializing them ("SetCharacterData"). Also, trance gauge is disabled early game and for Dagger when she's depressed.

6) Also, another array must be extended for Beatrix. In the method "btl_stat::SetStatusVfx", which mostly takes care of glowing effects (and floating/confused but we don't care there), you can find an array like that
Code: [Select]
byte[][] array = new byte[][]
new byte[] { 255, 96, 96 },
// etc...
These are RGB color factors for the glowing effect of the trance. You must extend the array with one entry (of 3 bytes) corresponding to Beatrix's glowing effect when she turns to trance. The default color factor is 128, which means that "{ 128, 128, 128 }" will not display any glowing at all. Personally, I used the following in my mod:
Code: [Select]
new byte[] { 255, 160, 128 }
In conclusion
In order to add a properly working trance to Blank, Cinna, Marcus or Beatrix, you need to do 3 things:
• In HW, setup a trance command for the character (define it in the panel "Party -> Commands" and give it to the character "Party -> Stats"; remind that Beatrix has 2 ability sets),
• In HW, remove the "Temporary Character" category of the character(s) you want to give trance to. It's in the "Environment -> Fields" scripts, when the characters join the team, there are lines "SetCharacterData" to tweak a bit. For instance, Beatrix's character data is initialized twice:
-- in the script "A. Castle/Queen’s Chamber" (one of them anyway, there are several ones... the good one has "Bandersnatch" functions in its script), whithin the function "Beatrix_Loop", there's a line "SetCharacterData( 8, 1, 11, 22, 14 )" near 4/5 of the function.
-- in the script "Alexandria/Square" (the one at night), whithin the function "Steiner_Init", there's a line "SetCharacterData( 8, 1, 13, 22, 15 )". In both cases, the change should be replacing the argument "22" by a "6" to remove the "Temporary Character" category.
• In dnSpy, append 5 lines to "btl_init::model_id" by giving trance models.
-- Either use the exact same models as the regulars (ie. append "GEO_MAIN_B0_013", "GEO_MAIN_B0_014", "GEO_MAIN_B0_015", "GEO_MAIN_B0_016" and "GEO_MAIN_B0_017" to the list): the only difference will be the glowing,
-- Or use another existing model and the same bone hierarchy (the correspondances between "model name" and "model numerical ID" is in the dictionary "FF9BattleDB::GEO"),
-- Or find a way to import a workable new model with the right bone hierarchy and register it in both "FF9BattleDB::GEO" and "btl_init::model_id". I have been unsuccessful to import a workable model, as the files added to the archives require a lot of registering and linkage and I apparently miss some (I don't remember if I successfully imported a new texture with the Unity Assets Viewer...).
• Still in dnSpy, append 1 entry to the local array that can be found around the middle of "btl_stat::SetStatusVfx".

No sorry, I don't know how Ze PilOt did for the music. It may be only music files in the archives or it may use features of Memoria (which is not compatible with Hades Workshop most of the time).

Pages: [1] 2 3 ... 24