ActorSerializeParameter and AI

From Pikmin Technical Knowledge Base
Jump to navigation Jump to search

All of the info on this page (thus far) is research and assumptions by Noodl - it may not all be entirely correct.

ActorSerializeParameter is a property on each object in an ActorGeneratorList. It contains much of the "per-instance" characteristics of the enemy it is generating, and contains parameter such as AI, object life and audio.

Each member of ActorSerializeParameter is an object containing Static and Dynamic properties, both of which are varying-width arrays of 8bit integers that represent other data types.

{
    "ActorSerializeParameter": {
        "AI": {
            "Static": [],
            "Dynamic": [];
        },
        "Hash": { ... },
        "CheckComp": { ... },
        "ActorParameter": { ... },
        "SubAI": { ... },
        "Life": { ... },
        "PortalTrigger": { ... },
        "DemoTrigger": { ... },
        "Pikmin": { ... },
        "CharacterEdit": { ... },
        "PopPlace": { ... },
        "Strategy": { ... },
        "CakEmitterConfig": { ... },
        "CakSimpleState": { ... },
        "CakTrigger": { ... },
        "CakMultiplePosition": { ... },
        "CakAudioTable": { ... },
        "WaterTrigger": { ... },
        "NavMeshTrigger": { ... },
        "Affordance": { ... },
        "HiddenBoxTrigger": { ... },
        "NarrowSpaceBoxTrigger": { ... },
        "WarpTrigger": { ... },
        "ActionMarker": { ... },
    }
}

For the majority of actors in the game, most of the fields within ActorSerializeParameter are the same for each type of enemy - that is, all Kochappys in the game have the same Life, ActorParameter, CheckComp, etc. A data scrape of each unique value that each type of enemy can have can be found here. This can be used for sourcing the non-changing values, or for various examples of each byte array for every creature and object. (Generating code is here).

AI

AI is the interesting and important parameter. It controls per-instance parameters for actors such as drops, territory, and configuration for gimmicks. I currently believe that AI is mapped on top of an entity's own blueprint configuration from its Placeables/ file, with AI able to send overrides to parts of it, though I've yet to work out where these bytes go. We can see traces of the Sublevels files showing AI gen variables with overriden base parameters.

Concepts

