import { Injectable } from '@angular/core' ; import { SceneMode , TaskStatus } from '@firestone-hs/reference-data' ; import { Subject } from 'rxjs' ; import { MemoryMercenariesCollectionInfo , MemoryVisitor } from '../../models/memory/memory-mercenaries-collection-info' ; import { MemoryMercenariesInfo } from '../../models/memory/memory-mercenaries-info' ; import { MemoryUpdate } from '../../models/memory/memory-update' ; import { Events } from '../events.service' ; import { LocalStorageService } from '../local-storage' ; import { MemoryInspectionService } from '../plugins/memory-inspection.service' ; import { PreferencesService } from '../preferences.service' ; import { areDeepEqual , groupByFunction , sleep } from '../utils' ; export const MERCENARIES_SCENES = [ SceneMode . LETTUCE_BOUNTY_BOARD , SceneMode . LETTUCE_BOUNTY_TEAM_SELECT , SceneMode . LETTUCE_COLLECTION , SceneMode . LETTUCE_COOP , SceneMode . LETTUCE_FRIENDLY , SceneMode . LETTUCE_MAP , SceneMode . LETTUCE_PACK_OPENING , SceneMode . LETTUCE_PLAY , SceneMode . LETTUCE_VILLAGE , ] ; export const SCENE_WITH_RELEVANT_MERC_INFO = [ SceneMode . GAMEPLAY , SceneMode . LETTUCE_BOUNTY_BOARD , SceneMode . LETTUCE_BOUNTY_TEAM_SELECT , SceneMode . LETTUCE_COLLECTION , SceneMode . LETTUCE_MAP , SceneMode . LETTUCE_PLAY , SceneMode . LETTUCE_VILLAGE , ] ; @ Injectable ( ) export class MercenariesMemoryCacheService { public memoryCollectionInfo$ = new Subject < MemoryMercenariesCollectionInfo > ( ) ; public memoryMapInfo$ = new Subject < MemoryMercenariesInfo > ( ) ; private previousScene : SceneMode ; private previousCollectionInfo : MemoryMercenariesCollectionInfo ; private previousVisitorsInfo : readonly MemoryVisitor [ ] ; private previousMapInfo : MemoryMercenariesInfo ; constructor ( private readonly memoryService , private readonly events , private readonly prefs , private readonly localStorageService ) { this . init ( ) ; } private init ( ) { this . events . on ( Events . MEMORY_UPDATE ) . subscribe ( async ( event ) => { const changes = event . data [ 0 ] ; const newScene = changes . CurrentScene ; if ( newScene ) { if ( ! this . shouldFetchMercenariesMemoryInfo ( newScene ) ) { this . previousScene = newScene ; return ; } this . previousScene = newScene ; console . debug ( '[merc-memory] changing scene, refreshing merc info' , newScene , SceneMode [ newScene ] ) ; if ( newScene === SceneMode . LETTUCE_MAP ) { await sleep ( 2000 ) ; console . debug ( '[merc-memory] done waiting' ) ; } } else if ( changes . IsMercenariesTasksUpdated ) { console . debug ( '[merc-memory] updating tasks' , changes ) ; } else { return ; } const newMercenariesCollectionInfo = await this . getMercenariesMergedCollectionInfo ( true ) ; if ( newMercenariesCollectionInfo ) { this . memoryCollectionInfo$ . next ( newMercenariesCollectionInfo ) ; } const mapInfo = await this . memoryService . getMercenariesInfo ( ) ; if ( ! areDeepEqual ( mapInfo , this . previousMapInfo ) ) { this . previousMapInfo = mapInfo ; this . memoryMapInfo$ . next ( mapInfo ) ; } } ) ; } private shouldFetchMercenariesMemoryInfo ( newScene ) { if ( ! SCENE_WITH_RELEVANT_MERC_INFO . includes ( newScene ) ) { return false ; } if ( newScene === SceneMode . GAMEPLAY && ! MERCENARIES_SCENES . includes ( this . previousScene ) ) { return false ; } return true ; } public async getMercenariesMergedCollectionInfo ( forceMemoryResetIfCollectionInfoEmpty = false ) { let newMercenariesInfo = await this . memoryService . getMercenariesCollectionInfo ( 5 , forceMemoryResetIfCollectionInfoEmpty ) ; if ( ! ! newMercenariesInfo ) { console . debug ( '[merc-memory] retrieved merc info from memory' , newMercenariesInfo ) ; } let readFromMemory = true ; if ( ! newMercenariesInfo ?. Mercenaries ?. length ) { newMercenariesInfo = await this . loadLocalMercenariesCollectionInfo ( ) ; readFromMemory = false ; } const prefs = await this . prefs . getPreferences ( ) ; const savedVisitorsInfo = prefs . mercenariesVisitorsProgress ?? [ ] ; if ( ! newMercenariesInfo || ( areDeepEqual ( newMercenariesInfo , this . previousCollectionInfo ) && areDeepEqual ( savedVisitorsInfo , this . previousVisitorsInfo ) ) ) { console . debug ( '[merc-memory] no new info' , newMercenariesInfo , this . previousCollectionInfo , savedVisitorsInfo , this . previousVisitorsInfo ) ; return null ; } this . previousCollectionInfo = newMercenariesInfo ; this . previousVisitorsInfo = savedVisitorsInfo ; await this . saveLocalMercenariesCollectionInfo ( newMercenariesInfo ) ; console . debug ( '[merc-memory] new merc info' , newMercenariesInfo . Visitors ) ; console . debug ( '[merc-memory] savedVisitorsInfo' , savedVisitorsInfo ) ; const newVisitorsInformation = this . mergeVisitors ( newMercenariesInfo . Visitors ?? [ ] , savedVisitorsInfo ?? [ ] , readFromMemory ) ; console . debug ( '[merc-memory] newVisitorsInformation' , newVisitorsInformation ) ; const newCollection = { ... newMercenariesInfo , Visitors : newVisitorsInformation , } ; console . debug ( '[merc-memory] newCollection' , newCollection ) ; return newCollection ; } private async saveLocalMercenariesCollectionInfo ( newMercenariesInfo ) { if ( ! newMercenariesInfo ?. Mercenaries ?. length ) { return ; } this . localStorageService . setItem ( LocalStorageService . LOCAL_STORAGE_MERCENARIES_COLLECTION , newMercenariesInfo ) ; return ; } private async loadLocalMercenariesCollectionInfo ( ) { const result = this . localStorageService . getItem ( LocalStorageService . LOCAL_STORAGE_MERCENARIES_COLLECTION ) ; console . debug ( '[merc-memory] retrieved mercenariesMemoryCollectionInfo from localStoarge' , result ) ; return result ; } private mergeVisitors ( fromMemory , savedVisitorsInfo , readFromMemory ) { const updatedSavedVisitorsInfo = savedVisitorsInfo . map ( ( visitor ) => { const memoryVisitor = fromMemory . find ( ( v ) => v . VisitorId === visitor . VisitorId && v . TaskId === visitor . TaskId ) ; return ! memoryVisitor && readFromMemory ? visitor . Status === TaskStatus . CLAIMED || visitor . Status === TaskStatus . COMPLETE ? { ... visitor , Status : TaskStatus . CLAIMED } : null : { ... visitor , Status : visitor . Status === TaskStatus . CLAIMED || visitor . Status === TaskStatus . COMPLETE ? TaskStatus . CLAIMED : memoryVisitor ?. Status ?? visitor . Status , } ; } ) . filter ( ( visitor ) => visitor ) ; console . debug ( '[merc-memory] updated savedVisitorsInfo' , updatedSavedVisitorsInfo ) ; const cleanedVisitors = this . cleanVisitors ( [ ... fromMemory , ... updatedSavedVisitorsInfo ] ) ; console . debug ( '[merc-memory] cleanedVisitors' , cleanedVisitors ) ; this . prefs . updateMercenariesVisitorsProgress ( cleanedVisitors ) ; return cleanedVisitors ; } private cleanVisitors ( visitors ) { console . debug ( '[merc-memory] cleaning visitors' , visitors ) ; const grouped = groupByFunction ( ( visitor ) => visitor . VisitorId ) ( visitors ) ; return Object . values ( grouped ) . map ( ( visitorGroup ) => { const highestProgress = Math . max ( ... visitorGroup . map ( ( v ) => v . TaskChainProgress ) ) ; const highestVisitor = visitorGroup . find ( ( v ) => v . TaskChainProgress === highestProgress ) ; return highestVisitor ; } ) ; } }