Author Topic: [PSX/PC] General editor - Hades Workshop (0.50b)  (Read 954071 times)

Tirlititi

  • *
  • Posts: 883
    • View Profile
Re: [PSX/PC] General editor - Hades Workshop (0.50b)
« Reply #2025 on: 2024-08-11 08:57:23 »
Hello RoSoDude.
That's most likely MIPS code indeed. I am not sure that would be feasible by modifying the parts of the MIPS code that are accessible through Hades Workshop but there's a chance it is.

The parts that are accessible and that are indexed correspond to effects of abilities. That code (which was most likely written in C for the PSX version before it got compiled to MIPS) has been ported into C# that can be read there: btl_calc.cs
Having both MIPS and C# there might help you to see how that MIPS is written and maybe identify data banks that would be relevant to change what you want. For example, there are references to BTL_DATA's "cur.hp": the current ATB of each fighter is very close to that field because "cur" is a structure like that:
Code: [Select]
public class POINTS
{
public ushort hp;
public short mp;
public short at;
public sbyte at_coef;
public byte capa;
}
with "at" being the ATB and "at_coef" being its speed (that depends both on Haste/Slow but also on the battle speed configuration).

Unfortunatly, I don't know about the parts that identify whether an animation (SFX) is running or not in MIPS. I guess using a debugger there (to scan the RAM during runtime) would be the best way. You can see the list of different SFX identifiers here; that should help you when looking for that kind of info.

There are functions that handles ATB progression globally, not just each individual "at_coef", but the C# port of it is surely not as close to the original MIPS code as other parts. The following C# method determines if ATB should be advancing (and would ideally be the place you'd add the condition regarding SFX) but is part of the HUD class, which were much more re-factored than other parts of the code:
Code: [Select]
public bool FF9BMenu_IsEnableAtb()
{
if (!this.commandEnable)
return false;
if (FF9StateSystem.Settings.cfg.atb != 1uL)
return true;
if (this.hidingHud)
return this.currentPlayerId == -1 || this.currentButtonGroup == BattleHUD.CommandGroupButton || this.currentButtonGroup == string.Empty;
return this.currentPlayerId == -1 || ButtonGroupState.ActiveGroup == BattleHUD.CommandGroupButton || ButtonGroupState.ActiveGroup == string.Empty;
}

Good luck.
« Last Edit: 2024-08-11 08:59:28 by Tirlititi »

HopefulTarnished

  • *
  • Posts: 4
    • View Profile
Re: [PSX/PC] General editor - Hades Workshop (0.50b)
« Reply #2026 on: 2024-08-27 20:40:49 »
I want a function to apply specifically to the player controlled character, but in entries where the function DefinePlayerCharacter could apply to either Zidane OR Dagger, is there a way to achieve this?

EDIT: amended after accidentally deleting the post so people can see the original question. Apologies.
« Last Edit: 2024-09-01 13:19:19 by HopefulTarnished »

Tirlititi

  • *
  • Posts: 883
    • View Profile
Re: [PSX/PC] General editor - Hades Workshop (0.50b)
« Reply #2027 on: 2024-08-27 22:40:40 »
Hello HopefulTarnished
You can use "GetEntryUniqueID(250)". It converts the special UIDs 250-255 to their number counterpart. Let's say if Zidane is the entry 11 (then Garnet is the entry 13), you can do:
Code: [Select]
if ( GetEntryUniqueID(250) == 11 ) {
    // Zidane is the player character
}
if ( GetEntryUniqueID(250) == 13 ) {
    // Garnet is the player character
}
I have not tested it but I think that should be the way to go.

Good luck too!

HopefulTarnished

  • *
  • Posts: 4
    • View Profile
Re: [PSX/PC] General editor - Hades Workshop (0.50b)
« Reply #2028 on: 2024-08-31 17:26:35 »
@tirlititi

I've managed to reach Dali in terms of scripting, even re-writing the scenes and it's going really really well. I'm a bit overwhelmed by the party changing scripts used by the devs though. Is there a special reason they are so long?

