HUD¶
The game can present UI elements showing some game informations in a HUD fashion. They are expressed in 5 hud
elements with matching hudfont
elements to present their text with a DynamicFont. The first 4 hud
elements also have a backing hudpsrites
that changes color depending on the new values on RefreshHUD. Here's an overview of what each of the hud
elements shows:
hud[0]
:playerdata[0]
's HP / max HPhud[1]
:playerdata[1]
's HP / max HPhud[2]
:playerdata[2]
's HP / max HPhud[3]
: The party's TP / max TPhud[4]
: Berry count
There's also the discoverymessage
HUD element which is handled completely separately. It represents a HUD notification that only pops temporarilly when discoveryhud
cooldown hasn't expired yet. This typically happens when a new librarystuff
entry was unlocked.
RefreshHUD¶
RefreshHUD is the heart of the HUD system that manages all the hud
elements and their related data. It's a method called on LateUpdate as long as playerdata
isn't empty (a map isn't being loaded) and basicload
is true (LoadEverything ran once). It has the following signature:
private static void RefreshHUD()
The method has 3 main logic (the first one that applies is done, they are mutually exclusive):
- (Re)create all
hud
elements if it's null or empty - The first 4
hud
elements updates and are shown if instance.hudcooldown
hasn't expired yet by calling ShowHUD(true) which is detailed in a section below - Hides the HUD if neither of the above applies by calling ShowHUD(false) which is detailed in a section below
No matter what however, the following tasks always happen at the end of the method:
- If the instance.
showmoney
cooldown hasn't expired yet (thehud[4]
berry count HUD element should be shown) and the message lock is released, instance.showmoney
decreased byframestep
- If the instance.
discoveryhud
cooldown hasn't expired yet (the instance.discoverymessage
HUD element should be shown), it decreases by instance.framestep
- All of the
hud[4]
berry count HUD element updates happens as detailed below regardless even if the HUD was recreated and even if it's hidden
HUD Creation and Recreation¶
The HUD is meant to be automatically recreated once it's destroyed. This is ensured by RefreshHUD.
Creation¶
If the HUD isn't created yet or it was destroyed, RefreshHUD will do the following:
- Calls CreateHUD (detailed below)
- Reset
tphp
andptmhp
to new arrays with the same length asplayerdata
(these are last values observed by RefreshHUD for use in values updates, this is detailed in a section below) hud[4]
berry count HUD has its local x position set to thehud[3]
TP HUD's local x position so they are aligned horizontally with each other
CreateHUD is what does most of the HUD (re)creation. Here is the signature:
private static void CreateHUD()
More precisely, here's what this method does:
- Calls ApplyBadges
- Calls ApplyStatBonus
- Reset
hud
,husprites
andhudfont
to new arrays (length is 5 except forhudsprites
which is 4) - Creates a new GameObject named
HUD
childed to theGUICamera
with no angles or local position hudvalue
is reset to {6.5, 7.5, 8.5, 9.5, -6.5, -7.0}- Each
hud
is created via a call to NewHUDElement where the parent is theHUD
GameObject, the hudid is the matchinghud
index and the position depends on the specifichud
:- 0: (-7.0, 8.5)
- 1: (-3.5, 8.5)
- 2: (0.0, 8.5)
- 3: (7.0, 8.5)
- 4: (
hud[3]
x position, 0.0 -hud[3]
y position)
NewHUDElement is the method that creates each hud
, here's the signature:
private static void NewHUDElement(int hudid, Vector2 position, Transform parent)
This is the method that ends up assigning hud[hudid]
to a newly created GameObject with a local position of position
(the z is 10.0) childed to parent
(normally, always the GUICamera
).
Here's the properties of the GameObject assigned:
- Name of
hudX
whereX
ishudid
- Childed to
parent
- Local position of (
position
.x,position
.y, 10.0) - No angles
- One child being a new GameObject named
basespriteX
whereX
ishudid
that has a SpriteRenderer. It has the following properties:- Layer 5 (
UI
) - sprite of
guisprites[4]
(the HUD background sprite) - sortingOrder of 10
- Scale of (0.55, 0.65, 1.0)
- No angles
- A child being a new GameObject named
facesprite
that has the following properties:- Layer 5 (
UI
) - Local position of (-2.3, 0.0, 0.0)
- Scale of 80%
- sortingOrder of 12
- Layer 5 (
- Layer 5 (
From there, each hudid
gets different properties. For any below 3 (any playerdata
HP):
basesprite
's color set tocharcolor[instance.playerdata[hudid].animid]
facesprite
's sprite set toguisprites[instance.playerdata[hudid].animid + 5]
(player party member HUD icon)hudsprites[hudid]
set to thebasesprite
's SpriteRenderer- NewUIObject called to create a new object named
hpicon
childed to thefacesprite
with local position (0.45, -0.6, 0.0) and a scale of 50% usingguisprites[24]
(HP icon) with a sortingOrder of 13 - If
hudid
refers to aplayerdata
that exists,hudfont[hudid]
is SetUp with an inital text (see the table below for the formatting and values) with the following parameters:- Without centralize
- With noKerning
- Frequency of 2.0
- Fonttype of 2 (the unused mapping that redirects to
D3Streetism
) - Sortorder of 11
- Fontsize of 1.75x
- Childed to the
basesprite
- Position of (-0.9, -0.6, 0.0)
- Color of Color.white
- Small of (3.0, 0.85)
- The
dropshadow
is set to true
For a hudid
of 3 (TP):
basesprite
's color set tomenucolor[3]
facesprite
's sprite set toguisprites[28]
(TP icon)hudsprites[3]
set to thebasesprite
's SpriteRendererhudfont[3]
is SetUp with an inital text (see the table below for the formatting and values) with the following parameters:- Without centralize
- With noKerning
- Frequency of 2.0
- Fonttype of 2 (the unused mapping that redirects to
D3Streetism
) - Sortorder of 11
- Fontsize of 1.75x
- Childed to the
basesprite
- Position of (-0.9, -0.6, 0.0)
- Color of Color.white
- Small of (3.0, 0.85)
- The
dropshadow
is set to true
For a hudid
of 4 (Berry count):
basesprite
's color set tomenucolor[0]
facesprite
's sprite set toguisprites[29]
(Berry icon)hudfont[3]
is SetUp with an inital text (see the table below for the formatting and values) with the following parameters:- Without centralize
- With noKerning
- Frequency of 2.0
- Fonttype of 2 (the unused mapping that redirects to
D3Streetism
) - Sortorder of 11
- Fontsize of 1.75x
- Childed to the
basesprite
- Position of (-0.75, -0.6, 0.0)
- Color of Color.white
- The
dropshadow
is set to true
Destruction¶
The HUD can be destroyed temporarilly with a call to RebuildHUD:
public static void RebuildHUD()
It does the following:
- Calls ApplyBadges
- Calls ApplyStatBonus
- Destroys
hud[0]
's parent ifhud[0]
exists - Reset
hud
to null
The effects are temporary because as soon as RefreshHUD gets called, it will recreate the HUD with the process mentioned above, but it has the effect of forcing a reconstruction and thus an update of the HUD.
This method only gets called on event 22 (loading a save file) and on a ChangeParty. It's meant to reflect drastic HUD changes only as if only values updates are needed, ForceHUD or RefreshHUDValues are better suited (they are detailed in a section below).
HUD Updates¶
When the hud
updates via RefreshHUD, a bunch of different tasks and values are involved. As a reminder, the first 4 hud
elements are only updated if instance.hudcooldown
hasn't expired yet while hud[4]
(berry count) is always updated when RefreshHUD is called.
They are organised like the following (the first 3 hud
elements will be refered to as hud[0-2]
because they are all similar, but they refer to different associated values indexed by the same hud
index):
Transform and DynamicFont | Text format | Text values | Last values | Value updates | hudsprites updates |
Sounds |
---|---|---|---|---|---|---|
hud[0-2] , hudfont[0-2] |
X/Y |
|
|
|
hudsprites[0-2] .color changes depending on playerdata[0-2] .hp :
|
If playerdata[0-2] .hpt increases as a result of the incremental update while still not being equal to playerdata[0-2] .hp and sounds[5] isn't plauying anything, PlaySound is call to play TP on sounds[5] with 1.0 volume and a pitch of 1.15 + random number between -0.1 and 0.1 |
hud[3] , hudfont[3] |
X/Y |
|
|
|
hudsprites[3] .color (if the sprite exists) is updated to a lerp from the existing color to instance.menucolors[3] with a factor of 0.05 * framestep |
If instance.tpt increases as a result of the incremental update while still not being equal to instance.tp and sounds[5] isn't plauying anything, PlaySound is call to play TP on sounds[5] with 1.0 volume and a pitch of 1.0 + random number between -0.1 and 0.1 |
hud[4] , hudfont[4] |
X |
X : instance.moneyt padded left to 3 digits |
X : tempmoneh |
X : instance.moneyt incrementally updates towards instance.money , but the text only updates if last value is different |
None (hudsprites[4] doesn't exist) |
Before instance.moneyt updates, If it's not being equal to instance.money while instance.showmoney is above 0.0 and sounds[4] isn't plauying anything, PlaySound is call to play Money on sounds[4] with 1.0 volume and a pitch of 0.7 (1.0 instead if instance.money is higher than instance.moneyt ) + random number between -0.1 and 0.1 |
To note, an incremental value update means the backing HUD value only changes by 1 towards its target on each call while a direct update sets the value directly.
Additionally, a HUD update also causes the following to happen:
- If the game isn't
inbattle
, instance.hudcooldown
decreases byframestep
(so the HUD is never hidden during battle) - ShowHUD(true) is called to reveal the hud (this is detailed in a section below)
- If instance.
hudcooldown
expired, it is set to -100.0
Manually force direct updates¶
There's a way to force all HUD values to update immediately to their target values. This is done with the RefreshHUDValues method:
public static void RefreshHUDValues()
It the following to update the displayed HUD values to their backing value:
- Set all
playerdata
'shpt
to theirhp
- Set
tpt
totp
- Set
moneyt
tomoney
It's only called on event 22 (save loading), during StartBattle and on PauseMenu.PrepareExit, but it is meant to be callable externally to force the next RefreshHUD to have an updated HUD without any incremental value updates (everything will stabilise by the end of the next refresh).
Reveal and hiding the HUD¶
RefreshHUD also controls if it should reveal or show the HUD depending on instance.hudcooldown
. This is managed by the ShowHUD method:
public static void ShowHUD(bool show)
The HUD is shown if show
is true, hidden if it's false.
This method will first determine if the HUD should be shown when it wasn't before. It happens if show
is true while the hudvisible
is false. If that happens, ForceHUD is called. hudvisible
is updated to the show
value either way.
ForceHUD is a method with the following signature:
private static void ForceHUD()
It sets all tphp
, ptmhp
, tmtp
values and tempmoneh
to -1.
This essentially forces a call to RefreshHUD on the next LateUpdate because it will find that everything the HUD should display has changed.
After that, what happens in ShowHUD is each hud
local y position gets lerped from the existing one to a number that depends on the hud
index and its matching hudvalue
with a factor of TieFramerate(0.2). The target of the lerp uses the following formula:
A
* (hudvalue
+ (hudvalue
- X
) * (hudvalue
- Y
))
Here are the variables values:
A
: 1.0 except forhud[4]
where it's -1.0X
: 6.5 ifshow
is false, 4.25 if it's true. If it'shud[4]
, it's the other way aroundY
: 4.25 ifshow
is false, 6.5 if it's true. If it'shud[4]
, it's the other way around
Also, this formula is computed after lerping the hudvalue
from the existing one to X
+ the hud
index with a factor of TieFramerate(0.15).
discoverymessage
¶
There's a HUD notification object that is separared from all the rest of the hud
system and that object is discoverymessage
.
To show it, discoveryhud
needs to be set to the amount of frames that it should be shown. This cooldown is updated by RefreshHUD where if it hasn't expired yet, it is decreased by framestep
.
Setting the value will impact a method call in LateUpdate that happens right after RefreshHUD: RefreshDiscovery. Here is its signature:
private void RefreshDiscovery()
The method updates the local position of the discoverymessage
UI element according to discoveryhud
. If discoveryhud
is higher than 0.0, the local position is lerped from the existing one to (-8.0, -3.0, 0.0) with a factor of TieFramerate(0.1) which will smoothly reveal discoverymessage
. Otherwise, the same lerp happens, but towards (-8.0, -6.0, 0.0) instead which will hide discoverymessage
.
On the first time this is called, discoverymessage
will be null which will cause this method to create it from scratch (any subsequent calls reuses the same object). It is set to a NewUIObject with name Discovery
childed to the GUICamera
with a pos of (-8.0, -6.0, 0.0). It has one child GameObject that is instantiated from the Prefabs/Objects/logbookicon
prefab with a local position of (0.0, -1.0, 10.0).
From there, there's 3 SetText calls all done with the following parameters:
- fonttype: 1 (
D3Streetism
) - linebreak: null
- dialogue: false
- tridimensional: false
- position: (1.2, -1.0, 10.0)
- No camoffset
- size: Vector3.one
- parent:
discoverymessage
- caller: null
This means that there's going to be 3 more children to discoverymessage
, one for each of these SetText call for a total of 4 children. They corresponds to indicate what updated in the librarystuff
. Here's what they say in order:
|single||color,4||dropshadow,1,-1|
followed bymenutext[102]
(Logbook updated)|single||color,4||dropshadow,1,-1|
followed bymenutext[154]
(Quest complete)|single||color,4||dropshadow,1,-1|
followed bymenutext[155]
(Achievement unlocked)
The last 2 of these are initially disabled upon creation.
The way this is supposed to work is UpdateJournal will change the enablement of these 3 children such that only the relevant one is enabled and the other 2 disabled.