Enemies¶
Enemies data are split in 3 TextAssets in the game loaded on boot:
- By LoadEssentials:
Ressources/data/EnemyData
- By SetVariables:
Ressources/data/TattleList
EnemyTattle
from the corresponding dialogue directory of the current languageid
EnemyData
is also loaded on the Start and Update of the PauseMenu.
EnemyData
data¶
The asset contains one line per enemy whose id corresponds to the line index. Each line contains fields separated by @
:
Loaded index | Name | Type | Description |
---|---|---|---|
0 | battleentity.animid | int | The AnimID of the entity bound to this enemy (can be overriden if it's a variant, see the section below about it for details) |
1 | hp | int | The base max HP of the enemy (can be influenced by difficulty scaling, see the section below about it for details) |
2 | def | int | The base defense of the enemy (can be influenced by difficulty scaling, see the section below about it for details) |
3 | exp | int | The base amount of exploration points this enemy gives (can be overriden to 0 or be scaled by the instance.partylevel , see the section below about it for details) |
4 | money | int | The base amount of berries this enemy drops |
5 | cursoroffset.x | float | The x component of the offset to apply to the selection cursor upon targeting the enemy |
6 | cursoroffset.y | float | The y component of the offset to apply to the selection cursor upon targeting the enemy |
7 | cursoroffset.z | float | The z component of the offset to apply to the selection cursor upon targeting the enemy |
8 | poisonres | int | The base poison resistance of the enemy |
9 | freezeres | int | The base freeze resistance of the enemy |
10 | numbres | int | The base numb resistance of the enemy |
11 | sleepres | int | The base sleep resistance of the enemy |
12 | size | float | The width of the enemy used for calculating positioning |
13 | battleentity.freezesize.x | float | The x component of the ice cube size when the entity is frozen |
14 | battleentity.freezesize.y | float | The y component of the ice cube size when the entity is frozen |
15 | battleentity.freezesize.z | float | The z component of the ice cube size when the entity is frozen |
16 | battleentity.freezeoffset.x | float | The x component of the ice cube offset from the entity's center when the entity is frozen |
17 | battleentity.freezeoffset.y | float | The y component of the ice cube offset from the entity's center when the entity is frozen |
18 | battleentity.freezeoffset.z | float | The z component of the ice cube offset from the entity's center when the entity is frozen |
19 | position | BattlePosition (string or int) |
The starting battle position of the enemy on the battle field (Random has special logic, see the special fields logic section below for details) |
20 | battleentity.height | float | The height of the entity, If the position is Flying and the value is below 2.0, it is overriden to 2.0 |
21 | battleentity.bobspeed | float | The bobspeed of the entity |
22 | battleentity.bobrange | float | The bobrange of the entity |
23 | weakness | { separated list of AttackProperty (int or string) |
The list of AttackProperty that applies to this enemy |
24 | weight | float | A visual modifier that controls if and how much the enemy can get launched if attacked, should be between 0.0 (furthest launch) and 100.0 (no launch) |
25 | Base Enemy id (loaded as animid) | int | The Enemy id that this is a variant of (see the section below about enemy variant for details), can be omitted if it's a negative number |
26 | eventondeath | int | The EventDialogue to trigger when the enemy dies as detected by CheckDead, can be ommited if it's -1 |
27 | moves | int | The amount of actor turn the enemy has normally each main turn during the enemy phase |
28 | notaunt | bool | Tells if the enemy can't receive the Taunted condition by using the Taunt skill |
29 | cantfall | bool | Tells if the enemy cannot drop or have its position set to Ground |
30 | fixedexp | bool | Tells if the exp field should not be scaled and is a fixed number (see the section below about EXP for details) |
31 | notired | bool | Tells if exhaution increases won't happen if they were set to happen normally (with caveats, check the documentation of the feature to learn more) |
32 | hidehp | bool | Tells if the hp and def stats of the enemy should not be shown even after spying |
33 | deathtype | int | The manner in which the enemy dies (This isn't a DeathType , more on the feature's documentation) |
34 | chargeonotherenemy | ; separated list of int or -1 or empty |
The list of enemy ids that will cause this enemy to perform a hitaction when any enemy id in that list takes damages in the player phase. See the feature's documentation to learn more. This field also has special loading logic, check the section below for more details |
35 | hardatk | int | The amount to increase the attack on Hard Mode or EX (see the section below about difficulty scaling for details). Not loaded if the hard difficulty scaling doesn't apply and the value remains at 0 |
36 | int | The base amount to increase the max HP on Hard Mode, EX or HARDEST (this is NOT hardhp which stays at 0 and is UNUSED). See the section below about difficulty scaling for details |
|
37 | int | The base amount to increase the defense on Hard Mode, EX or HARDEST (this is NOT hardef which stays at 0 and is UNUSED). See the section below about difficulty scaling for details |
|
38 | defenseonhit | int | The amount of point of defenses granted to the enemy when isdefending is true (check the feature's documentation to learn more) |
39 | itemoffset.x | float | The x component of the offset to render an item relative to the enemy |
40 | itemoffset.y | float | The y component of the offset to render an item relative to the enemy |
41 | itemoffset.z | float | The z component of the offset to render an item relative to the enemy |
42 | bool | If true, the battleentity.basestate is set to 13 (BattleIdle ) |
|
43 | Portrait sprite index | int | Index of the sprite in Sprites/Items/EnemyPortraits (if this is negative, this defaults to the enemy id, see the GetEnemyPortrait section below about this for more details) |
44 | notattle | bool | Tells if the enemy is not spy-able. Check the feature's documentation for more details. This fields also has special loading logic that can override it to true, check the section below for more detials |
45 | eventonfall | int | The EventDialogue to trigger when the enemy drops, can be ommited if it's -1 |
46 | onhitaction | int | If 1, the enemy will process a hitaction when hit. If 2, it will only when its position is Flying and if 3, it will only when its position is Ground |
47 | actimmobile | bool | Prevent cantmove to be anything higher than 0 or IsStopped to return true as a result of being inflicted with a stopping condition. This works for most, but not all cases, check the feature's documentation to learn more |
48 | sizeonfreeze | float | The size of the enemy on freeze. If the value is less than 0.1, it is be overriden to size + 0.25, check the feature's documentation to learn more |
The data will be loaded by into enemydata[id, x]
, where id
is the enemy id and x
is the loaded index. On the pause menu, every lines is loaded into enemydata
all at once, but the meanings do not change.
GetEnemyData¶
While the data loading happens in LoadEssentials
, the actual parsing and usage of it is done by another method called GetEnemyData
. It receives an enemy to obtain the data, a bool called createentity that tells whether or not the battleentity
should be created too and a bool called noexp that disallows any positive numbers to be set to exp
. This returns a BattleData
containing all the informations of the enemy.
public static BattleData GetEnemyData(int id, bool createentity, bool noexp)
All battleentity
fields are loaded only when the createentity parameter is true. When that happens, battleentity
will be created via CreateNewEntity with name enemyX
where X is the enemy id with dummy animid and position (animid 0 which is Bee
and position (0.0, -10.0, 0.0)) as those can be overriden later.
Enemy variant¶
The base Enemy id field uses a feature to redirect most of the fields loaded to be the ones belonging to another enemy entirely. It is used to add an enemy variant which only shares some fields with its base enemy, but it has its own id. It also has the effect of overriden the animid
field to be the base enemy id instead. Here are the list of fields that are shared:
eventondeath
battleentity.height
battleentity.bobspeed
battleentity.bobrange
moves
notaunt
cantfall
notired
fixedexp
position
There are however some fields that are excluded from this if the createentity is true and the original Enemies id is FireKrawler
, FireWarden
, FireCape
, IceKrawler
or IceWarden
. They will load the following fields from their base entry (Krawler
, Warden
or CursedSkull
) instead on top of the standard variant system:
moves
notaunt
cantfall
notired
fixedexp
position
Every other loading logic or fields will be loaded as if it was the enemy of the corresponding base Enemy id. This imply that every other field of the actual enemy will be ignored and they can be left as dummy values.
BattleEntity special initialisation¶
When createentity is true, on top of actually creating the entity and setting it to the battleentity
field, the entity itself has special logic when it comes to initialise it. This section will only mention anything that's outside the scope of loading a data field:
battleentity.overridejump
is set to truebattleentity.battle
is set to truebattleentity.alwaysactive
is set to truebattleentity.onground
is set to true- If this enemy hasn't been seen while the map is
CaveOfTrials
or it's aTANGYBUG
or the enemy isFireKrawler
,FireCape
orFireWarden
while flag 664 is false (not yet approached the oven during Chapter 7), the following happens (this can have an impact on theexp
field of theBattleData
, see the section below for more details):battleentity.name
gets prepended with theCOT
Modifierbattleentity.hologram
is set to truebattleentity.cotunknown
is set to true- RefreshCOT is invoked in 0.1 seconds on the
battleentity
battleentity.tag
is set toEnemy
battleentity.gameObject.layer
is set to 9 (Follower
)entity
is set to thebattleentity
- If the
position
isFlying
andbattleentity.height
is below 2.0,battleentity.height
is overriden to 2.0 battleentity.initialheight
is set tobattleentity.height
- CreateHPBar is called on the entity
battleentity.emoticonoffset
is set to thecursoroffset
- If the
position
isUnderground
:- InstantDig is called on the
battleentity
battleentity.height
andbattleentity.initialheight
are set to 0.0
- InstantDig is called on the
- If the field 42 of
enemydata
is true,battleentity.basestate
is set to 13 (BattleIdle
)
After this, the entity.destroytype
gets mapped to a DeathType depending on the value of the BattleData
's deathtype (this also changes the battleentity
's because they hold the same reference).
Exp logic¶
The exp
field has particularily complex logic to determine its value:
- It is always 0 if the sent noexp value is true
- It is left at the default value of 0 if instance.
partylevel
is at least 27 (it's maxed) or if flag 613 is true (RUIGEE is active) - If the
fixedexp
field is true, then the amount is always the raw one coming fromenemydata
with no modifications to it - If none of the cases above applied, the result is the return of MainManager.GetEXP with the
exp
field fromenemydata
, the instance.partylevel
and the enemy id (the base id if it's a variant)
The return of MainManager.GetEXP implies even more logic where the base exp
field can be changed. Here is the process used to calculate the final value:
- The base EXP amount is determined. It's the same than the sent exp value unless the enemy is a
WaspTrooper
orWaspHealer
which can increase this value: - The value gets multiplied by map.
expmulti
which is a field that must be defined on the map's prefab. The result isn't rounded yet - The value gets subtracted by (level - 1) * 2.5 and the result is clamped from 1.0 to 99.0 then floored
- The now integer value is decremented and returned
Additionally, GetEnemyData can also change yet again the value after it was calculated in 2 special cases (they both can happen and stack and they can happen regardless of how the initial exp
value was determined):
- flag 162 is true (during a B.O.S.S or Cave Of Trials session): the value gets multiplied by 0.2, floored and then clamped from 0 to 5
- createentity is true, flag 166 is false (EX mode isn't active on the B.O.S.S. system) and the
battleentity
is ahologram
(see the section above about thebattleentity
initialisation for when this happens): the value is multiplied by 0.1 and floored
Then, StartBattle can further change the exp
If all of the following conditions are true:
- The enemy is a
Krawler
,CursedSkull
orCape
- battleentity.
forcefire
is true or we are in theGiantLair
area except for theGiantLairFridgeInside
map - instance.
partylevel
is less than 27 (meaning it's not maxed) - flags 613 is false (RUIGEE is inactive)
If that happens, the exp
is incremented by the floored result of a lerp from 10.0 to 3.0 with a factor of instance.partylevel
/ 27.0
Finally, while all of the above describes the exp
field itself, CheckDead calls BattleControl.GetEXP (not to be confused with MainManager.GetEXP) to determine the rewarded exp amount. This method contains further special logic where it receives the exp
and fixedexp
fields of the enemy as well as its enemy id. Here's what the method does:
- If flags 613 is true (RUIGEE is active) or
partylevel
is 27 (max rank), the return is 0 (effectively disables all exp gains to the player) - Otherwise:
- If the
DoublePain
medal is equipped or flags 614 is true (HARDEST is active),exp
increases by (exp
* 0.15 ceiled which is basically 15% ceiled) - If the
EXPBoost
medal is equipped,exp
increases by (exp
* 0.50 ceiled which is basically 50% ceiled) - A result is determined from there (only the first one that applies is returned):
- If Flags 166 is true (during a B.O.S.S session in EX mode), the return is
exp
clamped from 0 to 5 - Otherwise, if
fixedexp
is true,exp
is returned without any clamping - Otherwise, if the enemy is among ChomperBrute, ToeBitter, DeadLanderA, DeadLanderB or DeadLanderG, the return is
exp
clamped from 0 to 20 - Otherwise (neither of the above applied), the return is clamped from 0 to 15
- If Flags 166 is true (during a B.O.S.S session in EX mode), the return is
- If the
Special fields logic¶
Additionally, some BattleData fields have special logic attached to them:
entityname
: See the section aboutEnemyTattle
belowholditem
: always set to -1chargeonotherenemy
: The value depends on the loaded data:- If it's
-1
, the value is an empty array - If it's empty, the value is null
- If it's a
;
separated list of enemy ids, each element will be converted to int and the value will be that array, but for any element that's empty in that list, the element will be -1 instead
- If it's
notattle
: If the loaded data value is false, it will still be overriden to true if any of these is true:sizeonfreeze
If the loaded value is less than 0.1, it will be overriden tosize
+ 0.25hardatk
,hp
anddef
: See the section below detailing how enemy stats difficulty scaling worksmaxhp
: Always set tohp
after it was calculated post difficulty scalingposition
: If its value is Random, then it will be determined randomly and uniformly between Ground and Flying, but there's an exception if the enemy is aMushroom
and flag 24 is true (received the Turn Relay tutorial) in which case, the position field is Flyingentity
: If createentity is true, this is set to thebattleenentity
being createdcondition
: Always set to a new listcantmove
: Always set to -moves
+ 1 which gives itmoves
amount of actions availableharddef
andhardhp
: These fields are practicaly unused because they always have a value of 0 which doesn't have any impact in the game
Stats difficulty scaling¶
There are some fields in enemy data that are influenced by the different difficulty tiers the game has. This only happens if any of the following is true:
- The
DoublePain
Medal is equipped - Flags 614 is true (HARDEST is active)
- Flags 166 is true (EX mode is active on the B.O.S.S. system)
If the above is met, it will always have the following effect:
hardatk
: Set to the loaded data value (this is left at 0 if hard scaling didn't apply in the first place)hp
: Increased byenemydata
field 36def
: Increased byenemydata
field 37
On top of this, if HARDEST was active, the following applies after:
hardatk
: Gets incrementedhp
: Gets increased by 15% then ceileddef
: If flags 300 is true (Chapter 4 started) and the existingdef
wasn't negative, it gets incremented
On top of this, if EX mode is active on the B.O.S.S. system, then there are additional effects after everything if the enemy isn't WaspGeneral
, KeyR
, KeyL
or Tablet
:
hardatk
: Gets increased by 15% ceiledhp
: Gets increased by 15% ceiled. If the result ends up above 90, it is then decreased by 15% ceiled.def
: Gets clamped from 1 to 99
GetEnemyPortrait¶
This field is loaded, but never written to. Its only usage is in the GetEnemyPortrait method which by default returns the value directly (or the enemy id if the value is negative).
However, the return can be overridden to 225 for a Cape
, 226 for a Krawler
and 227 for a CursedSkull
if flag 664 is true (approached the oven during Chapter 7). This overrides the portrait sprite to include the fire variants of the enemy under this condition.
EnemyTattle
data¶
The asset contains one line per Enemy whose id corresponds to the line index. Each line contains fields separated by @
:
Loaded index | Name | Type | Description |
---|---|---|---|
0 | Name | SetText string | The name of the enemy |
1 | Biography | SetText string | The biography of the enemy |
2 | Vi's spy | SetText string | Vi's Spy dialogue |
3 | Kabbu's spy | SetText string | Kabbu's Spy dialogue |
4 | Leif's spy | SetText string | Leif's Spy dialogue |
The data will be loaded into librarydata[1, id, x]
where id
is the Enemy id and x
is the loaded index. The name is also loaded into enemynames
.
Enemies not meant to have a Bestiary entry by convention have only the name field defined and the rest are dummy fields. The biography's dummy value is typically "biotattle" as well as the spy text fields being "beetattle", "beetletattle" and "mothtattle" respectively.
During GetEnemyData
, the typical entityname
value is the one loaded into enemynames
but it will be menutext 59 (?????) during Cave Of Trials for an unseen enemy or if createentity is true and the enemy is FireKrawler
, FireCape
or FireWarden
while flag 664 is false (not yet approached the oven during Chapter 7).
TattleList
data¶
The asset contains the order to render the Bestiary entry in the Library List Type where each line contains one field:
Name | Type | Description |
---|---|---|
Enemy id | int | The Enemies id of the enemy to render at the position of the line index |
The data will be loaded into libraryorder[1, i]
where i is the line index.