Code: [Select]
    if ( General_FieldEntrance == 65535 ) {
        set Setting_PartyReserve = 15
        SetPartyReserve( Setting_PartyReserve )
        set VAR_GlobInt16_8 = ( VAR_GlobInt16_10 = ( VAR_GlobInt16_12 = ( VAR_GlobInt16_14 = 0 ) ) )
        set VAR_GlobInt16_8 = 0
        while ( VAR_GlobInt16_8 <= 11 ) {
            if ( IsInParty(VAR_GlobInt16_8) ) {
                set VAR_GlobInt16_10 |= ( 1 << VAR_GlobInt16_8 )
            }
            set VAR_GlobInt16_8++
        }
        if ( 1 ) {
            set VAR_GlobInt16_12 |= 1
        }
        if ( 1 ) {
            set VAR_GlobInt16_12 |= 4
        }
        if ( 0 ) {
            set VAR_GlobInt16_12 |= 2147483648L
        }
        if ( 0 ) {
            set VAR_GlobInt16_12 |= 2147483648L
        }
        set VAR_GlobInt16_14 = ( VAR_GlobInt16_10 ^ VAR_GlobInt16_12 )
        set VAR_GlobInt16_8 = 0
        while ( VAR_GlobInt16_8 <= 11 ) {
            if ( ( VAR_GlobInt16_14 >> VAR_GlobInt16_8 ) & 1 ) {
                RemoveParty( VAR_GlobInt16_8 )
            }
            set VAR_GlobInt16_8++
        }
        if ( IsInParty(0) == 0 ) {
            set VAR_GlobBool_147 = AddParty(0)
        }
        if ( IsInParty(2) == 0 ) {
            set VAR_GlobBool_147 = AddParty(2)
        }
        if ( IsInParty(65535) == 0 ) {
            set VAR_GlobBool_147 = AddParty(65535)
        }
        if ( IsInParty(65535) == 0 ) {
            set VAR_GlobBool_147 = AddParty(65535)
        }
        set VARL_GenUInt8_303 = 0
        set VAR_GlobInt16_8 = ( VAR_GlobInt16_10 = ( VAR_GlobInt16_12 = ( VAR_GlobInt16_14 = 99 ) ) )
        if ( 1 ) {
            set VARL_GenUInt8_303++
            set VAR_GlobInt16_8 = 0
        }
        if ( 1 ) {
            set VARL_GenUInt8_303++
            set VAR_GlobInt16_10 = 2
        }
        if ( 0 ) {
            set VARL_GenUInt8_303++
            set VAR_GlobInt16_12 = 65535
        }
        if ( 0 ) {
            set VARL_GenUInt8_303++
            set VAR_GlobInt16_14 = 65535
        }
        set VAR_GlobBool_148 = ( VAR_GlobBool_149 = ( VAR_GlobBool_150 = ( VAR_GlobBool_151 = 0 ) ) )
        if ( VAR_GlobInt16_8 != 99 ) {
            if ( GetHP(VAR_GlobInt16_8) == 0 ) {
                set VAR_GlobBool_148 = 1
            }
        }
        if ( VAR_GlobInt16_10 != 99 ) {
            if ( GetHP(VAR_GlobInt16_10) == 0 ) {
                set VAR_GlobBool_149 = 1
            }
        }
        if ( VAR_GlobInt16_12 != 99 ) {
            if ( GetHP(VAR_GlobInt16_12) == 0 ) {
                set VAR_GlobBool_150 = 1
            }
        }
        if ( VAR_GlobInt16_14 != 99 ) {
            if ( GetHP(VAR_GlobInt16_14) == 0 ) {
                set VAR_GlobBool_151 = 1
            }
        }
        if ( ( ( ( VAR_GlobBool_148 + VAR_GlobBool_149 ) + VAR_GlobBool_150 ) + VAR_GlobBool_151 ) == VARL_GenUInt8_303 ) {
            if ( ( VAR_GlobInt16_8 != 99 ) && ( VAR_GlobBool_148 == 1 ) ) {
                SetHP( VAR_GlobInt16_8, 1 )
            }
            if ( ( VAR_GlobInt16_10 != 99 ) && ( VAR_GlobBool_149 == 1 ) ) {
                SetHP( VAR_GlobInt16_10, 1 )
            }
            if ( ( VAR_GlobInt16_12 != 99 ) && ( VAR_GlobBool_150 == 1 ) ) {
                SetHP( VAR_GlobInt16_12, 1 )
            }
            if ( ( VAR_GlobInt16_14 != 99 ) && ( VAR_GlobBool_151 == 1 ) ) {
                SetHP( VAR_GlobInt16_14, 1 )
            }
        }
        CureStatus( 0, 127 )
        CureStatus( 1, 127 )
        CureStatus( 3, 127 )
        CureStatus( 2, 127 )
        CureStatus( 4, 127 )
        CureStatus( 5, 127 )
        CureStatus( 7, 127 )
        CureStatus( 6, 127 )
        CureStatus( 8, 127 )
        if ( IsInParty(5) ) {
            set Setting_OptionalQuina = 1
        } else {
            set Setting_OptionalQuina = 0
        }
        if ( ( ( Setting_PartyInitialized >> 0 ) & 1 ) == 0 ) {
            SetCharacterData( 0, 1, 255, 9, 0 )
            set Setting_PartyInitialized |= 1
        }
        if ( ( ( Setting_PartyInitialized >> 2 ) & 1 ) == 0 ) {
            SetCharacterData( 2, 1, 255, 6, 2 )
            set Setting_PartyInitialized |= 4
        }
        set Setting_OptionalQuina = 0
        set Setting_DaggerDepresses = 0
        set Setting_MPx4 = 1
        SetHP( 0, 9999 )
        SetHP( 1, 9999 )
        SetHP( 3, 9999 )
        SetHP( 2, 9999 )
        SetHP( 4, 9999 )
        SetHP( 5, 9999 )
        SetHP( 7, 9999 )
        SetHP( 6, 9999 )
        SetHP( 8, 9999 )
        SetMP( 0, 999 )
        SetMP( 1, 999 )
        SetMP( 3, 999 )
        SetMP( 2, 999 )
        SetMP( 4, 999 )
        SetMP( 5, 999 )
        SetMP( 7, 999 )
        SetMP( 6, 999 )
        SetMP( 8, 999 )
        CureStatus( 0, 127 )
        CureStatus( 1, 127 )
        CureStatus( 3, 127 )
        CureStatus( 2, 127 )
        CureStatus( 4, 127 )
        CureStatus( 5, 127 )
        CureStatus( 7, 127 )
        CureStatus( 6, 127 )
        CureStatus( 8, 127 )
        if ( General_ScenarioCounter == 2639 ) {
            if ( IsInParty(2) ) {
                RemoveParty( 2 )
                set VARL_GenUInt8_303 = 0
                set VAR_GenInt8_60 = ( VAR_GenInt8_61 = ( VAR_GenInt8_62 = ( VAR_GenInt8_63 = ( VAR_GlobInt16_8 = ( VAR_GlobInt16_10 = ( VAR_GlobInt16_12 = ( VAR_GlobInt16_14 = 65535 ) ) ) ) ) ) )
                set VAR_GlobInt16_14 = 0
                while ( VAR_GlobInt16_14 <= 11 ) {
                    if ( IsInParty(VAR_GlobInt16_14) ) {
                        switch 4 ( ++VARL_GenUInt8_303 ) from 1 {
                        case +0:
                            set VAR_GenInt8_60 = VAR_GlobInt16_14
                            break
                        case +1:
                            set VAR_GenInt8_61 = VAR_GlobInt16_14
                            break
                        case +2:
                            set VAR_GenInt8_62 = VAR_GlobInt16_14
                            break
                        case +3:
                            set VAR_GenInt8_63 = VAR_GlobInt16_14
                            break
                        }
                    }
                    set VAR_GlobInt16_14++
                }
                switch 1 ( VARL_GenUInt8_303 ) from 0 {
                case +0:
                    set VAR_GlobBool_148 = AddParty(2)
                    if ( GetHP(2) == 0 ) {
                        SetHP( 2, 1 )
                    }
                    CureStatus( 2, 127 )
                    break
                default:
                    set VARL_GenUInt8_303 = 0
                    set VAR_GlobInt16_8 = ( VAR_GlobInt16_10 = ( VAR_GlobInt16_12 = ( VAR_GlobInt16_14 = 99 ) ) )
                    if ( VAR_GenInt8_60 != 65535 ) {
                        set VARL_GenUInt8_303++
                        set VAR_GlobInt16_8 = VAR_GenInt8_60
                    }
                    if ( VAR_GenInt8_61 != 65535 ) {
                        set VARL_GenUInt8_303++
                        set VAR_GlobInt16_10 = VAR_GenInt8_61
                    }
                    if ( VAR_GenInt8_62 != 65535 ) {
                        set VARL_GenUInt8_303++
                        set VAR_GlobInt16_12 = VAR_GenInt8_62
                    }
                    if ( VAR_GenInt8_63 != 65535 ) {
                        set VARL_GenUInt8_303++
                        set VAR_GlobInt16_14 = VAR_GenInt8_63
                    }
                    set VAR_GlobBool_148 = ( VAR_GlobBool_149 = ( VAR_GlobBool_150 = ( VAR_GlobBool_151 = 0 ) ) )
                    if ( VAR_GlobInt16_8 != 99 ) {
                        if ( GetHP(VAR_GlobInt16_8) == 0 ) {
                            set VAR_GlobBool_148 = 1
                        }
                    }
                    if ( VAR_GlobInt16_10 != 99 ) {
                        if ( GetHP(VAR_GlobInt16_10) == 0 ) {
                            set VAR_GlobBool_149 = 1
                        }
                    }
                    if ( VAR_GlobInt16_12 != 99 ) {
                        if ( GetHP(VAR_GlobInt16_12) == 0 ) {
                            set VAR_GlobBool_150 = 1
                        }
                    }
                    if ( VAR_GlobInt16_14 != 99 ) {
                        if ( GetHP(VAR_GlobInt16_14) == 0 ) {
                            set VAR_GlobBool_151 = 1
                        }
                    }
                    if ( ( ( ( VAR_GlobBool_148 + VAR_GlobBool_149 ) + VAR_GlobBool_150 ) + VAR_GlobBool_151 ) == VARL_GenUInt8_303 ) {
                        if ( ( VAR_GlobInt16_8 != 99 ) && ( VAR_GlobBool_148 == 1 ) ) {
                            SetHP( VAR_GlobInt16_8, 1 )
                        }
                        if ( ( VAR_GlobInt16_10 != 99 ) && ( VAR_GlobBool_149 == 1 ) ) {
                            SetHP( VAR_GlobInt16_10, 1 )
                        }
                        if ( ( VAR_GlobInt16_12 != 99 ) && ( VAR_GlobBool_150 == 1 ) ) {
                            SetHP( VAR_GlobInt16_12, 1 )
                        }
                        if ( ( VAR_GlobInt16_14 != 99 ) && ( VAR_GlobBool_151 == 1 ) ) {
                            SetHP( VAR_GlobInt16_14, 1 )
                        }
                    }
                    CureStatus( 0, 127 )
                    CureStatus( 1, 127 )
                    CureStatus( 3, 127 )
                    CureStatus( 2, 127 )
                    CureStatus( 4, 127 )
                    CureStatus( 5, 127 )
                    CureStatus( 7, 127 )
                    CureStatus( 6, 127 )
                    CureStatus( 8, 127 )
                    break
                }
            }
            if ( 0 ) {
                set Setting_OptionalQuina = 0
            }
        }
    }

