forked from quisquous/cactbot
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathmissed_buff_collector.ts
133 lines (114 loc) · 4.55 KB
/
missed_buff_collector.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
import logDefinitions from '../../resources/netlog_defs';
import { MissableAbility, MissableEffect } from './buff_map';
// Abilities seem roughly instant.
// Observation: up to ~1.2 seconds for an effect to roll through the party.
const defaultCollectSeconds = 0.5;
export type CollectedBuff = {
timestamp: number;
expireTimestamp: number;
sourceId: string;
buffName: string;
targetIds: string[];
splitLine: string[];
buff: MissableAbility | MissableEffect;
expireCallback: (timestamp: number) => void;
};
export type RequestTimestampCallback = (
timestamp: number,
callback: (timestamp: number) => void,
) => void;
type CollectedBuffCallback = (timestamp: number, buff: CollectedBuff) => void;
// Handles tracking whether everybody received a buff or not.
// In response to missed buffs, calls `collectedBuffCallback` when timestamps have expired.
export class MissedBuffCollector {
private buffs: { [sourceId: string]: { [buffId: string]: CollectedBuff } } = {};
constructor(
private requestTimestampCallback: RequestTimestampCallback,
private collectedBuffCallback: CollectedBuffCallback,
) {
}
// TODO: call something like this on zone change, etc?
ExpireBuffsIfNeeded(timestamp: number): void {
for (const buffList of Object.values(this.buffs)) {
for (const buffId of Object.keys(buffList)) {
const collectedBuff = buffList[buffId];
if (!collectedBuff)
continue;
if (timestamp > collectedBuff.timestamp)
collectedBuff.expireCallback(timestamp);
}
}
}
// Caller has vetted that we care about the target, so we don't need to do that here.
// Most (all) buffs only hit the party, and so no need to vet that the source is in the party.
OnAbilityBuff(splitLine: string[], buff: MissableAbility): void {
const sourceId = splitLine[logDefinitions.Ability.fields.sourceId];
const targetId = splitLine[logDefinitions.Ability.fields.targetId];
const buffName = splitLine[logDefinitions.Ability.fields.ability];
const timestamp = splitLine[logDefinitions.Ability.fields.timestamp];
if (
sourceId === undefined || targetId === undefined || buffName === undefined ||
timestamp === undefined
)
return;
this.OnBuff(new Date(timestamp).getTime(), splitLine, buff, buffName, sourceId, targetId);
}
OnEffectBuff(splitLine: string[], buff: MissableEffect): void {
const sourceId = splitLine[logDefinitions.GainsEffect.fields.sourceId];
const targetId = splitLine[logDefinitions.GainsEffect.fields.targetId];
const buffName = splitLine[logDefinitions.GainsEffect.fields.effect];
const timestamp = splitLine[logDefinitions.GainsEffect.fields.timestamp];
if (
sourceId === undefined || targetId === undefined || buffName === undefined ||
timestamp === undefined
)
return;
this.OnBuff(new Date(timestamp).getTime(), splitLine, buff, buffName, sourceId, targetId);
}
OnBuff(
timestamp: number,
splitLine: string[],
buff: MissableAbility | MissableEffect,
buffName: string,
sourceId: string,
targetId: string,
): void {
const buffList = this.buffs[sourceId] ??= {};
// Expire this buff if needed.
const expiredBuff = buffList[buff.id];
if (expiredBuff && timestamp > expiredBuff.expireTimestamp) {
// Handle and remove this buff if it has expired.
expiredBuff.expireCallback(timestamp);
}
// If we're already tracking, and it hasn't expired, just append the targetId.
const collectedBuff = buffList[buff.id];
if (collectedBuff) {
collectedBuff.targetIds.push(targetId);
return;
}
// Otherwise, we're tracking a new buff.
const collectSeconds = buff.collectSeconds ?? defaultCollectSeconds;
const expireTimestamp = timestamp + collectSeconds * 1000;
const expireCallback = (timestamp: number) => {
// Re-get the buff from the map, so that repeated calls to expireCallback will not
// call the collectedBuffCallback multiple times.
const expiredBuff = this.buffs[sourceId]?.[buff.id];
if (!expiredBuff)
return;
this.collectedBuffCallback(timestamp, expiredBuff);
delete this.buffs[sourceId]?.[buff.id];
};
// If we get here, this buff is not being tracked yet.
buffList[buff.id] = {
timestamp: timestamp,
splitLine: splitLine,
expireTimestamp: expireTimestamp,
sourceId: sourceId,
buffName: buffName,
targetIds: [targetId],
buff: buff,
expireCallback: expireCallback,
};
this.requestTimestampCallback(expireTimestamp, expireCallback);
}
}