Module nari.io.reader.actlogutils.effectresult

Parse effect result data from ACT log line

Expand source code
"""Parse effect result data from ACT log line"""
from struct import unpack

from nari.types import Timestamp
from nari.types.event import Event
from nari.types.actor import Actor
from nari.types.event.effectresult import EffectResult, EffectResultEntry


def effectresult_from_logline(timestamp: Timestamp, params: list[str]) -> Event:
    """Returns an effect result event from an ACT log line

    ACT Event ID (decimal): 37

    ## Param layout from ACT

    The first two params in every event is the ACT event ID and the timestamp it was parsed; the following table documents all the other fields.

    |Index|Type|Description|
    |----:|----|:----------|
    |0    |int|Actor ID|
    |1    |string|Actor name|
    |2    |uint32|Sequence ID|
    |3    |int|Actor current HP|
    |4    |int|Actor max HP|
    |5    |int|Actor current MP|
    |6    |int|Actor max MP|
    |7    |uint8|Shield|
    |8    |null|Blank field|
    |9    |float|Actor X position|
    |10   |float|Actor Y position|
    |11   |float|Actor Z position|
    |12   |float|Actor bearing|
    |13   |int|Unknown|
    |14   |int|Unknown|
    |15   |int|Unknown|
    |16   |int|EffectResult entry count|
    |17-N |EffectResult(s)|Every four fields makes up one EffectResult entry, and there are up to four groups, meaning N <= 29.|

    Each EffectResult entry has the following four fields, in order:

    |Index|Type|Description|
    |----:|----|:----------|
    |0    |int|BBH -> EffectIndex / Padding / EffectId|
    |1    |int|II -> padding / some kind of param|
    |2    |float|Effect duration (as hex)|
    |3    |int|Source actor ID|

    """
    target_actor = Actor(*params[0:2])
    sequence_id = int(params[2], 16)
    try:
        target_actor.resources.update(
            *[int(x) for x in params[3:7]]
        )
        target_actor.position.update(
            *[int(x) for x in params[9:13]]
        )
    except ValueError:
        pass
    try:
        shield_percent = int(params[7], 16)
    except ValueError:
        shield_percent = 0

    effect_result_entries = []
    for i in range(17, len(params)-1, 4): # len(params)-1 because the last param is always blank
        effect_hexdata = int(params[i].zfill(8), 16)
        effect_index, _, effect_id = unpack('>BBH', effect_hexdata.to_bytes(4, 'big'))
        effect_duration, *_ = unpack('<f', int(params[i+2].zfill(8), 16).to_bytes(4, 'little'))
        source_actor_id = int(params[i+3].zfill(4), 16)
        effect_result_entries.append(
            EffectResultEntry(
                effect_index=effect_index,
                effect_id=effect_id,
                effect_duration=effect_duration,
                source_actor_id=source_actor_id
            )
        )
    return EffectResult(
        timestamp=timestamp,
        target_actor=target_actor,
        sequence_id=sequence_id,
        shield_percent=shield_percent,
        effect_results=effect_result_entries,
    )

Functions

def effectresult_from_logline(timestamp: int, params: list[str]) ‑> Event

Returns an effect result event from an ACT log line

ACT Event ID (decimal): 37

Param layout from ACT

The first two params in every event is the ACT event ID and the timestamp it was parsed; the following table documents all the other fields.

Index Type Description
0 int Actor ID
1 string Actor name
2 uint32 Sequence ID
3 int Actor current HP
4 int Actor max HP
5 int Actor current MP
6 int Actor max MP
7 uint8 Shield
8 null Blank field
9 float Actor X position
10 float Actor Y position
11 float Actor Z position
12 float Actor bearing
13 int Unknown
14 int Unknown
15 int Unknown
16 int EffectResult entry count
17-N EffectResult(s) Every four fields makes up one EffectResult entry, and there are up to four groups, meaning N <= 29.

Each EffectResult entry has the following four fields, in order:

Index Type Description
0 int BBH -> EffectIndex / Padding / EffectId
1 int II -> padding / some kind of param
2 float Effect duration (as hex)
3 int Source actor ID
Expand source code
def effectresult_from_logline(timestamp: Timestamp, params: list[str]) -> Event:
    """Returns an effect result event from an ACT log line

    ACT Event ID (decimal): 37

    ## Param layout from ACT

    The first two params in every event is the ACT event ID and the timestamp it was parsed; the following table documents all the other fields.

    |Index|Type|Description|
    |----:|----|:----------|
    |0    |int|Actor ID|
    |1    |string|Actor name|
    |2    |uint32|Sequence ID|
    |3    |int|Actor current HP|
    |4    |int|Actor max HP|
    |5    |int|Actor current MP|
    |6    |int|Actor max MP|
    |7    |uint8|Shield|
    |8    |null|Blank field|
    |9    |float|Actor X position|
    |10   |float|Actor Y position|
    |11   |float|Actor Z position|
    |12   |float|Actor bearing|
    |13   |int|Unknown|
    |14   |int|Unknown|
    |15   |int|Unknown|
    |16   |int|EffectResult entry count|
    |17-N |EffectResult(s)|Every four fields makes up one EffectResult entry, and there are up to four groups, meaning N <= 29.|

    Each EffectResult entry has the following four fields, in order:

    |Index|Type|Description|
    |----:|----|:----------|
    |0    |int|BBH -> EffectIndex / Padding / EffectId|
    |1    |int|II -> padding / some kind of param|
    |2    |float|Effect duration (as hex)|
    |3    |int|Source actor ID|

    """
    target_actor = Actor(*params[0:2])
    sequence_id = int(params[2], 16)
    try:
        target_actor.resources.update(
            *[int(x) for x in params[3:7]]
        )
        target_actor.position.update(
            *[int(x) for x in params[9:13]]
        )
    except ValueError:
        pass
    try:
        shield_percent = int(params[7], 16)
    except ValueError:
        shield_percent = 0

    effect_result_entries = []
    for i in range(17, len(params)-1, 4): # len(params)-1 because the last param is always blank
        effect_hexdata = int(params[i].zfill(8), 16)
        effect_index, _, effect_id = unpack('>BBH', effect_hexdata.to_bytes(4, 'big'))
        effect_duration, *_ = unpack('<f', int(params[i+2].zfill(8), 16).to_bytes(4, 'little'))
        source_actor_id = int(params[i+3].zfill(4), 16)
        effect_result_entries.append(
            EffectResultEntry(
                effect_index=effect_index,
                effect_id=effect_id,
                effect_duration=effect_duration,
                source_actor_id=source_actor_id
            )
        )
    return EffectResult(
        timestamp=timestamp,
        target_actor=target_actor,
        sequence_id=sequence_id,
        shield_percent=shield_percent,
        effect_results=effect_result_entries,
    )