Is there no more succinct way to achieve saving a party formation and recalling it?

Thank you so much for taking the time to reply. I had no background in code only a week ago, and have learned to script some pretty decent scenes in the last week using your tool. I'm blown away how good it is to be honest!

Oh last question, is there a list anywhere of the VAR_GlobInt values used in the base game? I'm cautious about using them in case I overwrite something important to the original code.

Hope
« Last Edit: 2024-09-01 08:25:40 by HopefulTarnished »

Tirlititi

  • *
  • Posts: 883
    • View Profile
Re: [PSX/PC] General editor - Hades Workshop (0.50b)
« Reply #2029 on: 2024-09-01 10:10:13 »
I guess devs had some kind of "macros", ie. they only wrote a single line for changing the party formation and that line was converted into all these lines, doing the process step by step in a way that adapts to all situations. That's why it heals everyone even if they didn't join the party yet or why there are "if ( 0 )" blocks that just never happen. I never added a feature for having macros there.

The "VAR_GlobInt" are restricted to a field.
"local" variables are restricted to the entry, so "VAR_LocInt8_0" in the function "Main_Init" is a different variable than "VAR_LocInt8_0" in the function "Zidane_Init".
"global" variables are restricted to the field, so "VAR_GlobInt8_0" in the field "Prima Vista/Cargo Room" is not the same as in the field "Prima Vista/Meeting Room".
Only "general" variables are unrestricted (and they are the ones saved in save games). It would require a lot of work to identify all of the general variables already and I don't advance on that part anymore since a long time.
If you understand what a global or local variable does, you can name it inside the "local variable panel" right above the function's script. It's lines like
Code: [Select]
global int8 {variable_name} VAR_GlobInt8_0
Please don't edit your posts to remove the question after it is solved. It helps other people that read the topic.
« Last Edit: 2024-09-01 10:14:13 by Tirlititi »