Before trying to break down a byte array, it helps to understand a few concepts of the data types used:

  • Each integer in the array is a single 8-bit/1-byte value, so can only go up to 255.
  • The arrays are not fixed to 32-bit boundaries. A 4-byte float can be adjacent to a 16bit integer.
  • Booleans are 4-bytes with the 0/1 being in the first index
  • Floats are little-endian, and 4 bytes long. For example the value 100 will be 4 bytes of 0, 0, 200, 66.
  • Integers can also be encoded across 2-bytes (two integers of this array). For example, the value 271 would be 15,1 - 15 + (256 * 1). They are also sometimes just 1-byte long with the value at index 0.
  • Strings are encoded by 4 bytes dictating the length of the string (null terminator included) followed by the string's characters in ASCII codes, ending with 0. For example the string of None will be encoded as 5, 0, 0, 0, 78, 111, 110, 101, 0, with 5 being the length of the string (4 chars + 0).
  • When arrays/objects are used (for fields like DropConditions or bSetTerritory, there is often a 4 byte boolean that dictates if the rest of the object is present, or the number of items in the array. For example, with territory, if bSetTerritory is false, the bytes will look like 0, 0, 0, 0, ... followed by whatever else comes after. However if it is true (1, 0, 0, 0), 5 floats (X, Y, Z, HalfHeight and Radius) will follow, making the array vary in length.

Dandori Desktop provides tools for byte conversions that can aid in finding values.

Charts

Attempts have been made to chart the bytes of various AI parameters to uncover patterns and the locations of values in order to manipulate them.

There are many bytes AFTER the end of the inventory which ends in 4 bytes of 255, 255, 255, 255 (-1), but I have no idea what they do yet. I believe they're probably the overrides for the enemy's AI_GEN_VARIABLE, and other objects like SniffPointParameter.

Creature AI

Details the drop system for a regular enemy (and eggs). The inventory system (DropParameter) used here is nearly universal in the game, with objects having a slightly different structure for some reason, but is mostly the same, so it helps to understand creatures first.

P4-creature-ai-inventory-bytes.png

ActorSpawner AI

ActorSpawners are a little different, and their only drop is the thing they spawn, with some configuration for it.

P4-actorspawner-ai.png

GroupDropManager AI

P4-gdm-ai.png

NoraSpawner AI

P4-noraspawner-ai.png

PortalTrigger Gen Variable

This chart is for the PortalTrigger.Static part of ActorSerializeParameter and will usually be used in conjunction with portal objects like MadoriRuins and DownPortal.

P4-portaltrigger-ai.png

AreaBaseAI Gen Variable

This chart is for the base camps.

P4-areabasecamp-ai.png


SprinklerAI and ObjectAIParameter

Objects mostly seem composed of two objects - ObjectAIParameter and an entity-specific object, like SprinklerAIParameter. The former contains inventories (DropParameter) and some stuff to do with edge collision it seems, while the latter controls everything specific to the entity. The chart below illustrates it for sprinklers.

  • To have a valve that is permanently on - enable `bSprinklerOnly` and set `valveID` to `None`.
  • To connect a valve, match the `valveID` to the valve object's ID. The only valve config I've found works is to use the `Build` workType. This setup will have a sprinkler turn on once for `OpenTime` number of seconds upon valve activation, then turn off - this is how Cave013 douses fire.
  • To have a sprinkler always on, make a `NavMeshTriggerLinkForSplash` object on top of the sprinkler, and make the `NavMeshTriggerID`s for both match. The sprinkler and valve cannot have the same `demoBindName`. I've not played much with these, but I believe they _may_ be to do with saving the state of the sprinkler after the cutscene has played. In my working example, I used `GSprinkler05` and `GValveOnce06` for my `demoBindName`s, and `demoId` of `0`.
P4-sprinkler-objectAI.png

ValveAI

Valves have a few properties in their parameter, but ObjectParameter is mostly unused here. There are some missing values that I haven't found the indexes for, but you can see the default values in FModel's blueprint. The enum values for EValveWorkType are also unknown, but 2 and 6 are confirmed.

P4-valveai.png

NavMeshTriggers

There are 3 types: NavMeshTrigger, NavMeshTriggerClear and NavMeshTriggerLinkForSplash (for sprinklers). My current understanding is that these triggers send pulses to whatever they're linked to on a timer or some conditions. These bytes come from the NavMeshTrigger part of ActorSerializeParameter, and seem to be some of the only objects in the game that use that property. It is worth noting that the AI property for NavMeshTriggerLinkForSplash is identical for every instance of this actor across the game. NavMeshTriggerClear has several different sets of AI bytes though.

P4-navmeshtrigger.png


Researching

The best way of breaking down the AI parameter (I have found) is to combine the blueprint JSONs from the Sublevels/ folder, with the ActorPlacementInfo file of the same type. The Sublevels files contain the gen_variables that seem to be encoded into the byte arrays of ActorSerializeParameter.

AI parameters can be found by searching for {entity}AI_GEN_VARIABLE, for example ElecMushiAI_GEN_VARIABLE for Anode Beetles. Most of the contents of that object's Properties will be encoded in that actor's ActorSerializeParameter.AI.Static in the ActorPlacementInfo file, so you can compare things like the drop numbers, asset path and other such floats for occurrences of those values in the AI array. Strings are often the easiest to find/locate with due to their easy to spot nature (look for the big sequence of non-0 numbers and decode it to see if it matches what you need). Finding two instances in Sublevel files that have different values for a given property (or the property being set at all for infrequent ones) will tell you which actor instances to look for in the AP files, which you can then anaylse the bytes for to find your different value, giving you its array index/type.

A scrape of all of the sublevel files can be found in Dandori Desktop's repo which collects every parameter that each entity ever defines - so every drop, and config param ever given to any Kochappy, for example. This is very useful for finding all the potential values of random things like GameRulePermissionFlag or CustomParameter, and is a great starting point for piecing parts of the AI puzzles together, as params like ObjectAIParameter and TekiAIParameter are shared frequently, with individual properties being set on certain entities. This isn't comprehensive as many properties are just left defaulted, which doesn't mean they can't be changed, just that we don't have 2 differing examples to easily locate them in the bytes.

The blueprints can also be viewed in FModel (in Placeables/), and you can find the AI_GEN_VARIABLEs which should have all the property names and their default values. I've had limited success here, as you often can't just find the value in the AI bytes, but it can definitely give insight into what must be available in the object, and other possible enum values etc. This leads me to think it's possible that non-overriden fields of the gen variable objects are left zeroed, which doesn't make much sense, because how would you set a value of zero? idk.

Data Dumps

  • entityData.json - a dump of every unique value every actor has in the game from the AP files
  • Sublevels.json - a dump of every value each actor can have from the Sublevels files - these are the English overriden parameter names
  • scrape.js - the code to generate the above lists - can be supplied an actor name to extract its section from entityData.json given how bulky that file is. Clone Dandori Desktop and run node scrape.js Kochappy to dump Kochappy's data into the extractions/ folder