From 796dff6012345432b6a76a67a43eb09cff7634ef Mon Sep 17 00:00:00 2001 From: chrstngyn Date: Mon, 28 Mar 2022 15:57:47 -0700 Subject: [PATCH] BREAKING CHANGE: restructure folder that contains events components made breaking renames and refactors to implement better naming conventions for components and types remove duplicate nested events/events dir refactored file structure: events | |-- case (contains specific occurrences of a given event) | |-- type (categorizes events into 2 types: Basic and Interactive) | |-- Card.vue, Container.vue naming convention for case/ components is: verb + noun/thing (e.g. VotePlayer) other refactors: EventClientView -> EventCase currentEventView -> currentEventCase clientViewHandler -> eventCaseClientHandler refs #764 --- client/src/api/tutorial/steps/events.ts | 2 +- client/src/components/game/HUDRight.vue | 2 +- client/src/components/game/phases/Events.vue | 4 +- .../phases/events/{EventCard.vue => Card.vue} | 8 +- .../game/phases/events/Container.vue | 47 ++++ .../DiscardAccomplishment.vue} | 2 +- .../DrawResource.vue} | 2 +- .../SaveResource.vue} | 2 +- .../VoteHeroPariah.vue} | 2 +- .../game/phases/events/case/VotePlayer.vue | 68 +++++ .../{events/views => case}/VoteYesNo.vue | 0 .../events/events/EventAccomplishments.vue | 30 --- .../phases/events/events/EventContainer.vue | 59 ----- .../phases/events/events/EventInfluences.vue | 34 --- .../phases/events/events/EventNoChange.vue | 49 ---- .../game/phases/events/events/EventVote.vue | 38 --- .../events/views/VoteForPlayerSingle.vue | 63 ----- .../game/phases/events/type/Basic.vue | 32 +++ .../game/phases/events/type/Interactive.vue | 44 ++++ client/src/store/getters.ts | 66 ++--- server/src/data/MarsEvents.ts | 244 +++++++++--------- server/src/rooms/game/state/bot.ts | 211 +++++++++------ .../rooms/game/state/marsevents/MarsEvent.ts | 50 ++-- shared/src/types.ts | 200 +++++++------- 24 files changed, 639 insertions(+), 620 deletions(-) rename client/src/components/game/phases/events/{EventCard.vue => Card.vue} (93%) create mode 100644 client/src/components/game/phases/events/Container.vue rename client/src/components/game/phases/events/{events/views/AccomplishmentsSelectPurchased.vue => case/DiscardAccomplishment.vue} (98%) rename client/src/components/game/phases/events/{events/views/InfluencesDraw.vue => case/DrawResource.vue} (98%) rename client/src/components/game/phases/events/{events/views/InfluencesSelect.vue => case/SaveResource.vue} (99%) rename client/src/components/game/phases/events/{events/views/VoteForPlayerHeroPariah.vue => case/VoteHeroPariah.vue} (98%) create mode 100644 client/src/components/game/phases/events/case/VotePlayer.vue rename client/src/components/game/phases/events/{events/views => case}/VoteYesNo.vue (100%) delete mode 100644 client/src/components/game/phases/events/events/EventAccomplishments.vue delete mode 100644 client/src/components/game/phases/events/events/EventContainer.vue delete mode 100644 client/src/components/game/phases/events/events/EventInfluences.vue delete mode 100644 client/src/components/game/phases/events/events/EventNoChange.vue delete mode 100644 client/src/components/game/phases/events/events/EventVote.vue delete mode 100644 client/src/components/game/phases/events/events/views/VoteForPlayerSingle.vue create mode 100644 client/src/components/game/phases/events/type/Basic.vue create mode 100644 client/src/components/game/phases/events/type/Interactive.vue diff --git a/client/src/api/tutorial/steps/events.ts b/client/src/api/tutorial/steps/events.ts index 94f4a6130..9039923af 100644 --- a/client/src/api/tutorial/steps/events.ts +++ b/client/src/api/tutorial/steps/events.ts @@ -35,7 +35,7 @@ const steps: Array = [ of this round.)`, flavorText: `Create contingencies for your contingencies and contingencies for those contingencies. Then prepare to improvise.`, - clientViewHandler: "NO_CHANGE" as const, + eventCaseClientHandler: "NO_CHANGE" as const, duration: 1 } } diff --git a/client/src/components/game/HUDRight.vue b/client/src/components/game/HUDRight.vue index 823a83fe3..53d6b9b52 100644 --- a/client/src/components/game/HUDRight.vue +++ b/client/src/components/game/HUDRight.vue @@ -51,7 +51,7 @@ import { Vue, Component } from "vue-property-decorator"; import HUDRightButtons from "@port-of-mars/client/components/game/HUDRightButtons.vue"; import GameInformation from "./static/panels/GameInformation.vue"; import PhaseInstructions from "./static/panels/PhaseInstructions.vue"; -import EventCard from "@port-of-mars/client/components/game/phases/events/EventCard.vue"; +import EventCard from "@port-of-mars/client/components/game/phases/events/Card.vue"; import { Phase } from "@port-of-mars/shared/types"; import { HUDRightView } from "@port-of-mars/shared/game/client/panes"; diff --git a/client/src/components/game/phases/Events.vue b/client/src/components/game/phases/Events.vue index fdba173ca..c29aebf0b 100644 --- a/client/src/components/game/phases/Events.vue +++ b/client/src/components/game/phases/Events.vue @@ -43,8 +43,8 @@ + + diff --git a/client/src/components/game/phases/events/events/views/AccomplishmentsSelectPurchased.vue b/client/src/components/game/phases/events/case/DiscardAccomplishment.vue similarity index 98% rename from client/src/components/game/phases/events/events/views/AccomplishmentsSelectPurchased.vue rename to client/src/components/game/phases/events/case/DiscardAccomplishment.vue index 3a5ae8182..2a1685be7 100644 --- a/client/src/components/game/phases/events/events/views/AccomplishmentsSelectPurchased.vue +++ b/client/src/components/game/phases/events/case/DiscardAccomplishment.vue @@ -54,7 +54,7 @@ import _ from "lodash"; AccomplishmentCard } }) -export default class AccomplishmentsSelectPurchased extends Vue { +export default class DiscardAccomplishment extends Vue { @Inject() readonly api!: GameRequestAPI; purchasedAccomplishmentsLength: number = Object.keys(this.purchasedAccomplishments).length; diff --git a/client/src/components/game/phases/events/events/views/InfluencesDraw.vue b/client/src/components/game/phases/events/case/DrawResource.vue similarity index 98% rename from client/src/components/game/phases/events/events/views/InfluencesDraw.vue rename to client/src/components/game/phases/events/case/DrawResource.vue index a57115880..71baba543 100644 --- a/client/src/components/game/phases/events/events/views/InfluencesDraw.vue +++ b/client/src/components/game/phases/events/case/DrawResource.vue @@ -44,7 +44,7 @@ import { Role, Resource } from "@port-of-mars/shared/types"; import { GameRequestAPI } from "@port-of-mars/client/api/game/request"; @Component({}) -export default class InfluencesDraw extends Vue { +export default class DrawResource extends Vue { drawnInfluence: string = "None Selected"; @Inject() api!: GameRequestAPI; diff --git a/client/src/components/game/phases/events/events/views/InfluencesSelect.vue b/client/src/components/game/phases/events/case/SaveResource.vue similarity index 99% rename from client/src/components/game/phases/events/events/views/InfluencesSelect.vue rename to client/src/components/game/phases/events/case/SaveResource.vue index 46f8380b6..b613cb63d 100644 --- a/client/src/components/game/phases/events/events/views/InfluencesSelect.vue +++ b/client/src/components/game/phases/events/case/SaveResource.vue @@ -98,7 +98,7 @@ import TimeBlockMeter from "@port-of-mars/client/components/game/phases/investme InvestmentCard } }) -export default class InfluencesSelect extends Vue { +export default class SaveResource extends Vue { @Inject() readonly api!: GameRequestAPI; icon = { center: true, diff --git a/client/src/components/game/phases/events/events/views/VoteForPlayerHeroPariah.vue b/client/src/components/game/phases/events/case/VoteHeroPariah.vue similarity index 98% rename from client/src/components/game/phases/events/events/views/VoteForPlayerHeroPariah.vue rename to client/src/components/game/phases/events/case/VoteHeroPariah.vue index 29117ede0..58e669b61 100644 --- a/client/src/components/game/phases/events/events/views/VoteForPlayerHeroPariah.vue +++ b/client/src/components/game/phases/events/case/VoteHeroPariah.vue @@ -64,7 +64,7 @@ import { Role, ROLES } from "@port-of-mars/shared/types"; import { GameRequestAPI } from "@port-of-mars/client/api/game/request"; @Component({}) -export default class VoteForPlayerHeroPariah extends Vue { +export default class VoteHeroPariah extends Vue { @Inject() api!: GameRequestAPI; voteHeroOrPariah: "hero" | "pariah" | "" = ""; role: Role | "" = ""; diff --git a/client/src/components/game/phases/events/case/VotePlayer.vue b/client/src/components/game/phases/events/case/VotePlayer.vue new file mode 100644 index 000000000..17c3a5eb4 --- /dev/null +++ b/client/src/components/game/phases/events/case/VotePlayer.vue @@ -0,0 +1,68 @@ + + + + + diff --git a/client/src/components/game/phases/events/events/views/VoteYesNo.vue b/client/src/components/game/phases/events/case/VoteYesNo.vue similarity index 100% rename from client/src/components/game/phases/events/events/views/VoteYesNo.vue rename to client/src/components/game/phases/events/case/VoteYesNo.vue diff --git a/client/src/components/game/phases/events/events/EventAccomplishments.vue b/client/src/components/game/phases/events/events/EventAccomplishments.vue deleted file mode 100644 index 2f8fcba40..000000000 --- a/client/src/components/game/phases/events/events/EventAccomplishments.vue +++ /dev/null @@ -1,30 +0,0 @@ - - - - - diff --git a/client/src/components/game/phases/events/events/EventContainer.vue b/client/src/components/game/phases/events/events/EventContainer.vue deleted file mode 100644 index 8574b1c8a..000000000 --- a/client/src/components/game/phases/events/events/EventContainer.vue +++ /dev/null @@ -1,59 +0,0 @@ - - - - - diff --git a/client/src/components/game/phases/events/events/EventInfluences.vue b/client/src/components/game/phases/events/events/EventInfluences.vue deleted file mode 100644 index ee712792d..000000000 --- a/client/src/components/game/phases/events/events/EventInfluences.vue +++ /dev/null @@ -1,34 +0,0 @@ - - - - - diff --git a/client/src/components/game/phases/events/events/EventNoChange.vue b/client/src/components/game/phases/events/events/EventNoChange.vue deleted file mode 100644 index fc851d450..000000000 --- a/client/src/components/game/phases/events/events/EventNoChange.vue +++ /dev/null @@ -1,49 +0,0 @@ - - - - - diff --git a/client/src/components/game/phases/events/events/EventVote.vue b/client/src/components/game/phases/events/events/EventVote.vue deleted file mode 100644 index 7e45bed5c..000000000 --- a/client/src/components/game/phases/events/events/EventVote.vue +++ /dev/null @@ -1,38 +0,0 @@ - - - - - diff --git a/client/src/components/game/phases/events/events/views/VoteForPlayerSingle.vue b/client/src/components/game/phases/events/events/views/VoteForPlayerSingle.vue deleted file mode 100644 index 4f0163220..000000000 --- a/client/src/components/game/phases/events/events/views/VoteForPlayerSingle.vue +++ /dev/null @@ -1,63 +0,0 @@ - - - - - diff --git a/client/src/components/game/phases/events/type/Basic.vue b/client/src/components/game/phases/events/type/Basic.vue new file mode 100644 index 000000000..1d8b2b555 --- /dev/null +++ b/client/src/components/game/phases/events/type/Basic.vue @@ -0,0 +1,32 @@ + + + + + diff --git a/client/src/components/game/phases/events/type/Interactive.vue b/client/src/components/game/phases/events/type/Interactive.vue new file mode 100644 index 000000000..4b664d62c --- /dev/null +++ b/client/src/components/game/phases/events/type/Interactive.vue @@ -0,0 +1,44 @@ + + + + + diff --git a/client/src/store/getters.ts b/client/src/store/getters.ts index 73f12f4eb..14c6697b1 100644 --- a/client/src/store/getters.ts +++ b/client/src/store/getters.ts @@ -1,15 +1,21 @@ -import {User, PlayerClientData, PlayerClientSet, State} from '@port-of-mars/shared/game/client/state'; import { - EventClientView, + User, + PlayerClientData, + PlayerClientSet, + State +} from "@port-of-mars/shared/game/client/state"; +import { + EventCase, MarsEventData, MarsLogCategory, - MarsLogMessageData, Phase, - ROLES, -} from '@port-of-mars/shared/types'; + MarsLogMessageData, + Phase, + ROLES +} from "@port-of-mars/shared/types"; export default { isAuthenticated(state: State): boolean { - return state.user?.username !== ''; + return state.user?.username !== ""; }, user(state: State): User { @@ -93,22 +99,21 @@ export default { return undefined; }, - /** * During events phase, gets current view of the center bottom container. * @param state The current state of the game. * @returns The current event view. * */ - currentEventView(state: State): EventClientView { + currentEventCase(state: State): EventCase { const marsEvents = state.marsEvents; const marsEventsProcessed = state.marsEventsProcessed; const current = marsEvents[marsEventsProcessed]; if (current) { - const view = current.clientViewHandler; + const view = current.eventCaseClientHandler; return view; } - return 'NO_CHANGE'; + return "NO_CHANGE"; }, /** @@ -120,45 +125,48 @@ export default { isUnderAudit(state: State): boolean { const marsEvents = state.marsEvents; const eventProcessedIndex = state.marsEventsProcessed; - const auditEventIndex = marsEvents.findIndex(event => event.id === 'audit'); + const auditEventIndex = marsEvents.findIndex(event => event.id === "audit"); return auditEventIndex !== -1 && auditEventIndex <= eventProcessedIndex; }, isEffortsWastedActive(state: State): boolean { const marsEvents = state.marsEvents; const processedIndex = state.marsEventsProcessed; - const effortsWastedIndex = marsEvents.findIndex(event => event.id === 'effortsWasted'); - return state.phase === Phase.events && effortsWastedIndex !== -1 && effortsWastedIndex <= processedIndex; + const effortsWastedIndex = marsEvents.findIndex(event => event.id === "effortsWasted"); + return ( + state.phase === Phase.events && + effortsWastedIndex !== -1 && + effortsWastedIndex <= processedIndex + ); }, isChatAvailable(state: State): boolean { const marsEvents = state.marsEvents; const eventProcessedIndex = state.marsEventsProcessed; - const solarFlareIndex = marsEvents.findIndex(event => event.id === 'solarFlare'); + const solarFlareIndex = marsEvents.findIndex(event => event.id === "solarFlare"); return !(solarFlareIndex !== -1 && eventProcessedIndex <= eventProcessedIndex); }, categoryColorMap(state: State): Map { - return new Map([ // round - [MarsLogCategory.newRound, 'var(--light-shade-05)'], + [MarsLogCategory.newRound, "var(--light-shade-05)"], // system health - [MarsLogCategory.systemHealth, 'var(--marslog-red)'], - [MarsLogCategory.systemHealthContributions, 'var(--marslog-red)'], + [MarsLogCategory.systemHealth, "var(--marslog-red)"], + [MarsLogCategory.systemHealthContributions, "var(--marslog-red)"], // trade - [MarsLogCategory.trade, 'var(--marslog-purple)'], - [MarsLogCategory.sentTrade, 'var(--marslog-purple)'], - [MarsLogCategory.acceptTrade, 'var(--marslog-purple)'], - [MarsLogCategory.rejectTrade, 'var(--marslog-purple)'], - [MarsLogCategory.cancelTrade, 'var(--marslog-purple)'], - [MarsLogCategory.invalidTrade, 'var(--marslog-purple)'], + [MarsLogCategory.trade, "var(--marslog-purple)"], + [MarsLogCategory.sentTrade, "var(--marslog-purple)"], + [MarsLogCategory.acceptTrade, "var(--marslog-purple)"], + [MarsLogCategory.rejectTrade, "var(--marslog-purple)"], + [MarsLogCategory.cancelTrade, "var(--marslog-purple)"], + [MarsLogCategory.invalidTrade, "var(--marslog-purple)"], // accomplishment - [MarsLogCategory.accomplishment, 'var(--marslog-green)'], - [MarsLogCategory.purchaseAccomplishment, 'var(--marslog-green)'], + [MarsLogCategory.accomplishment, "var(--marslog-green)"], + [MarsLogCategory.purchaseAccomplishment, "var(--marslog-green)"] ]); }, @@ -176,7 +184,5 @@ export default { toggleYourTrades(state: State) { return state.userInterface.toggleYourTrades; - }, - - -} + } +}; diff --git a/server/src/data/MarsEvents.ts b/server/src/data/MarsEvents.ts index 1afbc96e3..181dcb0da 100644 --- a/server/src/data/MarsEvents.ts +++ b/server/src/data/MarsEvents.ts @@ -1,220 +1,222 @@ -import _ from 'lodash'; -import { MarsEventData } from '@port-of-mars/shared/types'; -import { getLogger } from '@port-of-mars/server/settings'; - +import _ from "lodash"; +import { MarsEventData } from "@port-of-mars/shared/types"; +import { getLogger } from "@port-of-mars/server/settings"; const logger = getLogger(__filename); -export type MarsEventDeckItem = { event: MarsEventData, numberOfCopies: number } +export type MarsEventDeckItem = { + event: MarsEventData; + numberOfCopies: number; +}; const _marsEvents: Array = [ { - id: 'audit', - name: 'Audit', + id: "audit", + name: "Audit", effect: `In this round, players will be able to view each other's accomplishments, inventories, resources and investment decisions.`, flavorText: `"Of course we trust everyone to be truthful. But it doesn't hurt to check now and again." - The Politician`, - clientViewHandler: 'AUDIT' as const, + eventCaseClientHandler: "AUDIT" as const, duration: 1, timeDuration: 10, }, { - id: 'bondingThroughAdversity', - name: 'Bonding Through Adversity', - effect: 'Each player gains one unit of Influence of their choice.', - flavorText: 'Challenges brings communities together.', - clientViewHandler: 'INFLUENCES_DRAW' as const, - duration: 1 + id: "bondingThroughAdversity", + name: "Bonding Through Adversity", + effect: "Each player gains one unit of Influence of their choice.", + flavorText: "Challenges brings communities together.", + eventCaseClientHandler: "DRAW_RESOURCE" as const, + duration: 1, }, { - id: 'breakdownOfTrust', - name: 'Breakdown of Trust', + id: "breakdownOfTrust", + name: "Breakdown of Trust", effect: `Each player can choose to save up to 2 units of Influence that they already own. The rest will be lost.`, flavorText: `Setbacks are inevitable, but no less painful each time.`, - clientViewHandler: 'INFLUENCES_SELECT' as const, - duration: 1 + eventCaseClientHandler: "SAVE_RESOURCE" as const, + duration: 1, }, { - id: 'changingTides', - name: 'Changing Tides', + id: "changingTides", + name: "Changing Tides", effect: `Each player discards all of their available Accomplishments and draws 1 new Accomplishment. You will be able to discard this Accomplishment at the end of this round and draw up to three new Accomplishments at the start of the next round (if this is not the final round).`, flavorText: `Create contingencies for your contingencies and contingencies for those contingencies. Then prepare to improvise.`, - clientViewHandler: 'NO_CHANGE' as const, + eventCaseClientHandler: "NO_CHANGE" as const, timeDuration: 10, - duration: 1 + duration: 1, }, { - id: 'compulsivePhilanthropy', - name: 'Compulsive Philanthropy', + id: "compulsivePhilanthropy", + name: "Compulsive Philanthropy", effect: `Players must vote for one player to put all their Time Blocks into System Health this round.`, flavorText: `There's nothing quite like being volun-told for the greater good.`, - clientViewHandler: 'VOTE_FOR_PLAYER_SINGLE' as const, - duration: 1 + clientViewHandler: "VOTE_PLAYER" as const, + duration: 1, }, { - id: 'cropFailure', - name: 'Crop Failure', + id: "cropFailure", + name: "Crop Failure", effect: `Destroy 20 System Health.`, flavorText: `"The good news is we're not eating any more potatoes this cycle! The bad news is we're not sure what we're eating." - The Researcher`, - clientViewHandler: 'NO_CHANGE' as const, + eventCaseClientHandler: "NO_CHANGE" as const, duration: 1, timeDuration: 10, }, { - id: 'difficultConditions', - name: 'Difficult Conditions', + id: "difficultConditions", + name: "Difficult Conditions", effect: `System Health costs twice as many Time Blocks as usual this round.`, flavorText: `When one component breaks, it puts a strain on the rest of the system. Small failures often snowball into critical ones.`, - clientViewHandler: 'NO_CHANGE' as const, + eventCaseClientHandler: "NO_CHANGE" as const, duration: 1, timeDuration: 15, }, { - id: 'effortsWasted', - name: 'Efforts Wasted', + id: "effortsWasted", + name: "Efforts Wasted", effect: `Each player must discard an Accomplishment that they have already purchased.`, flavorText: `"All markets are volatile. The trick is learning how to ride the waves." - The Entrepreneur`, - clientViewHandler: 'ACCOMPLISHMENT_SELECT_PURCHASED' as const, - duration: 1 + eventCaseClientHandler: "DISCARD_ACCOMPLISHMENT" as const, + duration: 1, }, { - id: 'heroOrPariah', - name: 'Hero or Pariah', + id: "heroOrPariah", + name: "Hero or Pariah", effect: `CHOOSE ONE: Players must vote for 1 player to lose all Influence OR Players must vote for 1 player to gain 4 of their specialty Influence`, flavorText: `In a community as small as Port of Mars, some individuals always stand out - for better or worse.`, - clientViewHandler: 'VOTE_FOR_PLAYER_HERO_PARIAH' as const, - duration: 1 + eventCaseClientHandler: "VOTE_HERO_PARIAH" as const, + duration: 1, }, { - id: 'hullBreach', - name: 'Hull Breach', + id: "hullBreach", + name: "Hull Breach", effect: `Destroy 7 System Health.`, flavorText: `"Accidents happen. It's unavoidable. Our job is to do our best to avoid them all the same."`, - clientViewHandler: 'NO_CHANGE' as const, + eventCaseClientHandler: "NO_CHANGE" as const, duration: 1, timeDuration: 10, }, { - id: 'interdisciplinary', - name: 'Interdisciplinary', + id: "interdisciplinary", + name: "Interdisciplinary", effect: `For this round, each player can spend 3 Time Blocks to earn an Influence in either of the 2 Influences they normally can't create.`, flavorText: `"Everyone knows the saying, 'Jack of all trades, master of none.' Few remember the second part: 'still better than a master of one.'" - The Pioneer`, - clientViewHandler: 'NO_CHANGE' as const, + eventCaseClientHandler: "NO_CHANGE" as const, duration: 1, timeDuration: 10, }, { - id: 'lifeAsUsual', - name: 'Life as Usual', - effect: 'No special effect', + id: "lifeAsUsual", + name: "Life as Usual", + effect: "No special effect", flavorText: `As the first human outpost on Mars, having a "usual" day is pretty unusual.`, - clientViewHandler: 'NO_CHANGE' as const, + eventCaseClientHandler: "NO_CHANGE" as const, duration: 1, timeDuration: 10, }, { - id: 'lostTime', - name: 'Lost Time', + id: "lostTime", + name: "Lost Time", effect: `Each player has 5 fewer Time Blocks to spend this round.`, flavorText: `Time flies when you're trying to stay alive.`, - clientViewHandler: 'NO_CHANGE' as const, + eventCaseClientHandler: "NO_CHANGE" as const, duration: 1, timeDuration: 10, }, { - id: 'marketsClosed', - name: 'Markets Closed', + id: "marketsClosed", + name: "Markets Closed", effect: `Players may not trade Influences this round.`, flavorText: `"Trust is difficult to build and easy to break. Yet without it, this community would fall apart." - The Curator`, - clientViewHandler: 'NO_CHANGE' as const, + eventCaseClientHandler: "NO_CHANGE" as const, duration: 1, timeDuration: 10, }, { - id: 'murphysLaw', + id: "murphysLaw", name: "Murphy's Law", effect: `Reveal 2 more events. They're both in effect.`, flavorText: `Residents at Port of Mars know better than to ask, "what ELSE could go wrong?"`, - clientViewHandler: 'NO_CHANGE' as const, + eventCaseClientHandler: "NO_CHANGE" as const, duration: 1, timeDuration: 10, }, { - id: 'outOfCommissionCurator', - name: 'Out of Commission', - effect: 'The Curator receives only 3 Time Blocks this round.', + id: "outOfCommissionCurator", + name: "Out of Commission", + effect: "The Curator receives only 3 Time Blocks this round.", flavorText: `The mental and physical health of all residents is critical to mission success. The absence of even one person can have rippling effects on the community.`, - clientViewHandler: 'NO_CHANGE' as const, + eventCaseClientHandler: "NO_CHANGE" as const, duration: 1, timeDuration: 15, }, { - id: 'outOfCommissionEntrepreneur', - name: 'Out of Commission', - effect: 'The Entrepreneur receives only 3 Time Blocks this round.', + id: "outOfCommissionEntrepreneur", + name: "Out of Commission", + effect: "The Entrepreneur receives only 3 Time Blocks this round.", flavorText: `The mental and physical health of all residents is critical to mission success. The absence of even one person can have rippling effects on the community.`, - clientViewHandler: 'NO_CHANGE' as const, + eventCaseClientHandler: "NO_CHANGE" as const, duration: 1, timeDuration: 15, }, { - id: 'outOfCommissionPioneer', - name: 'Out of Commission', - effect: 'The Pioneer receives only 3 Time Blocks this round.', + id: "outOfCommissionPioneer", + name: "Out of Commission", + effect: "The Pioneer receives only 3 Time Blocks this round.", flavorText: `The mental and physical health of all residents is critical to mission success. The absence of even one person can have rippling effects on the community.`, - clientViewHandler: 'NO_CHANGE' as const, + eventCaseClientHandler: "NO_CHANGE" as const, duration: 1, timeDuration: 15, }, { - id: 'outOfCommissionPolitician', - name: 'Out of Commission', - effect: 'The Politician receives only 3 Time Blocks this round.', + id: "outOfCommissionPolitician", + name: "Out of Commission", + effect: "The Politician receives only 3 Time Blocks this round.", flavorText: `The mental and physical health of all residents is critical to mission success. The absence of even one person can have rippling effects on the community.`, - clientViewHandler: 'NO_CHANGE' as const, + eventCaseClientHandler: "NO_CHANGE" as const, duration: 1, timeDuration: 15, }, { - id: 'outOfCommissionResearcher', - name: 'Out of Commission', - effect: 'The Researcher receives only 3 Time Blocks this round.', + id: "outOfCommissionResearcher", + name: "Out of Commission", + effect: "The Researcher receives only 3 Time Blocks this round.", flavorText: `The mental and physical health of all residents is critical to mission success. The absence of even one person can have rippling effects on the community.`, - clientViewHandler: 'NO_CHANGE' as const, + eventCaseClientHandler: "NO_CHANGE" as const, duration: 1, timeDuration: 15, }, { - id: 'personalGain', - name: 'Personal Gain', + id: "personalGain", + name: "Personal Gain", effect: `Each player secretly chooses Yes or No. Then, simultaneously, players reveal their choice. Players who chose yes gain 6 extra Time Blocks this round, but destroy 6 System Health.`, flavorText: `It's easy to take risks when others are incurring the costs.`, - clientViewHandler: 'VOTE_YES_NO' as const, - duration: 1 + eventCaseClientHandler: "VOTE_YES_NO" as const, + duration: 1, }, { - id: 'sandstorm', - name: 'Sandstorm', + id: "sandstorm", + name: "Sandstorm", effect: `For the next 3 rounds, destroy an additional 10 System Health at the start of the round.`, flavorText: `Buckle in - things are about to get rough. And coarse. And irritating.`, - clientViewHandler: 'NO_CHANGE' as const, + eventCaseClientHandler: "NO_CHANGE" as const, duration: 3, timeDuration: 10, }, { - id: 'solarFlare', - name: 'Solar Flare', + id: "solarFlare", + name: "Solar Flare", effect: `Destroy 5 System Health. Skip discussion and trading phases this turn. Players cannot chat or trade Influences.`, flavorText: `Solar flares pose a far greater threat on Mars, where a thin atmosphere and non-existent magnetic field leaves settlers more vulnerable. `, - clientViewHandler: 'DISABLE_CHAT' as const, + eventCaseClientHandler: "DISABLE_CHAT" as const, duration: 1, timeDuration: 10, }, { - id: 'stymied', - name: 'Stymied', + id: "stymied", + name: "Stymied", effect: `Players may not earn their specialty Influence this round.`, flavorText: `"That's very nice that you have three PhD's. Now pick up this toothbrush and help with cleaning our solar panel cells."`, - clientViewHandler: 'NO_CHANGE' as const, + eventCaseClientHandler: "NO_CHANGE" as const, duration: 1, timeDuration: 10, }, @@ -222,38 +224,42 @@ const _marsEvents: Array = [ export function getMarsEventDeckItems(): Array { const AVAILABLE_EVENTS: Array<[string, number]> = [ - ['audit', 1], - ['bondingThroughAdversity', 1], - ['breakdownOfTrust', 1], - ['changingTides', 1], - ['compulsivePhilanthropy', 1], - ['cropFailure', 1], - ['difficultConditions', 1], - ['effortsWasted', 1], - ['heroOrPariah', 1], - ['hullBreach', 1], - ['interdisciplinary', 1], - ['lifeAsUsual', 12], - ['lostTime', 1], - ['marketsClosed', 1], - ['murphysLaw', 1], - ['outOfCommissionCurator', 1], - ['outOfCommissionEntrepreneur', 1], - ['outOfCommissionPolitician', 1], - ['outOfCommissionPioneer', 1], - ['outOfCommissionResearcher', 1], - ['personalGain', 1], - ['sandstorm', 1], - ['solarFlare', 1], - ['stymied', 1], + ["audit", 1], + ["bondingThroughAdversity", 1], + ["breakdownOfTrust", 1], + ["changingTides", 1], + ["compulsivePhilanthropy", 1], + ["cropFailure", 1], + ["difficultConditions", 1], + ["effortsWasted", 1], + ["heroOrPariah", 1], + ["hullBreach", 1], + ["interdisciplinary", 1], + ["lifeAsUsual", 12], + ["lostTime", 1], + ["marketsClosed", 1], + ["murphysLaw", 1], + ["outOfCommissionCurator", 1], + ["outOfCommissionEntrepreneur", 1], + ["outOfCommissionPolitician", 1], + ["outOfCommissionPioneer", 1], + ["outOfCommissionResearcher", 1], + ["personalGain", 1], + ["sandstorm", 1], + ["solarFlare", 1], + ["stymied", 1], ]; const availableEvents: Array = []; for (const [eventId, numberOfCopies] of AVAILABLE_EVENTS) { - const marsEventData = _.find(_marsEvents, (ev: MarsEventData) => ev.id === eventId); + const marsEventData = _.find( + _marsEvents, + (ev: MarsEventData) => ev.id === eventId + ); if (marsEventData) { - availableEvents.push(_.cloneDeep({ event: marsEventData, numberOfCopies: numberOfCopies })); - } - else { + availableEvents.push( + _.cloneDeep({ event: marsEventData, numberOfCopies: numberOfCopies }) + ); + } else { logger.warn("No event ID found in mars events: %s", eventId); } } diff --git a/server/src/rooms/game/state/bot.ts b/server/src/rooms/game/state/bot.ts index 6aa27b2e2..f093df800 100644 --- a/server/src/rooms/game/state/bot.ts +++ b/server/src/rooms/game/state/bot.ts @@ -1,6 +1,17 @@ -import {EventClientView, InvestmentData, Phase, RESOURCES, Role, ROLES} from "@port-of-mars/shared/types"; -import {Bot, GameState, Player} from "@port-of-mars/server/rooms/game/state/index"; -import {GameEvent} from "@port-of-mars/server/rooms/game/events/types"; +import { + EventCase, + InvestmentData, + Phase, + RESOURCES, + Role, + ROLES, +} from "@port-of-mars/shared/types"; +import { + Bot, + GameState, + Player, +} from "@port-of-mars/server/rooms/game/state/index"; +import { GameEvent } from "@port-of-mars/server/rooms/game/events/types"; import { AcceptedTradeRequest, RejectedTradeRequest, @@ -15,17 +26,20 @@ import { VotedForPersonalGain, VotedForPhilanthropist, VoteHeroOrPariah, - VoteHeroOrPariahRole + VoteHeroOrPariahRole, } from "@port-of-mars/server/rooms/game/events"; -import {getLogger} from "@port-of-mars/server/settings"; +import { getLogger } from "@port-of-mars/server/settings"; import _ from "lodash"; -import {downCastEventState, HeroOrPariah} from "@port-of-mars/server/rooms/game/state/marsevents/state"; -import {canPlayerMakeTrade} from "@port-of-mars/shared/validation"; +import { + downCastEventState, + HeroOrPariah, +} from "@port-of-mars/server/rooms/game/state/marsevents/state"; +import { canPlayerMakeTrade } from "@port-of-mars/shared/validation"; const logger = getLogger(__filename); function getTopAdversary(state: GameState, player: Player): Player { - let maxVictoryPoints: number = -1; + let maxVictoryPoints = -1; let adversary!: Player; for (const p of state.players) { if (p.role != player.role) { @@ -38,74 +52,99 @@ function getTopAdversary(state: GameState, player: Player): Player { return adversary; } -export type AbstractMarsEventVisitor = { [view in EventClientView]: (state: GameState, player: Player) => Array }; +export type AbstractMarsEventVisitor = { + [view in EventCase]: (state: GameState, player: Player) => Array; +}; // bot actions: voting events export class MarsEventVisitor implements AbstractMarsEventVisitor { NO_CHANGE(state: GameState, player: Player): Array { - return [new SetPlayerReadiness({value: true, role: player.role})] + return [new SetPlayerReadiness({ value: true, role: player.role })]; } AUDIT(state: GameState, player: Player): Array { - return [new SetPlayerReadiness({value: true, role: player.role})] + return [new SetPlayerReadiness({ value: true, role: player.role })]; } DISABLE_CHAT(state: GameState, player: Player): Array { - return [new SetPlayerReadiness({value: true, role: player.role})] + return [new SetPlayerReadiness({ value: true, role: player.role })]; } VOTE_YES_NO(state: GameState, player: Player): Array { - return [new VotedForPersonalGain({vote: state.systemHealth > 30, role: player.role})] + return [ + new VotedForPersonalGain({ + vote: state.systemHealth > 30, + role: player.role, + }), + ]; } - VOTE_FOR_PLAYER_SINGLE(state: GameState, player: Player): Array { + VOTE_PLAYER(state: GameState, player: Player): Array { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const defaultRole: Role = _.find(ROLES, r => r !== player.role)!; - const [role, points] = _.reduce(ROLES, ([r, p], role) => { - if (role !== player.role && player.victoryPoints > p) { - r = player.role; - p = player.victoryPoints; - } - return [r, p] - }, [defaultRole, state.players[defaultRole].victoryPoints]); + const defaultRole: Role = _.find(ROLES, (r) => r !== player.role)!; + const [role, points] = _.reduce( + ROLES, + ([r, p], role) => { + if (role !== player.role && player.victoryPoints > p) { + r = player.role; + p = player.victoryPoints; + } + return [r, p]; + }, + [defaultRole, state.players[defaultRole].victoryPoints] + ); - return [new VotedForPhilanthropist({voter: player.role, vote: role})] + return [new VotedForPhilanthropist({ voter: player.role, vote: role })]; } - VOTE_FOR_PLAYER_HERO_PARIAH(state: GameState, player: Player): Array { + VOTE_HERO_PARIAH(state: GameState, player: Player): Array { // 1. check game state: has H or P been decided? // if voting for hero or pariah, then local bot player (LBP) select another player randomly switch (state.heroOrPariah) { - // if hero -> make preference for voting for LBP + // if hero -> make preference for voting for LBP case "hero": - return [new VoteHeroOrPariahRole({role: player.role, vote: player.role})]; + return [ + new VoteHeroOrPariahRole({ role: player.role, vote: player.role }), + ]; - // if pariah -> pick someone else that's not the LBP + // if pariah -> pick someone else that's not the LBP case "pariah": - return [new VoteHeroOrPariahRole({role: player.role, vote: getTopAdversary(state, player).role})]; - - // 2. if not in hero or pariah voting phase 1, LBP submits vote for hero or pariah randomly + return [ + new VoteHeroOrPariahRole({ + role: player.role, + vote: getTopAdversary(state, player).role, + }), + ]; + + // 2. if not in hero or pariah voting phase 1, LBP submits vote for hero or pariah randomly case "": { - return downCastEventState(HeroOrPariah, state, (eventState) => { - if (!eventState.hasVoted(player.role)) { - const choice = _.random(0, 1) ? 'hero' : 'pariah'; - logger.debug('player %s choice %o', player.role, choice); - return [new VoteHeroOrPariah({role: player.role, heroOrPariah: choice})]; - } else return []; - }) ?? []; + return ( + downCastEventState(HeroOrPariah, state, (eventState) => { + if (!eventState.hasVoted(player.role)) { + const choice = _.random(0, 1) ? "hero" : "pariah"; + logger.debug("player %s choice %o", player.role, choice); + return [ + new VoteHeroOrPariah({ + role: player.role, + heroOrPariah: choice, + }), + ]; + } else return []; + }) ?? [] + ); } } } - INFLUENCES_SELECT(state: GameState, player: Player): Array { + SAVE_RESOURCE(state: GameState, player: Player): Array { const savedResources: InvestmentData = { culture: -player.inventory.culture, finance: -player.inventory.finance, government: -player.inventory.government, legacy: -player.inventory.legacy, science: -player.inventory.science, - systemHealth: 0 + systemHealth: 0, }; let unitsRemaining = 2; @@ -120,16 +159,18 @@ export class MarsEventVisitor implements AbstractMarsEventVisitor { unitsRemaining -= unitsToTake; } } - logger.info('influences draw %o', savedResources); - return [new BreakdownOfTrustOccurred({role: player.role, savedResources})] + logger.info("influences draw %o", savedResources); + return [ + new BreakdownOfTrustOccurred({ role: player.role, savedResources }), + ]; } - INFLUENCES_DRAW(state: GameState, player: Player): Array { - return [new SelectedInfluence({role: player.role, influence: 'culture'})] + DRAW_RESOURCE(state: GameState, player: Player): Array { + return [new SelectedInfluence({ role: player.role, influence: "culture" })]; } - ACCOMPLISHMENT_SELECT_PURCHASED(state: GameState, player: Player): Array { - return [new SetPlayerReadiness({value: true, role: player.role})] + DISCARD_ACCOMPLISHMENT(state: GameState, player: Player): Array { + return [new SetPlayerReadiness({ value: true, role: player.role })]; } } @@ -141,7 +182,7 @@ export class ActorRunner implements Actor { if (player.ready) { return []; } - return [new SetPlayerReadiness({value: true, role: player.role})]; + return [new SetPlayerReadiness({ value: true, role: player.role })]; } [Phase.events](state: GameState, player: Player): Array { @@ -149,7 +190,10 @@ export class ActorRunner implements Actor { return []; } const marsEvent = state.currentEvent; - return this.marsEventVisitor[marsEvent.clientViewHandler](state, player); + return this.marsEventVisitor[marsEvent.eventCaseClientHandler]( + state, + player + ); } [Phase.invest](state: GameState, player: Player): Array { @@ -160,43 +204,51 @@ export class ActorRunner implements Actor { const specialty = player.specialty; const specialtyInfluenceCost = player.costs[specialty]; // compute amounts to invest in specialty influence and system health - const specialtyInfluenceInvestment = Math.floor(tbs / specialtyInfluenceCost * 0.5); - const systemHealthCost = player.costs['systemHealth']; - const systemHealthInvestment = Math.floor((tbs - specialtyInfluenceCost * specialtyInfluenceInvestment) / systemHealthCost); + const specialtyInfluenceInvestment = Math.floor( + (tbs / specialtyInfluenceCost) * 0.5 + ); + const systemHealthCost = player.costs["systemHealth"]; + const systemHealthInvestment = Math.floor( + (tbs - specialtyInfluenceCost * specialtyInfluenceInvestment) / + systemHealthCost + ); const investment: InvestmentData = { culture: 0, finance: 0, government: 0, legacy: 0, science: 0, - systemHealth: systemHealthInvestment + systemHealth: systemHealthInvestment, }; investment[specialty] = specialtyInfluenceInvestment; return [ - new TimeInvested({investment, role: player.role}), - new SetPlayerReadiness({value: true, role: player.role}) + new TimeInvested({ investment, role: player.role }), + new SetPlayerReadiness({ value: true, role: player.role }), ]; - } [Phase.trade](state: GameState, player: Player): Array { const events: Array = []; for (const [id, trade] of state.tradeSet.entries()) { - if (trade.status === 'Active' && trade.recipient.role === player.role) { - if (canPlayerMakeTrade(trade.recipient.resourceAmount, player.inventory)) { - logger.debug("Bot can make trade for %o", trade.recipient.resourceAmount); + if (trade.status === "Active" && trade.recipient.role === player.role) { + if ( + canPlayerMakeTrade(trade.recipient.resourceAmount, player.inventory) + ) { + logger.debug( + "Bot can make trade for %o", + trade.recipient.resourceAmount + ); // FIXME: bot should reject altruistic trades - events.push(new AcceptedTradeRequest({id})); - } - else { + events.push(new AcceptedTradeRequest({ id })); + } else { logger.debug("Bot rejecting invalid trade request"); - events.push(new RejectedTradeRequest({id})) + events.push(new RejectedTradeRequest({ id })); } break; } } if (!player.ready) { - events.push(new SetPlayerReadiness({value: true, role: player.role})) + events.push(new SetPlayerReadiness({ value: true, role: player.role })); } return events; } @@ -208,13 +260,15 @@ export class ActorRunner implements Actor { if (state.systemHealth <= 35 && accomplishment.systemHealth < 0) { continue; } - return [new PurchasedAccomplishment({accomplishment, role: player.role})] + return [ + new PurchasedAccomplishment({ accomplishment, role: player.role }), + ]; } } if (!player.ready) { - return [new SetPlayerReadiness({value: true, role: player.role})]; + return [new SetPlayerReadiness({ value: true, role: player.role })]; } - return [] + return []; } [Phase.discard](state: GameState, player: Player): Array { @@ -225,19 +279,24 @@ export class ActorRunner implements Actor { const events: Array = []; for (const accomplishment of player.accomplishments.purchasable) { if (accomplishment.systemHealth <= 0) { - events.push(new DiscardedAccomplishment({id: accomplishment.id, role: player.role})) + events.push( + new DiscardedAccomplishment({ + id: accomplishment.id, + role: player.role, + }) + ); } } - events.push(new SetPlayerReadiness({value: true, role: player.role})) + events.push(new SetPlayerReadiness({ value: true, role: player.role })); return events; } [Phase.defeat](state: GameState, player: Player): Array { - return [] + return []; } [Phase.victory](state: GameState, player: Player): Array { - return [] + return []; } } @@ -247,8 +306,7 @@ export class SimpleBot implements Bot { warningTime = 240; active = false; - constructor(public actor: ActorRunner, public player: Player) { - } + constructor(public actor: ActorRunner, public player: Player) {} get shouldBeActive(): boolean { return this.elapsed >= this.maxInactivityTime; @@ -280,14 +338,15 @@ export class SimpleBot implements Bot { if (this.active) { if (this.shouldBeActive) { const events = this.actor[state.phase](state, this.player); - if (events.length > 0) logger.debug('actor events performed: %o', events); + if (events.length > 0) + logger.debug("actor events performed: %o", events); return events; } else { - return [new BotControlRelinquished({role: this.player.role})] + return [new BotControlRelinquished({ role: this.player.role })]; } } else { if (this.shouldBeActive) { - return [new BotControlTaken({role: this.player.role})] + return [new BotControlTaken({ role: this.player.role })]; } else { this.updateBotWarning(); return []; @@ -296,4 +355,6 @@ export class SimpleBot implements Bot { } } -export type Actor = { [phase in Phase]: (state: GameState, player: Player) => Array } \ No newline at end of file +export type Actor = { + [phase in Phase]: (state: GameState, player: Player) => Array; +}; diff --git a/server/src/rooms/game/state/marsevents/MarsEvent.ts b/server/src/rooms/game/state/marsevents/MarsEvent.ts index 9f9fdcdd7..07a790a9b 100644 --- a/server/src/rooms/game/state/marsevents/MarsEvent.ts +++ b/server/src/rooms/game/state/marsevents/MarsEvent.ts @@ -1,5 +1,5 @@ import { Schema, type } from "@colyseus/schema"; -import { EventClientView, MarsEventData, Phase } from "@port-of-mars/shared/types"; +import { EventCase, MarsEventData, Phase } from "@port-of-mars/shared/types"; import { GameState } from "@port-of-mars/server/rooms/game/state"; import { MarsEventState } from "./common"; import { constructState } from "./state"; @@ -11,43 +11,61 @@ export class MarsEvent extends Schema implements MarsEventData { this.name = data.name; this.effect = data.effect; this.flavorText = data.flavorText; - this.clientViewHandler = data.clientViewHandler; + this.eventCaseClientHandler = data.eventCaseClientHandler; this.duration = data.duration; this.state = constructState(data.id); - this.timeDuration = data.timeDuration ?? GameState.DEFAULT_PHASE_DURATION[Phase.events]; + this.timeDuration = + data.timeDuration ?? GameState.DEFAULT_PHASE_DURATION[Phase.events]; } - @type('string') + @type("string") id: string; - @type('string') + @type("string") name: string; - @type('string') + @type("string") effect: string; - @type('string') + @type("string") flavorText: string; - @type('string') - clientViewHandler: EventClientView; + @type("string") + eventCaseClientHandler: EventCase; - @type('number') + @type("number") elapsed = 0; - @type('number') + @type("number") duration: number; - @type('number') + @type("number") timeDuration: number; state: MarsEventState; toJSON(): MarsEventData & { elapsed: number; state: any } { - const { id, name, effect, flavorText, clientViewHandler, elapsed, duration, timeDuration } = this; + const { + id, + name, + effect, + flavorText, + eventCaseClientHandler, + elapsed, + duration, + timeDuration, + } = this; return { - id, name, effect, flavorText, clientViewHandler, elapsed, duration, state: this.state.toJSON(), timeDuration - } + id, + name, + effect, + flavorText, + eventCaseClientHandler, + elapsed, + duration, + state: this.state.toJSON(), + timeDuration, + }; } updateElapsed(): void { @@ -61,4 +79,4 @@ export class MarsEvent extends Schema implements MarsEventData { get complete(): boolean { return this.elapsed >= this.duration; } -} \ No newline at end of file +} diff --git a/shared/src/types.ts b/shared/src/types.ts index 9d293fe2a..d497586a0 100644 --- a/shared/src/types.ts +++ b/shared/src/types.ts @@ -1,27 +1,27 @@ import { Page } from "@port-of-mars/shared/routes"; -export type Dictionary = { [key: string]: T }; - -export const RESEARCHER: 'Researcher' = 'Researcher'; -export const CURATOR: 'Curator' = 'Curator'; -export const PIONEER: 'Pioneer' = 'Pioneer'; -export const ENTREPRENEUR: 'Entrepreneur' = 'Entrepreneur'; -export const POLITICIAN: 'Politician' = 'Politician'; -export const SERVER: 'Server' = 'Server'; +export type Dictionary = { [key: string]: T }; + +export const RESEARCHER: "Researcher" = "Researcher"; +export const CURATOR: "Curator" = "Curator"; +export const PIONEER: "Pioneer" = "Pioneer"; +export const ENTREPRENEUR: "Entrepreneur" = "Entrepreneur"; +export const POLITICIAN: "Politician" = "Politician"; +export const SERVER: "Server" = "Server"; export const ROLES: Array = [ CURATOR, ENTREPRENEUR, PIONEER, POLITICIAN, - RESEARCHER + RESEARCHER, ]; export type Role = - | 'Researcher' - | 'Curator' - | 'Pioneer' - | 'Entrepreneur' - | 'Politician'; -export type ServerRole = 'Server'; + | "Researcher" + | "Curator" + | "Pioneer" + | "Entrepreneur" + | "Politician"; +export type ServerRole = "Server"; export interface ChatMessageData { message: string; @@ -52,29 +52,29 @@ export type ResourceCostData = InvestmentData; export type Resource = keyof ResourceAmountData; export type Investment = keyof InvestmentData; export const INVESTMENTS: Array = [ - 'culture', - 'finance', - 'government', - 'legacy', - 'science', - 'systemHealth' + "culture", + "finance", + "government", + "legacy", + "science", + "systemHealth", ]; export const RESOURCES: Array = [ - 'culture', - 'finance', - 'government', - 'legacy', - 'science' + "culture", + "finance", + "government", + "legacy", + "science", ]; -export const INVESTMENT_LABELS: {[k in keyof InvestmentData]: string} = { - culture: 'Culture', - finance: 'Finance', - government: 'Government', - legacy: 'Legacy', - science: 'Science', - systemHealth: 'System Health' -} +export const INVESTMENT_LABELS: { [k in keyof InvestmentData]: string } = { + culture: "Culture", + finance: "Finance", + government: "Government", + legacy: "Legacy", + science: "Science", + systemHealth: "System Health", +}; export enum Phase { newRound, @@ -84,34 +84,32 @@ export enum Phase { purchase, discard, victory, - defeat + defeat, } export const PHASE_LABELS: { [k in Phase]: string } = { - [Phase.newRound]: 'New Round', - [Phase.events]: 'Events', - [Phase.invest]: 'Investment', - [Phase.trade]: 'Trade', - [Phase.purchase]: 'Purchase', - [Phase.discard]: 'Discard', - [Phase.victory]: 'Victory!', - [Phase.defeat]: 'Defeat!' + [Phase.newRound]: "New Round", + [Phase.events]: "Events", + [Phase.invest]: "Investment", + [Phase.trade]: "Trade", + [Phase.purchase]: "Purchase", + [Phase.discard]: "Discard", + [Phase.victory]: "Victory!", + [Phase.defeat]: "Defeat!", }; -export type EventClientView = -// EventNoChange (TODO) - | 'NO_CHANGE' - | 'AUDIT' - | 'DISABLE_CHAT' - // EventVote - | 'VOTE_YES_NO' - | 'VOTE_FOR_PLAYER_SINGLE' - | 'VOTE_FOR_PLAYER_HERO_PARIAH' - // EventInfluences - | 'INFLUENCES_SELECT' - | 'INFLUENCES_DRAW' - // EventAccomplishments - | 'ACCOMPLISHMENT_SELECT_PURCHASED'; +export type EventCase = + // Basic + | "NO_CHANGE" + | "AUDIT" + | "DISABLE_CHAT" + // Interaction + | "VOTE_YES_NO" + | "VOTE_PLAYER" + | "VOTE_HERO_PARIAH" + | "SAVE_RESOURCE" + | "DRAW_RESOURCE" + | "DISCARD_ACCOMPLISHMENT"; export interface AccomplishmentPurchaseData { name: string; @@ -125,10 +123,10 @@ export interface SystemHealthMarsEventData { } export interface RoundIntroductionData< - SystemHealth=SystemHealthMarsEventData, - AccomplishmentPurchase=AccomplishmentPurchaseData, - Trade=TradeData> -{ + SystemHealth = SystemHealthMarsEventData, + AccomplishmentPurchase = AccomplishmentPurchaseData, + Trade = TradeData +> { systemHealthGroupContributions: Map; systemHealthAtStartOfRound: number; systemHealthMaintenanceCost: number; @@ -142,7 +140,7 @@ export interface MarsEventData { name: string; effect: string; flavorText: string; - clientViewHandler: EventClientView; + eventCaseClientHandler: EventCase; duration: number; // real time duration of the round in seconds (overrides the default phase length) timeDuration?: number; @@ -161,7 +159,7 @@ export enum MarsLogCategory { invalidTrade = "INVALID TRADE", acceptTrade = "ACCEPT TRADE", rejectTrade = "REJECT TRADE", - cancelTrade = "CANCELLED TRADE" + cancelTrade = "CANCELLED TRADE", } export interface MarsLogData { @@ -193,7 +191,7 @@ export interface AccomplishmentData { effect: string; } -export interface AccomplishmentSetData { +export interface AccomplishmentSetData { purchased: Array; purchasable: Array; } @@ -203,44 +201,46 @@ export interface TradeAmountData { resourceAmount: ResourceAmountData; } +export type TradeStatus = "Active" | "Accepted" | "Rejected" | "Cancelled"; -export type TradeStatus = 'Active' | 'Accepted' | 'Rejected' | 'Cancelled'; - -export interface TradeData { +export interface TradeData { id: string; sender: TradeAmount; recipient: TradeAmount; status: TradeStatus; } -export type NullPartner = ''; +export type NullPartner = ""; export interface TradeAmountDataWithNull { role: R; resourceAmount: ResourceAmountData; } -export interface TradeDataWithNull{ +export interface TradeDataWithNull { sender: TradeAmountDataWithNull; recipient: TradeAmountDataWithNull; } -export type TradeSetData = Record; +export type TradeSetData = Record; export interface PurchasedSystemHealthData { description: string; systemHealth: number; } -export interface SystemHealthChangesData { +export interface SystemHealthChangesData< + PurchasedSystemHealth = PurchasedSystemHealthData +> { investment: number; - purchases: Array + purchases: Array; } export interface PlayerData< - AccomplishmentSet=AccomplishmentSetData, - ResourceAmount=ResourceAmountData, - SystemHealthChanges=SystemHealthChangesData> { + AccomplishmentSet = AccomplishmentSetData, + ResourceAmount = ResourceAmountData, + SystemHealthChanges = SystemHealthChangesData +> { role: Role; costs: ResourceCostData; botWarning: boolean; @@ -253,15 +253,16 @@ export interface PlayerData< inventory: ResourceAmount; } -export type PlayerSetData = { [role in Role]: Player }; +export type PlayerSetData = { [role in Role]: Player }; export interface GameData< - ChatMessage=ChatMessageData, - MarsEvent=MarsEventData, - MarsLogMessage=MarsLogMessageData, - PlayerSet=PlayerSetData, - RoundIntroduction=RoundIntroductionData, - TradeSet=TradeSetData> { + ChatMessage = ChatMessageData, + MarsEvent = MarsEventData, + MarsLogMessage = MarsLogMessageData, + PlayerSet = PlayerSetData, + RoundIntroduction = RoundIntroductionData, + TradeSet = TradeSetData +> { players: PlayerSet; timeRemaining: number; round: number; @@ -274,7 +275,7 @@ export interface GameData< roundIntroduction: RoundIntroduction; tradeSet: TradeSet; winners: Array; - heroOrPariah: '' | 'hero' | 'pariah'; + heroOrPariah: "" | "hero" | "pariah"; } export interface QuizData { @@ -291,24 +292,33 @@ export interface QuizQuestionData { } export interface ActionItem { - done: boolean - description: string - redoable: boolean - link: { kind: 'internal', data: { name: Page, params?: Dictionary } } | { kind: 'external', data: string } + done: boolean; + description: string; + redoable: boolean; + link: + | { kind: "internal"; data: { name: Page; params?: Dictionary } } + | { kind: "external"; data: string }; } export interface GameMetadata { - time: number // unix timestamp - round: number - tournamentName: string + time: number; // unix timestamp + round: number; + tournamentName: string; } -export type PlayerScores = Array<{ role: Role, points: number, winner: boolean }> +export type PlayerScores = Array<{ + role: Role; + points: number; + winner: boolean; +}>; -export type PlayerStatItem = GameMetadata & {playerScores: PlayerScores, victory: boolean} +export type PlayerStatItem = GameMetadata & { + playerScores: PlayerScores; + victory: boolean; +}; export interface Stats { - games: Array + games: Array; } export interface PlayerTaskCompletion { @@ -342,8 +352,8 @@ export interface DashboardData { } export interface DashboardMessage { - kind: 'success' | 'danger' | 'info' | 'warning' - message: string + kind: "success" | "danger" | "info" | "warning"; + message: string; } export type RoomId = string;