HopefulTarnished

  • *
  • Posts: 4
    • View Profile
Re: [PSX/PC] General editor - Hades Workshop (0.50b)
« Reply #2030 on: 2024-09-01 11:41:35 »
Thank you! I can't believe I didn't see that panel before omg...

Yes! Sorry about the edit. I've modified the post to include the original question. I deleted it before because I didn't realise anyone had replied, I figured out my own workaround, and didn't want to waste anyone's time with an unnecessary question. I'll leave all future posts as they are!

Thanks for the reply, that's amazing! That variable panel is SO helpful for keeping track.

I'd really like to know also is there a way to modify the item drop rate chances (256/256, 96/256, etc)?
And with character-specific trance styles (like Steiner getting 300% damage, Zidane having access to Dyne, or Dbl Wht), are they hardcoded into the character id's (0 for zidane etc), or are they controlled some other way?
« Last Edit: 2024-09-01 20:16:37 by HopefulTarnished »

bumble_treez

  • *
  • Posts: 1
    • View Profile
Re: [PSX/PC] General editor - Hades Workshop (0.50b)
« Reply #2031 on: 2024-09-05 04:13:05 »
Holy wow this is a huge thread and its astonishing how active you are! Thanks for your commitment to one of my favorite games ever!

Im just dipping my toes into this whole scene and into coding in general so excuse any misunderstandings I may have here. Ive looked around at the information available here but didnt see what i was looking for.

Im trying to find a way to implement an alternative stat system. Something where you get to choose which stat bonuses you get over time for each character and perhaps respec later in the game.

My initial idea was to have this be some sort of pop up window triggered on level up but that seemed a little crazy to implement. I also thought it may be impossible if it wasnt just insanely hard.

So my next best idea was to just have a Stats option at every Moogle that let you interact with your stats. For all i know this could be even more difficult to implement, though, so your advice is welcome.

My main question is about how to interact with the character stats in general. Can i just give a permanent stat bonus to a character via a menu? Could a key item give a stat boost to a character? ive got a couple of ways i think it could work but im utterly lost at how to get started.

I also want this system to not be affected by the level at which the character enters the party, so i imagine i will have to find a way to deal with that, too. i expect that i will need to calculate how many points are expected to be available to a character based on their current level and then compare that to the amount of allocated stats for that character. Then, if there is a difference between those numbers, you are awarded the difference. Can this just be done with code or will i have to get creative with something like dummy items in the inventory?