diff --git a/cultist/index.ts b/cultist/index.ts index 58c856730b..a5f552a3ad 100644 --- a/cultist/index.ts +++ b/cultist/index.ts @@ -1,3 +1,5 @@ export * from '//cultist/types'; export * as action from '//cultist/actions'; +export * as save from '//cultist/save'; +export * as state from '//cultist/state'; export { Action } from '//cultist/actions'; diff --git a/cultist/savestate.ts b/cultist/save.ts similarity index 92% rename from cultist/savestate.ts rename to cultist/save.ts index b5c9c7e613..0dc7cd7f85 100644 --- a/cultist/savestate.ts +++ b/cultist/save.ts @@ -4,10 +4,10 @@ export interface ElementInstance { lastTablePosY?: string; markedForConsumption?: string; elementId?: string; - quantity: string; + quantity?: string; } -export interface SaveState { +export interface State { elementStacks?: Record; decks?: Record; metainfo?: { @@ -70,12 +70,7 @@ export interface Situation { recipeId?: string | null; situationWindowX?: string; state?: string; - situationOutputNotes?: Record< - string, - { - title?: string; - } - >; + situationOutputNotes?: Record; situationWindowOpen?: string; completioncount?: string; } diff --git a/cultist/save_test.ts b/cultist/save_test.ts new file mode 100644 index 0000000000..b44b4fdc0f --- /dev/null +++ b/cultist/save_test.ts @@ -0,0 +1,6 @@ +import SaveStateExample from '//cultist/example/savestate'; +import * as save from '//cultist/save'; + +test('savestate', () => { + const test: save.State = SaveStateExample; +}); diff --git a/cultist/savestate_test.ts b/cultist/savestate_test.ts deleted file mode 100644 index ab84873e00..0000000000 --- a/cultist/savestate_test.ts +++ /dev/null @@ -1,6 +0,0 @@ -import SaveStateExample from '//cultist/example/savestate'; -import { SaveState } from '//cultist/savestate'; - -test('savestate', () => { - const test: SaveState = SaveStateExample; -}); diff --git a/cultist/state.ts b/cultist/state.ts new file mode 100644 index 0000000000..38cfd833fc --- /dev/null +++ b/cultist/state.ts @@ -0,0 +1,168 @@ +/** + * @fileoverview like save state, but more sane. + */ + +import * as save from '//cultist/save'; + +export interface State { + elementStacks?: { + [name: string]: ElementInstance; + }; + + decks?: { + [name: string]: Deck; + }; + + metainfo?: { + VERSIONNUMBER?: string; + }; + + characterDetails?: CharacterDetails; + + situations?: { + [name: string]: Situation; + }; +} + +function dictMap( + o: { [key: string]: V }, + f: (v: V) => O +): { [key: string]: O } { + return Object.assign( + {}, + ...Object.entries(o).map(([n, el]) => ({ [n]: f(el) })) + ); +} + +export function serializeState(s: State): save.State { + return { + ...s, + elementStacks: optionalChain(dictMap)( + s.elementStacks, + serializeElementInstance + ), + decks: optionalChain(dictMap)(s.decks, serializeDeck), + situations: optionalChain(dictMap)(s.situations, SerializeSituation), + }; +} + +export interface ElementInstance { + /** Time in seconds */ + lifetimeRemaining?: number; + lastTablePosX?: number; + lastTablePosY?: number; + /** Will be destroyed when recipe completes */ + markedForConsumption?: boolean; + elementId?: string; + quantity?: number; +} + +function optionalChain(f: (v: T, ...a: P) => O) { + return (v: T | undefined, ...a: P) => { + if (v === undefined) return undefined; + return f(v, ...a); + }; +} + +export function serializeBoolean(b: boolean): string { + return b ? 'True' : 'False'; +} + +export function serializeElementInstance( + e: ElementInstance +): save.ElementInstance { + return { + ...e, + lifetimeRemaining: e.lifetimeRemaining?.toString(), + lastTablePosX: e.lastTablePosX?.toString(), + lastTablePosY: e.lastTablePosY?.toString(), + markedForConsumption: optionalChain(serializeBoolean)( + e.markedForConsumption + ), + quantity: e.quantity?.toString(), + }; +} + +export interface Deck { + eliminatedCards: string[]; + cards: string[]; +} + +function serializeDeck({ eliminatedCards, cards }: Deck): save.Deck { + return { + eliminatedCards, + ...Object.assign( + {}, + ...cards.map(([card, index]) => ({ [index]: card })) + ), + }; +} + +export interface Levers { + lastheadquarters?: string; + lastfollower?: string; + lastsignificantpainting?: string; + lastpersonkilled?: string; + lastcharactername?: string; + lastcult?: string; + lasttool?: string; + lastbook?: string; + lastdesire?: string; +} + +export interface CharacterDetails { + name?: string; + /** + * Just a label, not an ID. + */ + profession?: string; + + pastLevers?: Levers; + + executions?: { + /** + * Not sure what this means yet, + * but it might be running recipes. + */ + [key: string]: string; + }; + + futureLevers?: Levers; + + activeLegacy?: string; +} + +export interface Situation { + situationStoredElements?: Record; + verbId?: string; + ongoingSlotElements?: Record; + situationWindowY?: string; + title?: string; + timeRemaining?: string; + recipeId?: string | null; + situationWindowX?: string; + state?: string; + situationOutputNotes?: Record< + string, + { + title?: string; + } + >; + situationWindowOpen?: string; + completioncount?: string; +} + +function SerializeSituation(s: Situation): save.Situation { + return { + ...s, + situationStoredElements: optionalChain(dictMap)( + s.situationStoredElements, + serializeElementInstance + ), + + ongoingSlotElements: optionalChain(dictMap)( + s.ongoingSlotElements, + serializeElementInstance + ), + }; +} diff --git a/package.json b/package.json index b562bc16f0..9c829c9c7c 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "concurrently": "^6.2.1", "jest-cli": "^27.0.6", "module-alias": "^2.2.2", + "onchange": "^7.1.0", "prettier": "^2.3.2", "ts-node": "^10.2.1", "ttypescript": "^1.5.12", @@ -23,7 +24,9 @@ "scripts": { "build": "yarn bazel build //...", "test": "yarn bazel test //...", - "fix": "yarn prettier '**/*.ts' --ignore-path .gitignore --config .prettierrc.json --write" + "fix": "yarn run prettier-cmd '**/*.ts'", + "prettier-cmd": "yarn prettier --ignore-path .gitignore --config .prettierrc.json --write", + "prettier-watch": "onchange --exclude-path .gitignore '**/*.ts' -- yarn run prettier-cmd --ignore-unknown {{changed}}" }, "dependencies": { "@bazel/labs": "^3.8.0", diff --git a/yarn.lock b/yarn.lock index afed1e58d4..eadb4d5099 100644 --- a/yarn.lock +++ b/yarn.lock @@ -332,6 +332,16 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== +"@blakeembrey/deque@^1.0.5": + version "1.0.5" + resolved "https://registry.yarnpkg.com/@blakeembrey/deque/-/deque-1.0.5.tgz#f4fa17fc5ee18317ec01a763d355782c7b395eaf" + integrity sha512-6xnwtvp9DY1EINIKdTfvfeAtCYw4OqBZJhtiqkT3ivjnEfa25VQ3TsKvaFfKm8MyGIEfE95qLe+bNEt3nB0Ylg== + +"@blakeembrey/template@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@blakeembrey/template/-/template-1.0.0.tgz#bf8828bc3ae8004d97904d78f64e3cc2cd216438" + integrity sha512-J6WGZqCLdRMHUkyRG6fBSIFJ0rL60/nsQNh5rQvsYZ5u0PsKw6XQcJcA3DWvd9cN3j/IQx5yB1fexhCafwwUUw== + "@cspotcode/source-map-consumer@0.8.0": version "0.8.0" resolved "https://registry.yarnpkg.com/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz#33bf4b7b39c178821606f669bbc447a6a629786b" @@ -824,7 +834,7 @@ ansi-styles@^5.0.0: resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== -anymatch@^3.0.3: +anymatch@^3.0.3, anymatch@~3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== @@ -832,7 +842,7 @@ anymatch@^3.0.3: normalize-path "^3.0.0" picomatch "^2.0.4" -arg@^4.1.0: +arg@^4.1.0, arg@^4.1.3: version "4.1.3" resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== @@ -920,6 +930,11 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== +binary-extensions@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" + integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== + brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -928,7 +943,7 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" -braces@^3.0.1: +braces@^3.0.1, braces@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== @@ -1020,6 +1035,21 @@ check-type@^0.4.11: dependencies: underscore "1.6.0" +chokidar@^3.3.1: + version "3.5.2" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.2.tgz#dba3976fcadb016f66fd365021d91600d01c1e75" + integrity sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + ci-info@^3.1.1: version "3.2.0" resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.2.0.tgz#2876cb948a498797b5236f0095bc057d0dca38b6" @@ -1117,7 +1147,7 @@ create-require@^1.1.0: resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== -cross-spawn@^7.0.3: +cross-spawn@^7.0.1, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== @@ -1358,7 +1388,7 @@ fs.realpath@^1.0.0: resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= -fsevents@^2.3.2: +fsevents@^2.3.2, fsevents@~2.3.2: version "2.3.2" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== @@ -1388,6 +1418,13 @@ get-stream@^6.0.0: resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== +glob-parent@~5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4: version "7.1.7" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90" @@ -1473,6 +1510,11 @@ iconv-lite@0.4.24: dependencies: safer-buffer ">= 2.1.2 < 3" +ignore@^5.1.4: + version "5.1.8" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57" + integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw== + immutable@^4.0.0-rc.14: version "4.0.0-rc.14" resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.0.0-rc.14.tgz#29ba96631ec10867d1348515ac4e6bdba462f071" @@ -1509,6 +1551,13 @@ is-arrayish@^0.2.1: resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + is-ci@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-3.0.0.tgz#c7e7be3c9d8eef7d0fa144390bd1e4b88dc4c994" @@ -1523,6 +1572,11 @@ is-core-module@^2.2.0: dependencies: has "^1.0.3" +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= + is-fullwidth-code-point@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" @@ -1533,6 +1587,13 @@ is-generator-fn@^2.0.0: resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== +is-glob@^4.0.1, is-glob@~4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" + integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== + dependencies: + is-extglob "^2.1.1" + is-number@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" @@ -2240,7 +2301,7 @@ normalize-package-data@^2.5.0: semver "2 || 3 || 4 || 5" validate-npm-package-license "^3.0.1" -normalize-path@^3.0.0: +normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== @@ -2264,6 +2325,19 @@ once@^1.3.0: dependencies: wrappy "1" +onchange@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/onchange/-/onchange-7.1.0.tgz#a6f0f7733e4d47014b4cd70aa1ad36c2b4cf3804" + integrity sha512-ZJcqsPiWUAUpvmnJri5TPBooqJOPmC0ttN65juhN15Q8xA+Nbg3BaxBHXQ45EistKKlKElb0edmbPWnKSBkvMg== + dependencies: + "@blakeembrey/deque" "^1.0.5" + "@blakeembrey/template" "^1.0.0" + arg "^4.1.3" + chokidar "^3.3.1" + cross-spawn "^7.0.1" + ignore "^5.1.4" + tree-kill "^1.2.2" + onetime@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" @@ -2354,7 +2428,7 @@ path-parse@^1.0.6: resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== -picomatch@^2.0.4, picomatch@^2.2.3: +picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3: version "2.3.0" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972" integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw== @@ -2460,6 +2534,13 @@ read-pkg@^5.2.0: parse-json "^5.0.0" type-fest "^0.6.0" +readdirp@~3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" + integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== + dependencies: + picomatch "^2.2.1" + require-directory@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"