@axiapps/axilog 0.3.2 → 0.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +169 -21
- package/index.d.ts +37 -18
- package/index.js +54 -54
- package/package.json +6 -6
- package/types.d.ts +1281 -0
package/types.d.ts
CHANGED
|
@@ -58,6 +58,33 @@ export interface TeamOut {
|
|
|
58
58
|
team_id: number
|
|
59
59
|
/** Stable content GUID for this team, when known (omitted, not null, when absent). */
|
|
60
60
|
guid?: string
|
|
61
|
+
/**
|
|
62
|
+
* This team's WvW world/shard id from `CBTS_WVWTEAMS`, omitted the same
|
|
63
|
+
* way. Absent for logs predating that event and for any team the event
|
|
64
|
+
* does not name -- a team can have a `color` (resolved from the static
|
|
65
|
+
* fallback id table) but no `shard_id`.
|
|
66
|
+
*/
|
|
67
|
+
shard_id?: number
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** One ownership observation. `time_ms` is log-relative milliseconds. */
|
|
71
|
+
export interface ObjectiveOwnerOut {
|
|
72
|
+
team_id: number
|
|
73
|
+
time_ms: number
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* One WvW objective's ownership timeline, from
|
|
78
|
+
* `CBTS_WVWOBJECTIVESTATUS`. Never carries an `"Unknown"`
|
|
79
|
+
* `objective_type`: an objective the static catalog cannot type is dropped
|
|
80
|
+
* rather than emitted untyped. `owners` keeps repeats rather than
|
|
81
|
+
* collapsing them, matching GW2EI.
|
|
82
|
+
*/
|
|
83
|
+
export interface ObjectiveOut {
|
|
84
|
+
map_id: number
|
|
85
|
+
objective_id: number
|
|
86
|
+
objective_type: 'Camp' | 'Ruins' | 'Tower' | 'Keep' | 'Castle'
|
|
87
|
+
owners: ObjectiveOwnerOut[]
|
|
61
88
|
}
|
|
62
89
|
|
|
63
90
|
export interface PerEnemyOut {
|
|
@@ -73,6 +100,15 @@ export interface PerEnemyOut {
|
|
|
73
100
|
* last-hit attributions, matching GW2EI. `downs_contribution_damage` is the
|
|
74
101
|
* arcdps-methodology per-target down-contribution, NOT EI's own
|
|
75
102
|
* 90%-to-downstate-window algorithm.
|
|
103
|
+
*
|
|
104
|
+
* Phase B (native-format-gap-closure Task 4) widened this from 8 fields to
|
|
105
|
+
* 24. `direct_damage` is EI's per-target `directDmg` -- the damage sum
|
|
106
|
+
* over `is_direct_hit` rows against this one target -- and is NOT the same
|
|
107
|
+
* quantity as this schema's `connected_direct_dmg` elsewhere (a different,
|
|
108
|
+
* whole-fight-only figure); collapsing the two would silently substitute
|
|
109
|
+
* the wrong number under a plausible-looking name. `critable_direct_count`
|
|
110
|
+
* is native-only: it is `criticalRate`'s denominator, which real EI never
|
|
111
|
+
* publishes per target, so it has no `statsTargets` counterpart.
|
|
76
112
|
*/
|
|
77
113
|
export interface PerTargetStatsOut {
|
|
78
114
|
enemy_id: number
|
|
@@ -83,6 +119,22 @@ export interface PerTargetStatsOut {
|
|
|
83
119
|
killed: number
|
|
84
120
|
interrupts: number
|
|
85
121
|
downs_contribution_damage: number
|
|
122
|
+
direct_count: number
|
|
123
|
+
direct_damage: number
|
|
124
|
+
crit_count: number
|
|
125
|
+
crit_damage: number
|
|
126
|
+
flank_count: number
|
|
127
|
+
glance_count: number
|
|
128
|
+
critable_direct_count: number
|
|
129
|
+
against_downed_damage: number
|
|
130
|
+
missed: number
|
|
131
|
+
evaded: number
|
|
132
|
+
blocked: number
|
|
133
|
+
invulned: number
|
|
134
|
+
applied_total: number
|
|
135
|
+
applied_duration_ms: number
|
|
136
|
+
applied_downs_contribution: number
|
|
137
|
+
applied_duration_downs_contribution_ms: number
|
|
86
138
|
}
|
|
87
139
|
|
|
88
140
|
export interface DamageOut {
|
|
@@ -531,6 +583,17 @@ export interface SkillMapEntryOut {
|
|
|
531
583
|
auto_attack?: boolean
|
|
532
584
|
is_swap: boolean
|
|
533
585
|
can_crit: boolean
|
|
586
|
+
/**
|
|
587
|
+
* MPROC. All five are OMITTED when false -- a proc flag is rare, and
|
|
588
|
+
* emitting ~370 x 5 literal `false`s cost 16% of the report. Absence
|
|
589
|
+
* means false, not unknown. `is_instant_cast` is the strong one: a
|
|
590
|
+
* finder actually fired in this log, not merely that one was available.
|
|
591
|
+
*/
|
|
592
|
+
is_trait_proc?: boolean
|
|
593
|
+
is_gear_proc?: boolean
|
|
594
|
+
is_unconditional_proc?: boolean
|
|
595
|
+
is_not_accurate?: boolean
|
|
596
|
+
is_instant_cast?: boolean
|
|
534
597
|
}
|
|
535
598
|
|
|
536
599
|
export interface EnemyOut {
|
|
@@ -577,8 +640,17 @@ export interface EncounterOut {
|
|
|
577
640
|
teams: TeamOut[]
|
|
578
641
|
/** Every `CBTS_MARKER` assignment observed in the log, across all agents -- not just squad/enemy players. Always present (possibly empty), never omitted. */
|
|
579
642
|
markers: MarkerAssignmentOut[]
|
|
643
|
+
/** WvW objective ownership timelines. Empty (not omitted) for non-WvW logs and for logs predating `CBTS_WVWOBJECTIVESTATUS`. */
|
|
644
|
+
objectives: ObjectiveOut[]
|
|
580
645
|
/** Omitted entirely (not `null`) when the log has fewer than two `CBTS_TICK` events. */
|
|
581
646
|
tick_rate?: TickRateOut
|
|
647
|
+
/**
|
|
648
|
+
* Wall-clock log start, **seconds** since the Unix epoch, from arcdps's
|
|
649
|
+
* `CBTS_LOGSTART`. Omitted entirely (not `null`) when the log carries no
|
|
650
|
+
* such event -- absence stays distinguishable from epoch zero, so do not
|
|
651
|
+
* default it to `0`.
|
|
652
|
+
*/
|
|
653
|
+
started_at_unix?: number
|
|
582
654
|
}
|
|
583
655
|
|
|
584
656
|
/** Min/max `x`/`y` observed across every `ReplayOut.tracks[].samples` -- lets a consumer size a viewBox without a second pass over `tracks`. */
|
|
@@ -699,3 +771,1212 @@ export interface Report {
|
|
|
699
771
|
*/
|
|
700
772
|
damage_mod_map?: Record<string, DamageModDescOut>
|
|
701
773
|
}
|
|
774
|
+
|
|
775
|
+
/* -------------------------------------------------------------------- */
|
|
776
|
+
/* Native format 1.0 (`axilog_schema::v1::ReportV1`), Task 12 */
|
|
777
|
+
/* -------------------------------------------------------------------- */
|
|
778
|
+
|
|
779
|
+
/**
|
|
780
|
+
* Hand-transcribed types for `axilog_schema::v1::ReportV1`
|
|
781
|
+
* (`crates/axilog-schema/src/v1/{mod.rs,envelope.rs,entities.rs,catalogs.rs,
|
|
782
|
+
* series.rs,blocks/*.rs}`), the native output format 1.0 container.
|
|
783
|
+
* `parseFile`/`parseBuffer` now return THIS shape, not the legacy `Report`
|
|
784
|
+
* above (kept in place, unused by these two entry points as of Task 12, but
|
|
785
|
+
* left untouched -- see this file's own top comment). `parseFileEi` is
|
|
786
|
+
* unaffected and keeps returning the EI-compatibility JSON typed `any` in
|
|
787
|
+
* `index.d.ts`.
|
|
788
|
+
*
|
|
789
|
+
* Same numeric-field convention as above: every Rust `u64`/`u32`/`f64`/`i32`
|
|
790
|
+
* field is `number`, exact for every value this schema actually produces but
|
|
791
|
+
* lossy in principle for a `u64` beyond 2^53.
|
|
792
|
+
*/
|
|
793
|
+
|
|
794
|
+
/** The FORMAT contract version plus the producing binary's version and the input file name. */
|
|
795
|
+
export interface AxilogMeta {
|
|
796
|
+
/** The FORMAT contract version. Moves independently of `version`. Currently always `"1.0"`. */
|
|
797
|
+
schema: string
|
|
798
|
+
/** The binary that produced this document (`CARGO_PKG_VERSION`). */
|
|
799
|
+
version: string
|
|
800
|
+
/** The input log's file NAME, never a full path. Omitted when unknown (e.g. `parseBuffer`, which has no file name to offer). */
|
|
801
|
+
generated_from?: string
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
/**
|
|
805
|
+
* One `CBTS_MARKER` assignment, native 1.0 shape. `agent_addr` is always
|
|
806
|
+
* present (arcdps does not restrict `CBTS_MARKER` to squad members, and many
|
|
807
|
+
* carrying agents never become tracked entities); `entity_id` is present
|
|
808
|
+
* only when the agent resolves to a roster entity.
|
|
809
|
+
*/
|
|
810
|
+
export interface MarkerAssignmentOutV1 {
|
|
811
|
+
/** The entity this marker is on. Absent for agents that carry markers but are not tracked entities. */
|
|
812
|
+
entity_id?: number
|
|
813
|
+
agent_addr: number
|
|
814
|
+
marker: string
|
|
815
|
+
time_ms: number
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
/**
|
|
819
|
+
* The 1.0 encounter envelope -- a reprojection of the legacy `EncounterOut`
|
|
820
|
+
* with `markers[]` rekeyed from `agent_addr` to `entity_id`.
|
|
821
|
+
*/
|
|
822
|
+
export interface EncounterOutV1 {
|
|
823
|
+
kind: string
|
|
824
|
+
map: string
|
|
825
|
+
duration_ms: number
|
|
826
|
+
build: string
|
|
827
|
+
revision: number
|
|
828
|
+
/** The recording player, as an entity id. Omitted when the recorder does not resolve to an entity. */
|
|
829
|
+
recorded_by?: number
|
|
830
|
+
teams: TeamOut[]
|
|
831
|
+
markers: MarkerAssignmentOutV1[]
|
|
832
|
+
/** Carried verbatim from the legacy shape -- an objective belongs to the map, not to an entity, so unlike `markers` there is no join key to rekey. */
|
|
833
|
+
objectives: ObjectiveOut[]
|
|
834
|
+
tick_rate?: TickRateOut
|
|
835
|
+
/**
|
|
836
|
+
* Wall-clock log start, **seconds** since the Unix epoch, from arcdps's
|
|
837
|
+
* `CBTS_LOGSTART`. Omitted when the log carries no such event -- absence
|
|
838
|
+
* is deliberately distinguishable from epoch zero, so do not default it
|
|
839
|
+
* to `0`. Multiply by 1000 for a JS `Date`.
|
|
840
|
+
*/
|
|
841
|
+
started_at_unix?: number
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
/**
|
|
845
|
+
* What an entity IS -- squad member, non-squad friendly, enemy player, or
|
|
846
|
+
* NPC/gadget. Declaration order is `entities[]`'s sort order.
|
|
847
|
+
*/
|
|
848
|
+
export type Role = 'squad' | 'friendly_player' | 'enemy_player' | 'npc'
|
|
849
|
+
|
|
850
|
+
export interface CommanderOut {
|
|
851
|
+
variant: string
|
|
852
|
+
guid: string
|
|
853
|
+
/**
|
|
854
|
+
* Terminated, half-open `[tag-on, tag-off)` windows in **arcdps session
|
|
855
|
+
* time** -- the same base as `encounter.markers[].time_ms`, NOT
|
|
856
|
+
* log-relative and NOT comparable against `encounter.duration_ms`. Do not
|
|
857
|
+
* clip these to `[0, duration_ms]`; subtract the log's own `t0` first if
|
|
858
|
+
* you need encounter-relative values.
|
|
859
|
+
*
|
|
860
|
+
* Literal per-instance holds, not a coalesced whole-fight span: a
|
|
861
|
+
* zero-width `[t, t]` pair from a same-timestamp reassignment is normal.
|
|
862
|
+
* An empty array on a PRESENT `commander` means the tag was detected but
|
|
863
|
+
* its windows could not be resolved -- not that the player never
|
|
864
|
+
* commanded.
|
|
865
|
+
*/
|
|
866
|
+
segments: [number, number][]
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
/**
|
|
870
|
+
* One agent's IDENTITY only -- no statistics; those live in `blocks`, keyed
|
|
871
|
+
* by `id`. The single place account and character names appear, so a PII
|
|
872
|
+
* scrub is one pass rather than a hunt through nested structures.
|
|
873
|
+
*/
|
|
874
|
+
export interface EntityOut {
|
|
875
|
+
/** Dense index into `entities[]`, from 0. Stable WITHIN a report only -- join across logs on `account`. */
|
|
876
|
+
id: number
|
|
877
|
+
role: Role
|
|
878
|
+
/** Players only. */
|
|
879
|
+
account?: string
|
|
880
|
+
/** Players only. */
|
|
881
|
+
character?: string
|
|
882
|
+
/** Non-player entities only. */
|
|
883
|
+
name?: string
|
|
884
|
+
/** Present exactly for player roles. */
|
|
885
|
+
profession?: string
|
|
886
|
+
/** Empty string when the agent has no elite spec, or one this project cannot name. Never a numeric spec id. */
|
|
887
|
+
elite_spec?: string
|
|
888
|
+
team: string
|
|
889
|
+
subgroup?: number
|
|
890
|
+
commander?: CommanderOut
|
|
891
|
+
guild_id?: string
|
|
892
|
+
marker?: string
|
|
893
|
+
/** The arcdps agent address -- a documented attribute, not a secret. */
|
|
894
|
+
agent_addr: number
|
|
895
|
+
instid?: number
|
|
896
|
+
/**
|
|
897
|
+
* Whether this entity interacted with the squad at all (dealt damage to
|
|
898
|
+
* the squad, took damage from the squad, or took CC from the squad).
|
|
899
|
+
* Always `true` for squad members and non-squad friendlies. Never
|
|
900
|
+
* optional -- absence would be ambiguous between "did not participate"
|
|
901
|
+
* and "not computed".
|
|
902
|
+
*/
|
|
903
|
+
combat_participant: boolean
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
/** Definition metadata for one referenced skill id. */
|
|
907
|
+
export interface SkillEntry {
|
|
908
|
+
name: string
|
|
909
|
+
/**
|
|
910
|
+
* Render-service or wiki URL (Phase C), resolved from `skill_icons` (the
|
|
911
|
+
* GW2 API) first and `buff_icons` (GW2EI's own table) second; omitted
|
|
912
|
+
* when neither knows the id. Buff ids resolve here too -- there is no
|
|
913
|
+
* separate icon field on `BuffEntry`.
|
|
914
|
+
*/
|
|
915
|
+
icon?: string
|
|
916
|
+
is_swap: boolean
|
|
917
|
+
can_crit: boolean
|
|
918
|
+
auto_attack?: boolean
|
|
919
|
+
/**
|
|
920
|
+
* MPROC. All five are OMITTED when false -- a proc flag is rare, and
|
|
921
|
+
* emitting ~370 x 5 literal `false`s cost 16% of the report. Absence
|
|
922
|
+
* means false, not unknown. `is_instant_cast` is the strong one: a
|
|
923
|
+
* finder actually fired in this log, not merely that one was available.
|
|
924
|
+
*/
|
|
925
|
+
is_trait_proc?: boolean
|
|
926
|
+
is_gear_proc?: boolean
|
|
927
|
+
is_unconditional_proc?: boolean
|
|
928
|
+
is_not_accurate?: boolean
|
|
929
|
+
is_instant_cast?: boolean
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
/**
|
|
933
|
+
* Definition metadata for one referenced buff id. `kind` is GW2's real
|
|
934
|
+
* three-way taxonomy (arcdps does not distinguish these structurally).
|
|
935
|
+
*/
|
|
936
|
+
export interface BuffEntry {
|
|
937
|
+
name: string
|
|
938
|
+
kind: 'condition' | 'boon' | 'effect'
|
|
939
|
+
stacking: 'intensity' | 'duration'
|
|
940
|
+
max_stacks?: number
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
/** Definition metadata for one referenced damage-modifier id. */
|
|
944
|
+
export interface DamageModEntry {
|
|
945
|
+
name: string
|
|
946
|
+
icon?: string
|
|
947
|
+
/**
|
|
948
|
+
* EI's full tooltip, including its derived `<br>Applied on ...` /
|
|
949
|
+
* `<br>Counter` / `<br>Approximate` suffixes.
|
|
950
|
+
*/
|
|
951
|
+
description?: string
|
|
952
|
+
non_multiplier?: boolean
|
|
953
|
+
is_counter?: boolean
|
|
954
|
+
skill_based?: boolean
|
|
955
|
+
approximate?: boolean
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
/**
|
|
959
|
+
* Definition metadata for one minion group referenced by `MinionsBlock`.
|
|
960
|
+
* Identity lives here rather than on the row, like every other catalog in
|
|
961
|
+
* this format.
|
|
962
|
+
*/
|
|
963
|
+
export interface MinionEntry {
|
|
964
|
+
species_id: number
|
|
965
|
+
name: string
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
/**
|
|
969
|
+
* Definition metadata for every id any block references. No human-readable
|
|
970
|
+
* name appears outside `catalogs` or `entities` -- every block references
|
|
971
|
+
* integers instead. Keys serialize as decimal strings.
|
|
972
|
+
*/
|
|
973
|
+
export interface Catalogs {
|
|
974
|
+
skills: Record<string, SkillEntry>
|
|
975
|
+
buffs: Record<string, BuffEntry>
|
|
976
|
+
/** Omitted entirely (not `{}`) when no damage-modifier id was referenced (e.g. `modifiers` was not requested). */
|
|
977
|
+
damage_mods?: Record<string, DamageModEntry>
|
|
978
|
+
/** Omitted entirely (not `{}`) when no minion id was referenced (e.g. `skillDamage` was not requested). */
|
|
979
|
+
minions?: Record<string, MinionEntry>
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
/**
|
|
983
|
+
* One time series in the format's single series envelope. `enc` is `"raw"`
|
|
984
|
+
* (a plain array of values) or `"rle"` (an array of `[value, run_length]`
|
|
985
|
+
* pairs), chosen per series by whichever serializes smaller. `len` is the
|
|
986
|
+
* DECODED length in both cases (NOT `data.length`), so a consumer can
|
|
987
|
+
* allocate before decoding and validate after.
|
|
988
|
+
*/
|
|
989
|
+
export interface SeriesOut {
|
|
990
|
+
interval_ms: number
|
|
991
|
+
/** Decoded length, NOT `data.length`. */
|
|
992
|
+
len: number
|
|
993
|
+
enc: 'raw' | 'rle'
|
|
994
|
+
data: unknown[]
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
/** Why a block is or is not present in `blocks`. */
|
|
998
|
+
/**
|
|
999
|
+
* Why a block is or is not in `blocks`.
|
|
1000
|
+
*
|
|
1001
|
+
* - `present` -- computed, carried, at least one row.
|
|
1002
|
+
* - `not_computed` -- the compute gate was off. NOT the same as empty:
|
|
1003
|
+
* it is a missing flag, not a fact about the log.
|
|
1004
|
+
* - `empty` -- computed and genuinely nothing to report. The block is
|
|
1005
|
+
* still carried; only `not_computed`/`unsupported` omit it.
|
|
1006
|
+
* - `unsupported` -- RESERVED. No code path in this version emits it:
|
|
1007
|
+
* nothing this container computes is era- or encounter-kind-gated.
|
|
1008
|
+
* Named now so spec #2's era-gated surfaces can fill the slot without
|
|
1009
|
+
* consumers having to learn a new value late. Handle it, but do not
|
|
1010
|
+
* expect it from this version.
|
|
1011
|
+
*/
|
|
1012
|
+
export type CoverageState = 'present' | 'not_computed' | 'empty' | 'unsupported'
|
|
1013
|
+
|
|
1014
|
+
/**
|
|
1015
|
+
* Per-block computation/presence status, keyed by block name
|
|
1016
|
+
* (`"damage"`, `"defenses"`, `"hit_stats"`, `"cc"`, `"boons"`, `"support"`,
|
|
1017
|
+
* `"contribution"`, `"healing"`, `"rotation"`, `"damage_mods"`,
|
|
1018
|
+
* `"missiles"`, `"replay"`, `"series"`, `"conditions"`, `"minions"`).
|
|
1019
|
+
* Always names every known block, including ones a given parse did not
|
|
1020
|
+
* compute -- those read `"not_computed"`.
|
|
1021
|
+
*
|
|
1022
|
+
* One entry is narrower than it looks: `"boons"` reports on that block's
|
|
1023
|
+
* always-on uptime half only, NOT on its `{ timeseries: true }` stack
|
|
1024
|
+
* timelines (see `BoonsBlock`). `"replay"` is the same shape of exception
|
|
1025
|
+
* (see `ReplayBlock`).
|
|
1026
|
+
*/
|
|
1027
|
+
export type Coverage = Record<string, CoverageState>
|
|
1028
|
+
|
|
1029
|
+
export type Severity = 'info' | 'warn' | 'error'
|
|
1030
|
+
|
|
1031
|
+
/**
|
|
1032
|
+
* A structured, user-facing analysis warning. `code` is a closed,
|
|
1033
|
+
* documented set: adding a code is additive, changing one's meaning is a
|
|
1034
|
+
* break.
|
|
1035
|
+
*/
|
|
1036
|
+
export interface WarningOut {
|
|
1037
|
+
code: string
|
|
1038
|
+
severity: Severity
|
|
1039
|
+
message: string
|
|
1040
|
+
/** The entity this warning is about, when it is about one. */
|
|
1041
|
+
entity_id?: number
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
/** The uniform entity-keyed map every block uses. Keys serialize as decimal strings. */
|
|
1045
|
+
export type ByEntity<T> = Record<string, T>
|
|
1046
|
+
|
|
1047
|
+
/* --- damage block -------------------------------------------------------- */
|
|
1048
|
+
|
|
1049
|
+
/**
|
|
1050
|
+
* Aggregates `Role::Squad` entities ONLY -- not every friendly player (see
|
|
1051
|
+
* `by_entity`, which is the full roster).
|
|
1052
|
+
*/
|
|
1053
|
+
export interface DamageSquad {
|
|
1054
|
+
total: number
|
|
1055
|
+
dps: number
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
/**
|
|
1059
|
+
* Mirrors the legacy `SkillEntryOut` field-for-field. `hits`/`min`/`max`
|
|
1060
|
+
* count only CONTRIBUTING (`dmg > 0`) events. `crit_hits`/`flank_hits` are
|
|
1061
|
+
* hit COUNTS, not damage sums.
|
|
1062
|
+
*/
|
|
1063
|
+
export interface SkillRow {
|
|
1064
|
+
total: number
|
|
1065
|
+
/**
|
|
1066
|
+
* CONTRIBUTING (`dmg > 0`) row count. Present on player rows, ABSENT on
|
|
1067
|
+
* enemy rows -- those come from a different pass that never computes it,
|
|
1068
|
+
* so nothing divides `total` by a fabricated denominator.
|
|
1069
|
+
*/
|
|
1070
|
+
hits?: number
|
|
1071
|
+
/**
|
|
1072
|
+
* `HasHit` row count -- GW2EI's `connectedHits`, which counts a
|
|
1073
|
+
* connecting hit that dealt zero health damage and excludes the
|
|
1074
|
+
* blocked/evaded/missed/invulned cases. A THIRD distinct quantity from
|
|
1075
|
+
* `hits` and `outcomes.attempt_hits`; they are separate fields so a
|
|
1076
|
+
* consumer cannot divide by the wrong denominator.
|
|
1077
|
+
*/
|
|
1078
|
+
connected_hits?: number
|
|
1079
|
+
min: number
|
|
1080
|
+
max: number
|
|
1081
|
+
/** Hit COUNTS, not damage sums. */
|
|
1082
|
+
crit_hits: number
|
|
1083
|
+
flank_hits: number
|
|
1084
|
+
/**
|
|
1085
|
+
* Present on PLAYER rows when `skillDamage` is on, absent on enemy rows.
|
|
1086
|
+
* Absent means "this pass did not measure this row", never "every
|
|
1087
|
+
* attempt connected".
|
|
1088
|
+
*/
|
|
1089
|
+
outcomes?: SkillOutcomeCols
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
/**
|
|
1093
|
+
* The hit-OUTCOME breakdown of one `SkillRow`.
|
|
1094
|
+
*
|
|
1095
|
+
* `glance`/`missed`/`evaded`/`blocked` are zero on a condition skill (EI
|
|
1096
|
+
* zeroes them inside its `if (!IndirectDamage)` guard); `invulned` is NOT,
|
|
1097
|
+
* because a condition tick can land on an invulnerable target and EI
|
|
1098
|
+
* counts it.
|
|
1099
|
+
*/
|
|
1100
|
+
export interface SkillOutcomeCols {
|
|
1101
|
+
/** GW2EI's own `hits`: every row that is not one of its no-damage markers. */
|
|
1102
|
+
attempt_hits: number
|
|
1103
|
+
glance: number
|
|
1104
|
+
missed: number
|
|
1105
|
+
evaded: number
|
|
1106
|
+
blocked: number
|
|
1107
|
+
invulned: number
|
|
1108
|
+
interrupted: number
|
|
1109
|
+
/**
|
|
1110
|
+
* This skill produced at least one non-direct (condition) damage row.
|
|
1111
|
+
* Every strike-damage surface downstream uses it as a skip filter.
|
|
1112
|
+
*/
|
|
1113
|
+
indirect: boolean
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
/**
|
|
1117
|
+
* Mirrors the legacy `PerTargetStatsOut` field-for-field, minus `enemy_id`
|
|
1118
|
+
* (the enclosing map's key here, as the target's ENTITY id). `interrupts`
|
|
1119
|
+
* and `downs_contribution_damage` are not derivable from any other block.
|
|
1120
|
+
*
|
|
1121
|
+
* Phase B (native-format-gap-closure Task 4) widened this from 7 fields to
|
|
1122
|
+
* 23, mirroring `PerTargetStatsOut`'s own widening above -- see that
|
|
1123
|
+
* interface's doc comment for the `direct_damage`/`connected_direct_dmg`
|
|
1124
|
+
* distinction and why `critable_direct_count` has no ei-json counterpart.
|
|
1125
|
+
*/
|
|
1126
|
+
export interface PerTargetDetail {
|
|
1127
|
+
connected_hits: number
|
|
1128
|
+
connected_damage: number
|
|
1129
|
+
against_downed_count: number
|
|
1130
|
+
downed: number
|
|
1131
|
+
killed: number
|
|
1132
|
+
interrupts: number
|
|
1133
|
+
/**
|
|
1134
|
+
* arcdps-methodology down-contribution DAMAGE for downs of this specific
|
|
1135
|
+
* target -- NOT GW2EI's 90%-to-downstate-window algorithm.
|
|
1136
|
+
*/
|
|
1137
|
+
downs_contribution_damage: number
|
|
1138
|
+
direct_count: number
|
|
1139
|
+
direct_damage: number
|
|
1140
|
+
crit_count: number
|
|
1141
|
+
crit_damage: number
|
|
1142
|
+
flank_count: number
|
|
1143
|
+
glance_count: number
|
|
1144
|
+
critable_direct_count: number
|
|
1145
|
+
against_downed_damage: number
|
|
1146
|
+
missed: number
|
|
1147
|
+
evaded: number
|
|
1148
|
+
blocked: number
|
|
1149
|
+
invulned: number
|
|
1150
|
+
applied_total: number
|
|
1151
|
+
applied_duration_ms: number
|
|
1152
|
+
applied_downs_contribution: number
|
|
1153
|
+
applied_duration_downs_contribution_ms: number
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
/**
|
|
1157
|
+
* One `(entity, target)` pair. `total` is ungated; `detail` and `by_skill`
|
|
1158
|
+
* come from the `skillDamage: true`-gated families, so a row can
|
|
1159
|
+
* legitimately carry `total` alone.
|
|
1160
|
+
*/
|
|
1161
|
+
export interface PerTarget {
|
|
1162
|
+
total: number
|
|
1163
|
+
/**
|
|
1164
|
+
* Grouped under one key rather than flattened so its absence has a
|
|
1165
|
+
* single unambiguous signal instead of 23 fabricated zeros.
|
|
1166
|
+
*/
|
|
1167
|
+
detail?: PerTargetDetail
|
|
1168
|
+
/** Per-(entity, target, skill) outgoing damage, keyed by skill id. */
|
|
1169
|
+
by_skill?: Record<string, SkillRow>
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
export interface DamageEntity {
|
|
1173
|
+
total: number
|
|
1174
|
+
dps: number
|
|
1175
|
+
taken: number
|
|
1176
|
+
/**
|
|
1177
|
+
* Enemy players this entity landed the DOWNING blow on. An outgoing
|
|
1178
|
+
* outcome; the incoming mirrors are `DefensesEntity.downs_taken`/
|
|
1179
|
+
* `deaths`, the same split GW2EI makes.
|
|
1180
|
+
*/
|
|
1181
|
+
downs_dealt: number
|
|
1182
|
+
/** Enemy players this entity landed the KILLING blow on. */
|
|
1183
|
+
kills_dealt: number
|
|
1184
|
+
/**
|
|
1185
|
+
* Breakbar damage this entity DEALT. Outgoing, hence here rather than on
|
|
1186
|
+
* `defenses`, whose `breakbar_count`/`breakbar_damage` are its INCOMING
|
|
1187
|
+
* mirror. Feeds ei-json's `dpsAll[0].breakbarDamage`.
|
|
1188
|
+
*/
|
|
1189
|
+
breakbar_damage_dealt: number
|
|
1190
|
+
/**
|
|
1191
|
+
* Keyed by the TARGET's entity id. Sparse; omitted when empty. A UNION of
|
|
1192
|
+
* three legacy per-enemy families, so a target can appear with `detail`
|
|
1193
|
+
* and no damage `total`.
|
|
1194
|
+
*/
|
|
1195
|
+
per_target?: Record<string, PerTarget>
|
|
1196
|
+
/** OUTGOING per-skill damage, keyed by skill id. Present only when the per-skill compute gate (`skillDamage: true`) was on. */
|
|
1197
|
+
by_skill?: Record<string, SkillRow>
|
|
1198
|
+
/** INCOMING per-skill damage, keyed by skill id -- mirroring this row's own `total`/`taken` pair. Same gate as `by_skill`. */
|
|
1199
|
+
by_skill_taken?: Record<string, SkillRow>
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1202
|
+
export interface DamageBlock {
|
|
1203
|
+
squad: DamageSquad
|
|
1204
|
+
by_entity: ByEntity<DamageEntity>
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1207
|
+
/* --- defenses / hit_stats / cc blocks ------------------------------------ */
|
|
1208
|
+
|
|
1209
|
+
/** Incoming defenses. Mirrors the legacy `DefensesOut` field-for-field. */
|
|
1210
|
+
export interface DefensesEntity {
|
|
1211
|
+
blocked_count: number
|
|
1212
|
+
evaded_count: number
|
|
1213
|
+
dodge_count: number
|
|
1214
|
+
missed_count: number
|
|
1215
|
+
interrupted_count: number
|
|
1216
|
+
invulned_count: number
|
|
1217
|
+
strike_count: number
|
|
1218
|
+
strike_damage: number
|
|
1219
|
+
power_count: number
|
|
1220
|
+
power_damage: number
|
|
1221
|
+
condition_count: number
|
|
1222
|
+
condition_damage: number
|
|
1223
|
+
life_leech_count: number
|
|
1224
|
+
life_leech_damage: number
|
|
1225
|
+
barrier_count: number
|
|
1226
|
+
barrier_damage: number
|
|
1227
|
+
breakbar_count: number
|
|
1228
|
+
breakbar_damage: number
|
|
1229
|
+
/** Incoming crowd control. */
|
|
1230
|
+
received_cc_count: number
|
|
1231
|
+
received_cc_duration_ms: number
|
|
1232
|
+
/** Boons stripped OFF this player -- the incoming counterpart of `SupportEntity.strips`. */
|
|
1233
|
+
boon_strips_taken: number
|
|
1234
|
+
boon_strips_taken_duration_ms: number
|
|
1235
|
+
/**
|
|
1236
|
+
* Times this entity entered downstate -- GW2EI's
|
|
1237
|
+
* `defenses[0].downCount`. The outgoing mirrors live on
|
|
1238
|
+
* `DamageEntity.downs_dealt`/`kills_dealt`.
|
|
1239
|
+
*/
|
|
1240
|
+
downs_taken: number
|
|
1241
|
+
/** Times this entity died -- GW2EI's `defenses[0].deadCount`. */
|
|
1242
|
+
deaths: number
|
|
1243
|
+
}
|
|
1244
|
+
|
|
1245
|
+
export interface DefensesBlock {
|
|
1246
|
+
by_entity: ByEntity<DefensesEntity>
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1249
|
+
/**
|
|
1250
|
+
* Outgoing hit quality. Mirrors the legacy `HitStatsOut` field-for-field.
|
|
1251
|
+
* `above90_*` counts/damage are against a target at or above 90% health.
|
|
1252
|
+
*/
|
|
1253
|
+
export interface HitStatsEntity {
|
|
1254
|
+
crit_count: number
|
|
1255
|
+
crit_damage: number
|
|
1256
|
+
flank_count: number
|
|
1257
|
+
glance_count: number
|
|
1258
|
+
moving_count: number
|
|
1259
|
+
connected_count: number
|
|
1260
|
+
connected_damage: number
|
|
1261
|
+
direct_count: number
|
|
1262
|
+
direct_damage: number
|
|
1263
|
+
condition_count: number
|
|
1264
|
+
condition_damage: number
|
|
1265
|
+
critable_direct_count: number
|
|
1266
|
+
against_downed_count: number
|
|
1267
|
+
against_downed_damage: number
|
|
1268
|
+
life_leech_count: number
|
|
1269
|
+
life_leech_damage: number
|
|
1270
|
+
above90_power_count: number
|
|
1271
|
+
above90_power_damage: number
|
|
1272
|
+
above90_condition_count: number
|
|
1273
|
+
above90_condition_damage: number
|
|
1274
|
+
}
|
|
1275
|
+
|
|
1276
|
+
export interface HitStatsBlock {
|
|
1277
|
+
by_entity: ByEntity<HitStatsEntity>
|
|
1278
|
+
}
|
|
1279
|
+
|
|
1280
|
+
/** Aggregates `Role::Squad` entities ONLY. */
|
|
1281
|
+
export interface CcSquad {
|
|
1282
|
+
applied_total: number
|
|
1283
|
+
applied_duration_ms: number
|
|
1284
|
+
}
|
|
1285
|
+
|
|
1286
|
+
/**
|
|
1287
|
+
* Mirrors the legacy `CcOut` field-for-field. Incoming CC lives on
|
|
1288
|
+
* `DefensesEntity.received_cc_count`/`received_cc_duration_ms` instead.
|
|
1289
|
+
*/
|
|
1290
|
+
export interface CcEntity {
|
|
1291
|
+
applied_total: number
|
|
1292
|
+
applied_duration_ms: number
|
|
1293
|
+
stun_breaks: number
|
|
1294
|
+
removed_stun_duration_ms: number
|
|
1295
|
+
}
|
|
1296
|
+
|
|
1297
|
+
export interface CcBlock {
|
|
1298
|
+
squad: CcSquad
|
|
1299
|
+
by_entity: ByEntity<CcEntity>
|
|
1300
|
+
}
|
|
1301
|
+
|
|
1302
|
+
/* --- boons / support / contribution / healing blocks --------------------- */
|
|
1303
|
+
|
|
1304
|
+
/**
|
|
1305
|
+
* Mirrors the legacy `GenerationOut` field-for-field. WASTED fields are
|
|
1306
|
+
* rounded to 3 decimals and omitted when exactly zero -- read them as 0
|
|
1307
|
+
* when absent.
|
|
1308
|
+
*/
|
|
1309
|
+
export interface GenerationRow {
|
|
1310
|
+
self_pct: number
|
|
1311
|
+
group_pct: number
|
|
1312
|
+
squad_pct: number
|
|
1313
|
+
self_wasted?: number
|
|
1314
|
+
group_wasted?: number
|
|
1315
|
+
squad_wasted?: number
|
|
1316
|
+
}
|
|
1317
|
+
|
|
1318
|
+
/**
|
|
1319
|
+
* One `[[time_ms_from_log_start, stacks], ...]` step timeline.
|
|
1320
|
+
*
|
|
1321
|
+
* Always opens with a `[0, 0]` pair and never carries two pairs at one
|
|
1322
|
+
* timestamp. Duration buffs report 0/1; only intensity buffs (Might,
|
|
1323
|
+
* Stability, most damaging conditions) exceed 1.
|
|
1324
|
+
*/
|
|
1325
|
+
export type StateTimeline = [number, number][]
|
|
1326
|
+
|
|
1327
|
+
/**
|
|
1328
|
+
* A buff's stack timeline split by who applied it, keyed by SOURCE ENTITY
|
|
1329
|
+
* ID -- joining back into `entities[]` like every other key in this format.
|
|
1330
|
+
*
|
|
1331
|
+
* The upstream analysis keys these by the applier's character NAME; native
|
|
1332
|
+
* does not, both because a name is identity data this format confines to
|
|
1333
|
+
* `entities[]` and because two players sharing a character name collide
|
|
1334
|
+
* onto one key.
|
|
1335
|
+
*/
|
|
1336
|
+
export interface PerSourceStates {
|
|
1337
|
+
/** Keyed by the applying entity's id. */
|
|
1338
|
+
by_source: Record<string, StateTimeline>
|
|
1339
|
+
/**
|
|
1340
|
+
* Every applier that resolves to no `entities[]` row at all, merged into
|
|
1341
|
+
* one timeline -- so those applications are neither dropped nor given a
|
|
1342
|
+
* fabricated entity id. Normally absent.
|
|
1343
|
+
*/
|
|
1344
|
+
unresolved?: StateTimeline
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1347
|
+
/**
|
|
1348
|
+
* Mirrors the legacy `BoonOut` field-for-field, minus `id` (the map key) and
|
|
1349
|
+
* `name` (resolve via `catalogs.buffs`) -- plus the two timeline fields,
|
|
1350
|
+
* which need `{ timeseries: true }`.
|
|
1351
|
+
*/
|
|
1352
|
+
export interface BoonRow {
|
|
1353
|
+
uptime_pct: number
|
|
1354
|
+
avg_stacks?: number
|
|
1355
|
+
generation: GenerationRow
|
|
1356
|
+
/**
|
|
1357
|
+
* The fused stack timeline. `{ timeseries: true }` only.
|
|
1358
|
+
*
|
|
1359
|
+
* May be `[]`, meaning the pass ran and this entity never held the buff
|
|
1360
|
+
* -- a real timeline always has at least its leading `[0, 0]`, so `[]` is
|
|
1361
|
+
* unambiguous, and the field's presence is the signal the gate was on.
|
|
1362
|
+
*/
|
|
1363
|
+
states?: StateTimeline
|
|
1364
|
+
/** The same timeline split by applier. `{ timeseries: true }` only. */
|
|
1365
|
+
per_source?: PerSourceStates
|
|
1366
|
+
}
|
|
1367
|
+
|
|
1368
|
+
/**
|
|
1369
|
+
* entity id -> buff id -> row. Two levels of real ids, no positional joins.
|
|
1370
|
+
*
|
|
1371
|
+
* Two gates, like `ReplayBlock`: the uptime/generation numbers are computed
|
|
1372
|
+
* on every parse, `BoonRow.states`/`per_source` need `{ timeseries: true }`.
|
|
1373
|
+
* So `coverage.boons === "present"` is about the uptime half ONLY and says
|
|
1374
|
+
* nothing about whether timelines are here -- check the fields.
|
|
1375
|
+
*/
|
|
1376
|
+
export interface BoonsBlock {
|
|
1377
|
+
by_entity: ByEntity<Record<string, BoonRow>>
|
|
1378
|
+
}
|
|
1379
|
+
|
|
1380
|
+
/**
|
|
1381
|
+
* One enemy's condition timeline, split by the squad member who applied it.
|
|
1382
|
+
*
|
|
1383
|
+
* There is no sibling fused `states` here, unlike `BoonRow`: the enemy-side
|
|
1384
|
+
* pass computes only the source split, and summing sources would not
|
|
1385
|
+
* reconstruct a total (two appliers holding the same duration condition
|
|
1386
|
+
* overlap rather than stack).
|
|
1387
|
+
*/
|
|
1388
|
+
export interface ConditionRow {
|
|
1389
|
+
per_source: PerSourceStates
|
|
1390
|
+
}
|
|
1391
|
+
|
|
1392
|
+
/**
|
|
1393
|
+
* enemy entity id -> condition buff id -> row. Wholly gated on
|
|
1394
|
+
* `{ timeseries: true }`, so unlike `boons` its `coverage` entry does settle
|
|
1395
|
+
* the question.
|
|
1396
|
+
*
|
|
1397
|
+
* Appliers are narrowed to the SQUAD -- an enemy-applied condition on
|
|
1398
|
+
* another enemy is real but has no consumer -- so
|
|
1399
|
+
* `PerSourceStates.unresolved` is always absent here.
|
|
1400
|
+
*/
|
|
1401
|
+
export interface ConditionsBlock {
|
|
1402
|
+
by_entity: ByEntity<Record<string, ConditionRow>>
|
|
1403
|
+
}
|
|
1404
|
+
|
|
1405
|
+
/**
|
|
1406
|
+
* Per-player minion damage-TAKEN rollups, gated on `{ skillDamage: true }`.
|
|
1407
|
+
* One entry per player that HAS minions -- a player with none is absent
|
|
1408
|
+
* rather than carrying an empty array.
|
|
1409
|
+
*/
|
|
1410
|
+
export interface MinionsBlock {
|
|
1411
|
+
by_entity: ByEntity<MinionRow[]>
|
|
1412
|
+
}
|
|
1413
|
+
|
|
1414
|
+
/**
|
|
1415
|
+
* One minion group belonging to one player. `minion_id` resolves through
|
|
1416
|
+
* `Catalogs.minions`, which carries the species id and name -- identity
|
|
1417
|
+
* lives there because no block in this format inlines a readable name.
|
|
1418
|
+
*/
|
|
1419
|
+
export interface MinionRow {
|
|
1420
|
+
minion_id: number
|
|
1421
|
+
/** The damage this species took, keyed by skill id. */
|
|
1422
|
+
taken: Record<string, MinionSkillTakenRow>
|
|
1423
|
+
}
|
|
1424
|
+
|
|
1425
|
+
/**
|
|
1426
|
+
* One minion group's damage-taken row for one skill.
|
|
1427
|
+
*
|
|
1428
|
+
* Deliberately NOT a `SkillRow`: that row carries `crit_hits`/`flank_hits`,
|
|
1429
|
+
* which a damage-taken rollup does not have, while this one carries the
|
|
1430
|
+
* outcome counters inline rather than nested in `outcomes`.
|
|
1431
|
+
*/
|
|
1432
|
+
export interface MinionSkillTakenRow {
|
|
1433
|
+
total: number
|
|
1434
|
+
/** Attempts, marker rows excluded. */
|
|
1435
|
+
hits: number
|
|
1436
|
+
/** `HasHit` rows only. */
|
|
1437
|
+
connected_hits: number
|
|
1438
|
+
min: number
|
|
1439
|
+
max: number
|
|
1440
|
+
blocked: number
|
|
1441
|
+
evaded: number
|
|
1442
|
+
glance: number
|
|
1443
|
+
missed: number
|
|
1444
|
+
invulned: number
|
|
1445
|
+
interrupted: number
|
|
1446
|
+
/** Condition damage rather than strike damage. */
|
|
1447
|
+
indirect: boolean
|
|
1448
|
+
}
|
|
1449
|
+
|
|
1450
|
+
/** Mirrors the legacy `SupportOut` field-for-field. */
|
|
1451
|
+
export interface SupportEntity {
|
|
1452
|
+
cleanses: number
|
|
1453
|
+
cleanses_self: number
|
|
1454
|
+
strips: number
|
|
1455
|
+
strips_duration_ms: number
|
|
1456
|
+
resurrects: number
|
|
1457
|
+
}
|
|
1458
|
+
|
|
1459
|
+
export interface SupportBlock {
|
|
1460
|
+
by_entity: ByEntity<SupportEntity>
|
|
1461
|
+
}
|
|
1462
|
+
|
|
1463
|
+
/** Mirrors the legacy `ContributionOut` field-for-field. */
|
|
1464
|
+
export interface ContributionRow {
|
|
1465
|
+
damage: number
|
|
1466
|
+
cc: number
|
|
1467
|
+
strips: number
|
|
1468
|
+
movement_impairing: number
|
|
1469
|
+
}
|
|
1470
|
+
|
|
1471
|
+
/** Both directions of the arcdps-methodology down contribution. */
|
|
1472
|
+
export interface ContributionEntity {
|
|
1473
|
+
downs_contribution: ContributionRow
|
|
1474
|
+
downed_by: ContributionRow
|
|
1475
|
+
/**
|
|
1476
|
+
* `downs_contribution.damage` sliced by the skill that dealt it, keyed by
|
|
1477
|
+
* skill id as a decimal string. Sparse -- only skills with a nonzero
|
|
1478
|
+
* credit appear -- and omitted entirely when there are none. Ungated:
|
|
1479
|
+
* the contribution pass is always-on, unlike the per-skill DAMAGE rows on
|
|
1480
|
+
* `blocks.damage`, which is why it lives here rather than beside them.
|
|
1481
|
+
*/
|
|
1482
|
+
downs_contribution_by_skill?: Record<string, number>
|
|
1483
|
+
}
|
|
1484
|
+
|
|
1485
|
+
export interface ContributionBlock {
|
|
1486
|
+
by_entity: ByEntity<ContributionEntity>
|
|
1487
|
+
}
|
|
1488
|
+
|
|
1489
|
+
/** Mirrors the legacy `HealingOut` field-for-field. */
|
|
1490
|
+
export interface HealingEntity {
|
|
1491
|
+
outgoing_total: number
|
|
1492
|
+
outgoing_allies: number
|
|
1493
|
+
outgoing_self: number
|
|
1494
|
+
barrier_out: number
|
|
1495
|
+
downed_healing_out: number
|
|
1496
|
+
/**
|
|
1497
|
+
* The per-ally / per-skill breakdowns. Omitted when that pass did not
|
|
1498
|
+
* run -- the "not measured" signal, as distinct from a measured zero.
|
|
1499
|
+
*
|
|
1500
|
+
* Note the cumulative healing SERIES is not here; it lives at
|
|
1501
|
+
* `EntitySeries.healing_1s`, because what a field belongs to in this
|
|
1502
|
+
* format is its grid and its gate, not its subject matter.
|
|
1503
|
+
*/
|
|
1504
|
+
detail?: HealingDetailCols
|
|
1505
|
+
}
|
|
1506
|
+
|
|
1507
|
+
/**
|
|
1508
|
+
* The three per-ally / per-skill breakdowns of one entity's outgoing
|
|
1509
|
+
* healing and barrier.
|
|
1510
|
+
*/
|
|
1511
|
+
export interface HealingDetailCols {
|
|
1512
|
+
/**
|
|
1513
|
+
* Keyed by the ALLY's entity id (EI's positional
|
|
1514
|
+
* `outgoingHealingAllies`/`outgoingBarrierAllies` over `log.Friendlies`).
|
|
1515
|
+
* Within a PRESENT map, an absent ally is a MEASURED zero; the optional
|
|
1516
|
+
* `HealingEntity.detail` one level up carries "not measured". The healer
|
|
1517
|
+
* appears at its own id -- self-healing is one of these cells, exactly as
|
|
1518
|
+
* in GW2EI, not a separate scalar.
|
|
1519
|
+
*/
|
|
1520
|
+
by_ally: Record<string, AllyHealingRow>
|
|
1521
|
+
/** EI's `totalHealingDist`, keyed by skill id. */
|
|
1522
|
+
by_skill: Record<string, HealSkillRow>
|
|
1523
|
+
/**
|
|
1524
|
+
* EI's `totalBarrierDist`, keyed by skill id. A separate map rather than
|
|
1525
|
+
* a column on `by_skill`: a skill can appear in one and not the other,
|
|
1526
|
+
* and merging them would force every healing row to publish a barrier it
|
|
1527
|
+
* never measured.
|
|
1528
|
+
*/
|
|
1529
|
+
barrier_by_skill: Record<string, HealSkillRow>
|
|
1530
|
+
}
|
|
1531
|
+
|
|
1532
|
+
/** One cell of `HealingDetailCols.by_ally`. */
|
|
1533
|
+
export interface AllyHealingRow {
|
|
1534
|
+
healing: number
|
|
1535
|
+
/** The subset of `healing` that landed while the ally was downed. */
|
|
1536
|
+
downed_healing: number
|
|
1537
|
+
barrier: number
|
|
1538
|
+
}
|
|
1539
|
+
|
|
1540
|
+
/**
|
|
1541
|
+
* One skill's row of EI's `totalHealingDist` / `totalBarrierDist`.
|
|
1542
|
+
*
|
|
1543
|
+
* `hits`/`min`/`max` count EVERY event in the group -- GW2EI's healing dist
|
|
1544
|
+
* has no `HasHit` gate, unlike its damage dist, which is why this row
|
|
1545
|
+
* carries one hit count where `SkillRow` carries three.
|
|
1546
|
+
*/
|
|
1547
|
+
export interface HealSkillRow {
|
|
1548
|
+
total: number
|
|
1549
|
+
/**
|
|
1550
|
+
* EI's `totalDownedHealing`. Omitted when zero, which is ALWAYS on a
|
|
1551
|
+
* barrier row: `EXTJsonBarrierDist` has no downed field at all, so a zero
|
|
1552
|
+
* there would invent a measurement GW2EI never makes.
|
|
1553
|
+
*/
|
|
1554
|
+
total_downed?: number
|
|
1555
|
+
hits: number
|
|
1556
|
+
min: number
|
|
1557
|
+
max: number
|
|
1558
|
+
/** The group contains at least one healing-over-time tick. */
|
|
1559
|
+
indirect: boolean
|
|
1560
|
+
}
|
|
1561
|
+
|
|
1562
|
+
export interface HealingBlock {
|
|
1563
|
+
by_entity: ByEntity<HealingEntity>
|
|
1564
|
+
}
|
|
1565
|
+
|
|
1566
|
+
/* --- rotation / damage_mods / missiles / replay / series blocks ---------- */
|
|
1567
|
+
|
|
1568
|
+
/**
|
|
1569
|
+
* One cast. Mirrors the legacy `CastOut` field-for-field, plus `skill_id`
|
|
1570
|
+
* hoisted from the enclosing skill grouping so this is a flat,
|
|
1571
|
+
* time-ordered cast list per entity.
|
|
1572
|
+
*/
|
|
1573
|
+
export interface CastRow {
|
|
1574
|
+
skill_id: number
|
|
1575
|
+
cast_time_ms: number
|
|
1576
|
+
duration_ms: number
|
|
1577
|
+
/** Negative when the cast was interrupted/cancelled early. */
|
|
1578
|
+
time_gained_ms: number
|
|
1579
|
+
quickness: number
|
|
1580
|
+
}
|
|
1581
|
+
|
|
1582
|
+
/**
|
|
1583
|
+
* Mirrors the legacy `AftercastOut`. Durations are MILLISECONDS (GW2EI
|
|
1584
|
+
* emits the same quantities as seconds).
|
|
1585
|
+
*
|
|
1586
|
+
* NOTE the name collision GW2EI bequeathed: `wasted_count` is a
|
|
1587
|
+
* CAST-INTERRUPT count, unrelated to the boon-generation `*_wasted` fields
|
|
1588
|
+
* under `boons`.
|
|
1589
|
+
*/
|
|
1590
|
+
export interface Aftercast {
|
|
1591
|
+
/** Casts that skipped their aftercast. */
|
|
1592
|
+
saved_count: number
|
|
1593
|
+
saved_ms: number
|
|
1594
|
+
/** Casts interrupted before firing. */
|
|
1595
|
+
wasted_count: number
|
|
1596
|
+
/** Already the positive "time lost" figure. */
|
|
1597
|
+
wasted_ms: number
|
|
1598
|
+
}
|
|
1599
|
+
|
|
1600
|
+
export interface RotationEntity {
|
|
1601
|
+
/**
|
|
1602
|
+
* This entity's casts, in cast-start order. Present exactly when the cast
|
|
1603
|
+
* gate (`rotation: true`) was on -- an EMPTY array means the pass ran and
|
|
1604
|
+
* this entity cast nothing, while an absent field means it never ran.
|
|
1605
|
+
* `aftercast` below is always-on, so the block's `coverage` cannot answer
|
|
1606
|
+
* that question and this field's presence is the gate record.
|
|
1607
|
+
*/
|
|
1608
|
+
casts?: CastRow[]
|
|
1609
|
+
/** Cast counters, computed unconditionally. */
|
|
1610
|
+
aftercast: Aftercast
|
|
1611
|
+
}
|
|
1612
|
+
|
|
1613
|
+
export interface RotationBlock {
|
|
1614
|
+
by_entity: ByEntity<RotationEntity>
|
|
1615
|
+
}
|
|
1616
|
+
|
|
1617
|
+
/**
|
|
1618
|
+
* One damage-modifier row. `id` (the map key) is SIGNED -- negative means
|
|
1619
|
+
* incoming -- so one map naturally separates outgoing from incoming.
|
|
1620
|
+
*/
|
|
1621
|
+
export interface DamageModRow {
|
|
1622
|
+
hit_count: number
|
|
1623
|
+
total_hit_count: number
|
|
1624
|
+
damage_gain: number
|
|
1625
|
+
total_damage: number
|
|
1626
|
+
}
|
|
1627
|
+
|
|
1628
|
+
/**
|
|
1629
|
+
* One entity's damage modifiers, in the two scopes GW2EI evaluates them at.
|
|
1630
|
+
* Neither is derivable from the other: `overall` counts every qualifying
|
|
1631
|
+
* hit, including hits on agents that are not targets at all (enemy minions),
|
|
1632
|
+
* while `per_target` restricts to one foe.
|
|
1633
|
+
*/
|
|
1634
|
+
export interface DamageModEntity {
|
|
1635
|
+
/** Whole fight. Keyed by the signed damage-modifier id as a decimal string. */
|
|
1636
|
+
overall: Record<string, DamageModRow>
|
|
1637
|
+
/**
|
|
1638
|
+
* Restricted to one foe, keyed by the TARGET's entity id then by the
|
|
1639
|
+
* signed modifier id. Sparse in both dimensions. The per-target split is
|
|
1640
|
+
* the expensive half and the native path does not compute it, so an
|
|
1641
|
+
* absent `per_target` on a present block means "the split was not
|
|
1642
|
+
* computed", not "there was none".
|
|
1643
|
+
*/
|
|
1644
|
+
per_target?: Record<string, Record<string, DamageModRow>>
|
|
1645
|
+
}
|
|
1646
|
+
|
|
1647
|
+
export interface DamageModsBlock {
|
|
1648
|
+
by_entity: ByEntity<DamageModEntity>
|
|
1649
|
+
}
|
|
1650
|
+
|
|
1651
|
+
export interface MissilesSquad {
|
|
1652
|
+
fired: number
|
|
1653
|
+
hit: number
|
|
1654
|
+
denied: number
|
|
1655
|
+
incoming_fired: number
|
|
1656
|
+
incoming_denied: number
|
|
1657
|
+
}
|
|
1658
|
+
|
|
1659
|
+
/** Mirrors the legacy `PlayerMissilesOut`, minus `agent_addr`/`account` (identity already lives on this row's own `entities[]` entry). */
|
|
1660
|
+
export interface MissilesEntity {
|
|
1661
|
+
fired: number
|
|
1662
|
+
hit: number
|
|
1663
|
+
denied: number
|
|
1664
|
+
reflected_at_self: number
|
|
1665
|
+
}
|
|
1666
|
+
|
|
1667
|
+
/**
|
|
1668
|
+
* `squad` is REQUIRED, like every other squad aggregate in this format
|
|
1669
|
+
* (`damage`, `cc`, `series`): when the block is present, so is its
|
|
1670
|
+
* aggregate.
|
|
1671
|
+
*/
|
|
1672
|
+
export interface MissilesBlock {
|
|
1673
|
+
squad: MissilesSquad
|
|
1674
|
+
by_entity: ByEntity<MissilesEntity>
|
|
1675
|
+
}
|
|
1676
|
+
|
|
1677
|
+
export interface ReplayBounds {
|
|
1678
|
+
min_x: number
|
|
1679
|
+
min_y: number
|
|
1680
|
+
max_x: number
|
|
1681
|
+
max_y: number
|
|
1682
|
+
}
|
|
1683
|
+
|
|
1684
|
+
/**
|
|
1685
|
+
* One entity's replay track. `samples` are `[t_ms, x, y]` triples;
|
|
1686
|
+
* `down_intervals`/`dead_intervals` are `[start_ms, end_ms]` pairs.
|
|
1687
|
+
* `name`/`team`/`commander`/`is_squad` are dropped versus the legacy
|
|
1688
|
+
* `ReplayTrackOut` -- they live on this entity's own `entities[]` row.
|
|
1689
|
+
*
|
|
1690
|
+
* The intervals are ALSO on `ReplayIntervals`, and that is deliberate: the
|
|
1691
|
+
* track roster covers enemy players too, whom the always-on intervals pass
|
|
1692
|
+
* never walks, so dropping them here would lose that history entirely. For
|
|
1693
|
+
* an entity present in both, the two copies are the same values.
|
|
1694
|
+
*/
|
|
1695
|
+
export interface ReplayTrack {
|
|
1696
|
+
samples: [number, number, number][]
|
|
1697
|
+
down_intervals: [number, number][]
|
|
1698
|
+
dead_intervals: [number, number][]
|
|
1699
|
+
/**
|
|
1700
|
+
* Disconnect/not-yet-spawned windows, half-open like the two above. See
|
|
1701
|
+
* `ReplayIntervals.dc` for the GW2EI divergence and the log-end rule.
|
|
1702
|
+
*/
|
|
1703
|
+
dc_intervals: [number, number][]
|
|
1704
|
+
}
|
|
1705
|
+
|
|
1706
|
+
/**
|
|
1707
|
+
* One SQUAD entity's activity window. `down`/`dead` are `[start_ms, end_ms]`
|
|
1708
|
+
* pairs.
|
|
1709
|
+
*
|
|
1710
|
+
* `active_ms` is carried rather than derivable: matching GW2EI, it subtracts
|
|
1711
|
+
* dead time and NOT down time. Recomputing it as "neither downed nor dead"
|
|
1712
|
+
* under-reports every player who went down.
|
|
1713
|
+
*/
|
|
1714
|
+
export interface ReplayIntervals {
|
|
1715
|
+
start_ms: number
|
|
1716
|
+
end_ms: number
|
|
1717
|
+
active_ms: number
|
|
1718
|
+
down: [number, number][]
|
|
1719
|
+
dead: [number, number][]
|
|
1720
|
+
/**
|
|
1721
|
+
* Disconnect/not-yet-spawned windows (`CBTS_DESPAWN` to the matching
|
|
1722
|
+
* `CBTS_SPAWN`), half-open `[start_ms, end_ms)` like `down`/`dead`, and
|
|
1723
|
+
* NOT mutually exclusive with them -- an agent can despawn while dead.
|
|
1724
|
+
*
|
|
1725
|
+
* An agent still disconnected at log end gets **no interval at all** for
|
|
1726
|
+
* that trailing window; it is dropped, not closed at the log's end. So
|
|
1727
|
+
* `end_ms - start_ms` minus summed `dc` over-counts activity for anyone
|
|
1728
|
+
* who disconnects and never returns -- use `active_ms`.
|
|
1729
|
+
*/
|
|
1730
|
+
dc: [number, number][]
|
|
1731
|
+
/**
|
|
1732
|
+
* GW2EI's `distToCom` -- mean distance to the commander over this actor's
|
|
1733
|
+
* active polls, in world inches.
|
|
1734
|
+
*
|
|
1735
|
+
* Three states, and the first two must not be collapsed: **absent** means
|
|
1736
|
+
* the position pass never ran (`{ replay: true }` was not passed, nothing
|
|
1737
|
+
* was measured); **`-1`** means the pass ran and nothing qualified (GW2EI's
|
|
1738
|
+
* own sentinel); **`>= 0`** is a real distance (`0` is legitimate -- the
|
|
1739
|
+
* commander's own value).
|
|
1740
|
+
*
|
|
1741
|
+
* These two scalars are the only part of `ReplayBlock.by_entity` that
|
|
1742
|
+
* depends on the `{ replay: true }` gate; every interval field above is
|
|
1743
|
+
* computed on every parse and is identical with and without it.
|
|
1744
|
+
*/
|
|
1745
|
+
dist_to_com?: number
|
|
1746
|
+
/** GW2EI's `stackDist`, against the squad centre. Same three states and same gate as `dist_to_com`. */
|
|
1747
|
+
stack_dist?: number
|
|
1748
|
+
}
|
|
1749
|
+
|
|
1750
|
+
/** The gated half of `ReplayBlock` -- present only under `{ replay: true }`. */
|
|
1751
|
+
export interface ReplayTracks {
|
|
1752
|
+
/** Shared polling interval for every track. */
|
|
1753
|
+
poll_ms: number
|
|
1754
|
+
bounds?: ReplayBounds
|
|
1755
|
+
/** Keyed by entity id. Wider than `ReplayBlock.by_entity`: enemy players appear here too. */
|
|
1756
|
+
by_entity: ByEntity<ReplayTrack>
|
|
1757
|
+
}
|
|
1758
|
+
|
|
1759
|
+
/**
|
|
1760
|
+
* Two halves on two different gates -- the only block in the format shaped
|
|
1761
|
+
* this way. `by_entity` (down/dead intervals, squad only) is computed on
|
|
1762
|
+
* every parse; `tracks` (positions) needs `{ replay: true }`. So
|
|
1763
|
+
* `coverage.replay === "present"` does NOT mean positions are available --
|
|
1764
|
+
* check `tracks` for that.
|
|
1765
|
+
*/
|
|
1766
|
+
export interface ReplayBlock {
|
|
1767
|
+
/** Keyed by entity id. Squad players only. */
|
|
1768
|
+
by_entity: ByEntity<ReplayIntervals>
|
|
1769
|
+
tracks?: ReplayTracks
|
|
1770
|
+
/**
|
|
1771
|
+
* Glider deploy/stow windows. Omitted (not `[]`) when the log has none.
|
|
1772
|
+
*
|
|
1773
|
+
* A flat list rather than an entity-keyed map: `CBTS_GLIDER` is not
|
|
1774
|
+
* restricted to the squad, so a window can belong to an agent that never
|
|
1775
|
+
* becomes a tracked entity. `agent_addr` is always present, `entity_id`
|
|
1776
|
+
* only when the join resolves.
|
|
1777
|
+
*/
|
|
1778
|
+
gliding?: GliderOut[]
|
|
1779
|
+
/** Transformation (mount/tonic/form) windows. Same shape rules as `gliding`. */
|
|
1780
|
+
transformations?: TransformationOut[]
|
|
1781
|
+
/**
|
|
1782
|
+
* Capture-point areas as decoded. Empty on every log written before
|
|
1783
|
+
* arcdps build 20260602, which does not emit the family at all.
|
|
1784
|
+
*/
|
|
1785
|
+
captures?: CaptureOut[]
|
|
1786
|
+
/** The renderable projection of `captures`. Neither form reconstructs the other. */
|
|
1787
|
+
decorations?: DecorationOut[]
|
|
1788
|
+
}
|
|
1789
|
+
|
|
1790
|
+
/**
|
|
1791
|
+
* One glider deployment. A MISSING `end_ms` means the glider was still
|
|
1792
|
+
* deployed at the last event in the log -- not that it closed at log end.
|
|
1793
|
+
*/
|
|
1794
|
+
export interface GliderOut {
|
|
1795
|
+
entity_id?: number
|
|
1796
|
+
agent_addr: number
|
|
1797
|
+
start_ms: number
|
|
1798
|
+
end_ms?: number
|
|
1799
|
+
}
|
|
1800
|
+
|
|
1801
|
+
/** One transformation window. */
|
|
1802
|
+
export interface TransformationOut {
|
|
1803
|
+
entity_id?: number
|
|
1804
|
+
agent_addr: number
|
|
1805
|
+
/**
|
|
1806
|
+
* The arcdps SESSION-LOCAL id -- meaningless across logs on its own.
|
|
1807
|
+
* `guid` is the portable identity, and is omitted when the log carried no
|
|
1808
|
+
* `CBTS_IDTOGUID` mapping for this id.
|
|
1809
|
+
*/
|
|
1810
|
+
transformation_id: number
|
|
1811
|
+
guid?: string
|
|
1812
|
+
start_ms: number
|
|
1813
|
+
end_ms?: number
|
|
1814
|
+
}
|
|
1815
|
+
|
|
1816
|
+
/**
|
|
1817
|
+
* The arcdps "wrbg" capture owner. `white` is unowned/neutral. An owner
|
|
1818
|
+
* index arcdps adds later serializes as `unknown_<n>` rather than folding
|
|
1819
|
+
* into `white`, which is why this is not a closed union.
|
|
1820
|
+
*/
|
|
1821
|
+
export type CaptureOwner = 'white' | 'red' | 'blue' | 'green' | string
|
|
1822
|
+
|
|
1823
|
+
/** One capture-point area over its lifetime. */
|
|
1824
|
+
export interface CaptureOut {
|
|
1825
|
+
/** Almost never resolves -- a capture point is a gadget, not a tracked entity. */
|
|
1826
|
+
entity_id?: number
|
|
1827
|
+
agent_addr: number
|
|
1828
|
+
start_ms: number
|
|
1829
|
+
/**
|
|
1830
|
+
* Omitted when the area never got a hide row and no later show superseded
|
|
1831
|
+
* it. Deliberately NOT defaulted to the gadget's last-aware time here --
|
|
1832
|
+
* that substitution is a rendering decision, made in `DecorationOut`.
|
|
1833
|
+
*/
|
|
1834
|
+
end_ms?: number
|
|
1835
|
+
original_owner: CaptureOwner
|
|
1836
|
+
/** Omitted when no geometry row ever arrived. Such an area produces no decoration. */
|
|
1837
|
+
shape?: CaptureShapeOut
|
|
1838
|
+
owner_states: OwnerStateOut[]
|
|
1839
|
+
progress_states: ProgressStateOut[]
|
|
1840
|
+
}
|
|
1841
|
+
|
|
1842
|
+
/**
|
|
1843
|
+
* The capture area's geometry, in WORLD coordinates. The circle case is the
|
|
1844
|
+
* arcdps single-point overload (radius around the gadget), not a degenerate
|
|
1845
|
+
* polygon.
|
|
1846
|
+
*/
|
|
1847
|
+
export type CaptureShapeOut =
|
|
1848
|
+
| { kind: 'circle'; radius: number }
|
|
1849
|
+
| { kind: 'polygon'; points: [number, number][] }
|
|
1850
|
+
|
|
1851
|
+
/** At `time_ms` the area was held by `from` and being taken by `by`. */
|
|
1852
|
+
export interface OwnerStateOut {
|
|
1853
|
+
time_ms: number
|
|
1854
|
+
from: CaptureOwner
|
|
1855
|
+
by: CaptureOwner
|
|
1856
|
+
}
|
|
1857
|
+
|
|
1858
|
+
/** A run of progress samples sharing one owner pair. */
|
|
1859
|
+
export interface ProgressStateOut {
|
|
1860
|
+
from: CaptureOwner
|
|
1861
|
+
by: CaptureOwner
|
|
1862
|
+
/** `by` is nobody, so the bar is falling back toward `from` rather than being captured. */
|
|
1863
|
+
decaying: boolean
|
|
1864
|
+
/** `[time_ms, percent]`, percent in 0..100 at 2 decimal places. */
|
|
1865
|
+
progress: [number, number][]
|
|
1866
|
+
}
|
|
1867
|
+
|
|
1868
|
+
/** One drawable environment decoration. */
|
|
1869
|
+
export interface DecorationOut {
|
|
1870
|
+
kind: 'capture_outline' | 'capture_progress'
|
|
1871
|
+
/**
|
|
1872
|
+
* Log-relative ms, SIGNED. Unlike every other time in this format this can
|
|
1873
|
+
* be negative by exactly one millisecond: the capture-progress splitter
|
|
1874
|
+
* synthesizes a sample at `time - 1`.
|
|
1875
|
+
*/
|
|
1876
|
+
start_ms: number
|
|
1877
|
+
end_ms: number
|
|
1878
|
+
/** World-space `[x, y]` the shape is drawn around. */
|
|
1879
|
+
anchor: [number, number]
|
|
1880
|
+
/**
|
|
1881
|
+
* CSS `rgba(...)`. For a progress bar the two colour slots do NOT have
|
|
1882
|
+
* fixed owner roles: capturing puts the capper here, decaying the holder.
|
|
1883
|
+
*/
|
|
1884
|
+
color: string
|
|
1885
|
+
secondary_color?: string
|
|
1886
|
+
shape: DecorationShapeOut
|
|
1887
|
+
}
|
|
1888
|
+
|
|
1889
|
+
/** A decoration's geometry, RELATIVE to `DecorationOut.anchor`. */
|
|
1890
|
+
export type DecorationShapeOut =
|
|
1891
|
+
| { kind: 'circle'; radius: number; filled: boolean }
|
|
1892
|
+
| { kind: 'polygon'; points: [number, number][]; filled: boolean }
|
|
1893
|
+
| { kind: 'progress_bar'; width: number; height: number; progress: [number, number][] }
|
|
1894
|
+
|
|
1895
|
+
export interface SquadSeries {
|
|
1896
|
+
damage: SeriesOut
|
|
1897
|
+
cc_applied: SeriesOut
|
|
1898
|
+
downs: SeriesOut
|
|
1899
|
+
}
|
|
1900
|
+
|
|
1901
|
+
/** Mirrors the legacy `PlayerTargetSeriesOut`, minus `enemy_id` (that's the map key here, joined by entity id). */
|
|
1902
|
+
export interface TargetSeries {
|
|
1903
|
+
damage: SeriesOut
|
|
1904
|
+
power_damage: SeriesOut
|
|
1905
|
+
}
|
|
1906
|
+
|
|
1907
|
+
/** Mirrors the legacy `PlayerPerSecondOut` field-for-field. `per_target` is keyed by the TARGET's entity id. */
|
|
1908
|
+
export interface EntitySeries {
|
|
1909
|
+
damage: SeriesOut
|
|
1910
|
+
/**
|
|
1911
|
+
* The non-condition half of OUTGOING `damage`. In practice present only
|
|
1912
|
+
* on ENEMY rows: no pass computes an outgoing power split for players.
|
|
1913
|
+
* Absent means "no pass measured this", which a zero-filled series would
|
|
1914
|
+
* misreport as "measured, and it was all condition damage".
|
|
1915
|
+
*/
|
|
1916
|
+
power_damage?: SeriesOut
|
|
1917
|
+
damage_taken: SeriesOut
|
|
1918
|
+
power_damage_taken: SeriesOut
|
|
1919
|
+
per_target?: Record<string, TargetSeries>
|
|
1920
|
+
/**
|
|
1921
|
+
* `[[time_ms, percent], ...]` -- a STEP function, which is why this is a
|
|
1922
|
+
* plain pair list rather than a `SeriesOut`: re-sampling it onto a fixed
|
|
1923
|
+
* grid would either invent readings between updates or lose updates
|
|
1924
|
+
* inside a bucket. A value holds until the next pair. Absent when the
|
|
1925
|
+
* entity emitted no health updates at all (EI omits `healthPercents` for
|
|
1926
|
+
* such a player rather than writing `[]`).
|
|
1927
|
+
*/
|
|
1928
|
+
health_percents?: Array<[number, number]>
|
|
1929
|
+
/**
|
|
1930
|
+
* Cumulative outgoing healing from the arcdps healing extension -- EI's
|
|
1931
|
+
* `extHealingStats.healing1S`. Absent for enemies, and for everyone on a
|
|
1932
|
+
* log with no healing extension.
|
|
1933
|
+
*/
|
|
1934
|
+
healing_1s?: SeriesOut
|
|
1935
|
+
}
|
|
1936
|
+
|
|
1937
|
+
/**
|
|
1938
|
+
* `squad` is REQUIRED (see `MissilesBlock`). The squad series is computed
|
|
1939
|
+
* unconditionally; only `by_entity` needs `timeseries: true`.
|
|
1940
|
+
*/
|
|
1941
|
+
export interface SeriesBlock {
|
|
1942
|
+
squad: SquadSeries
|
|
1943
|
+
by_entity: ByEntity<EntitySeries>
|
|
1944
|
+
}
|
|
1945
|
+
|
|
1946
|
+
/**
|
|
1947
|
+
* Every statistic block. A block is omitted entirely when `coverage` says
|
|
1948
|
+
* `not_computed`/`unsupported`; an `empty` block is still carried, so a
|
|
1949
|
+
* consumer can tell "computed and there was nothing" from "never ran".
|
|
1950
|
+
*/
|
|
1951
|
+
export interface Blocks {
|
|
1952
|
+
damage?: DamageBlock
|
|
1953
|
+
defenses?: DefensesBlock
|
|
1954
|
+
hit_stats?: HitStatsBlock
|
|
1955
|
+
cc?: CcBlock
|
|
1956
|
+
boons?: BoonsBlock
|
|
1957
|
+
support?: SupportBlock
|
|
1958
|
+
contribution?: ContributionBlock
|
|
1959
|
+
healing?: HealingBlock
|
|
1960
|
+
rotation?: RotationBlock
|
|
1961
|
+
damage_mods?: DamageModsBlock
|
|
1962
|
+
missiles?: MissilesBlock
|
|
1963
|
+
replay?: ReplayBlock
|
|
1964
|
+
series?: SeriesBlock
|
|
1965
|
+
conditions?: ConditionsBlock
|
|
1966
|
+
minions?: MinionsBlock
|
|
1967
|
+
}
|
|
1968
|
+
|
|
1969
|
+
/**
|
|
1970
|
+
* axilog's native output format 1.0 container (`axilog_schema::v1::
|
|
1971
|
+
* ReportV1`), as returned by `parseFile`/`parseBuffer` (Task 12).
|
|
1972
|
+
*/
|
|
1973
|
+
export interface ReportV1 {
|
|
1974
|
+
axilog: AxilogMeta
|
|
1975
|
+
encounter: EncounterOutV1
|
|
1976
|
+
entities: EntityOut[]
|
|
1977
|
+
catalogs: Catalogs
|
|
1978
|
+
blocks: Blocks
|
|
1979
|
+
coverage: Coverage
|
|
1980
|
+
/** Structured, user-facing analysis warnings. Omitted entirely (not `[]`) when there are none. */
|
|
1981
|
+
warnings?: WarningOut[]
|
|
1982
|
+
}
|