@@ -8,10 +8,31 @@ const UNKNOWN_CARDNAME = 'UNKNOWN ENTITY [cardType=INVALID]';
88 * Tests if a card name is empty or the "empty string"
99 * @param cardName
1010 */
11- const isEmpty = ( cardName : string ) => {
11+ const isEmpty = ( cardName ? : string ) => {
1212 return ! cardName || cardName === UNKNOWN_CARDNAME ;
1313} ;
1414
15+ /**
16+ * Returns an array of special tags that can be used to communicate additional properties of a card.
17+ * Currently the available properties deal with corruption.
18+ * @param entity
19+ */
20+ const identifySpecialTags = ( entity : CardEntity | undefined ) => {
21+ if ( ! entity || ! entity . tags ) {
22+ return ;
23+ }
24+
25+ // Tags are handled here. These are the "simplified" version.
26+ const tags = new Array < EntityTags > ( ) ;
27+ if ( entity . tags . CORRUPTEDCARD === '1' ) {
28+ tags . push ( 'corrupt' ) ;
29+ } else if ( entity . tags . CORRUPT === '1' ) {
30+ tags . push ( 'can-corrupt' ) ;
31+ }
32+
33+ return tags . length > 0 ? tags : undefined ;
34+ } ;
35+
1536export interface Secret {
1637 cardId : string ;
1738 cardClass : Class ;
@@ -34,52 +55,91 @@ export interface Card {
3455 /**
3556 * ID used by logs to distinguish same cards
3657 */
37- cardEntityId : number ;
58+ entityId : number ;
59+
3860 /**
3961 * Numeric ID for the card (same for same card)
4062 * Unknown card has this undefined
4163 */
4264 cardId ?: number ;
65+
4366 /**
4467 * Unknown card has this undefined
4568 */
4669 cardName ?: string ;
70+
4771 state : CardState ;
72+
4873 /**
4974 * If card is originally from the deck
5075 */
5176 readonly isSpawnedCard : boolean ;
77+
78+ /**
79+ * Additional tags for when cards have special types
80+ */
81+ tags ?: EntityTags [ ] ;
82+ }
83+
84+ type EntityTags = 'can-corrupt' | 'corrupt' ;
85+
86+ export interface EntityProps {
87+ entityId : number ;
88+ cardId ?: number ;
89+ cardName : string ;
90+ player : 'top' | 'bottom' ;
91+ damage ?: number ;
92+ healing ?: number ;
93+ dead ?: boolean ;
94+
95+ /**
96+ * Additional tags for when cards have special types
97+ */
98+ tags ?: EntityTags [ ] ;
99+ }
100+
101+ /**
102+ * Extracts a subset of entity data for presentational use.
103+ * @param entity CardEntity to pull data from
104+ */
105+ export const simplifyEntity = ( entity : CardEntity ) : EntityProps => {
106+ return {
107+ cardId : entity . cardId ,
108+ cardName : entity . cardName ,
109+ entityId : entity . entityId ,
110+ player : entity . player
111+ } ;
112+ } ;
113+
114+ export interface Discovery {
115+ enabled : boolean ;
116+ id : string | null ;
117+ source ?: EntityProps ;
118+ chosen ?: EntityProps ;
119+ options : EntityProps [ ] ;
52120}
53121
54122export interface Player {
55123 id : number ;
56124 name : string ;
57125 status : 'LOST' | 'WON' | 'TIED' | '' ;
58126 turn : boolean ;
127+ turnHistory : Array < {
128+ startTime : number ;
129+ duration ?: number ;
130+ } > ;
59131 quests : Quest [ ] ;
60132 timeout : number ;
61133 cardCount : number ;
62134 cards : Card [ ] ;
63135 position : 'top' | 'bottom' ;
64136 secrets : Secret [ ] ;
65- discovery : {
66- enabled : boolean ;
67- id : string | null ;
68- } ;
137+ discovery : Discovery ;
138+ discoverHistory : Discovery [ ] ;
69139 cardsReplacedInMulligan : number ;
70140 manaSpent : number ;
71141}
72142
73- export interface EntityProps {
74- cardId ?: number ;
75- cardName : string ;
76- entityId : number ;
77- player : 'top' | 'bottom' ;
78- damage ?: number ;
79- healing ?: number ;
80- dead ?: boolean ;
81- }
82-
83143type MatchLogType = 'attack' | 'play' | 'trigger' ;
84144
85145export class MatchLogEntry {
@@ -117,25 +177,54 @@ export class MatchLogEntry {
117177 this . targets . push ( this . createProps ( entity , ...props ) ) ;
118178 }
119179
180+ /**
181+ * Marks targets/sources using the death entries.
182+ * Returns the entity ids of the cards that were successfully marked.
183+ * @param deaths Entity IDs that need to be marked
184+ * @returns the subset of deaths that were present in this log entry
185+ */
186+ markDeaths ( deaths : Set < number > ) {
187+ const marked = new Set < number > ( ) ;
188+
189+ if ( deaths . has ( this . source . entityId ) ) {
190+ this . source . dead = true ;
191+ marked . add ( this . source . entityId ) ;
192+ }
193+
194+ for ( const target of this . targets ) {
195+ if ( deaths . has ( target . entityId ) ) {
196+ target . dead = true ;
197+ marked . add ( target . entityId ) ;
198+ }
199+ }
200+
201+ return marked ;
202+ }
203+
120204 private createProps ( entity : CardEntity , ...props : Array < Partial < EntityProps > | undefined > ) {
121- return merge ( {
122- cardId : entity . cardId ,
123- cardName : entity . cardName ,
124- entityId : entity . entityId ,
125- player : entity . player
126- } , ...props ) ;
205+ const tags = identifySpecialTags ( entity ) ;
206+ const merged = merge ( simplifyEntity ( entity ) , ...props ) ;
207+ if ( tags ) {
208+ merged . tags = tags ;
209+ }
210+
211+ return merged ;
127212 }
128213}
129214
130215export class GameState {
131216 startTime : number ;
132217
218+ matchDuration : number ;
219+
133220 playerCount : number ;
134221
135222 gameOverCount : number ;
136223
137224 players : Player [ ] ;
138225
226+ beginPhaseActive : boolean ;
227+
139228 mulliganActive : boolean ;
140229
141230 turnStartTime : Date ;
@@ -177,6 +266,7 @@ export class GameState {
177266 reset ( ) : void {
178267 this . players = [ ] ;
179268 this . matchLog = [ ] ;
269+ this . beginPhaseActive = true ;
180270 this . gameOverCount = 0 ;
181271 this . #entities = { } ;
182272 this . #missingEntityIds. clear ( ) ;
@@ -248,22 +338,34 @@ export class GameState {
248338 * this handles the name resolution. Recommended place is the TAG_CHANGE event.
249339 * @param entity
250340 */
251- resolveEntity ( entity : Pick < CardEntity , 'cardName' | ' entityId' | 'cardId '> & Partial < CardEntity > ) {
341+ resolveEntity ( entity : Pick < CardEntity , 'entityId' > & Partial < CardEntity > ) {
252342 const existing = this . #entities[ entity . entityId ] ;
253- this . #entities [ entity . entityId ] = merge ( {
343+ const newEntity = merge ( {
254344 type : 'card' ,
255345 tags : { } ,
256- player : 'bottom'
346+ player : 'bottom' ,
347+ cardName : ''
257348 } , existing , entity ) ;
258349
259- const { cardName, entityId, cardId} = entity ;
350+ this . #entities[ entity . entityId ] = newEntity ;
351+ const { cardName, entityId, cardId} = newEntity ;
260352 const newProps = { entityId, cardName, cardId} ;
261353
354+ // Update player cards in case this entity updated any important tags (like corrupt)
355+ for ( const player of this . players ) {
356+ for ( const card of player . cards . filter ( c => c . entityId === entity . entityId ) ) {
357+ const tags = identifySpecialTags ( this . #entities[ card . entityId ] ) ;
358+ if ( tags ) {
359+ card . tags = tags ;
360+ }
361+ }
362+ }
363+
262364 if ( isEmpty ( cardName ) || ! this . #missingEntityIds. has ( entityId ) ) {
263365 return ;
264366 }
265367
266- // Update entities for each log entry
368+ // Update entities for each match log entry (only card name, entity id, and card id)
267369 for ( const entry of this . matchLog ) {
268370 if ( isEmpty ( entry . source . cardName ) && entry . source . entityId === entityId ) {
269371 entry . source = { ...entry . source , ...newProps } ;
0 commit comments