diff --git a/src/board/Buttons.tsx b/src/board/Buttons.tsx index b300c4d..cc0dffe 100644 --- a/src/board/Buttons.tsx +++ b/src/board/Buttons.tsx @@ -20,11 +20,8 @@ export default class Buttons extends React.Component, obj const canAddTwo = isActive && Game.canAddTwo(G, ctx); const canEndTurn = isActive && Game.canEndTurn(G); - // if not null, then the player can activate the Renovation Company on this establishment, effectively "skipping" it - const skipRenovationCompanyEst = Game.canSkipRenovationCompany(G); - const canSkipOffice = isActive && Game.canSkipOffice(G); - const canSkipRenovationCompany = isActive && skipRenovationCompanyEst !== null; + const canSkipRenovationCompany = isActive && Game.canSkipRenovationCompany(G); const canSkipExhibitHall = isActive && Game.canSkipExhibitHall(G); const skipButtonActive = canSkipOffice || canSkipRenovationCompany || canSkipExhibitHall; @@ -32,7 +29,7 @@ export default class Buttons extends React.Component, obj if (canSkipOffice) { onClickSkipEvent = () => moves.skipOffice(); } else if (canSkipRenovationCompany) { - onClickSkipEvent = () => moves.doRenovationCompany(skipRenovationCompanyEst); + onClickSkipEvent = () => moves.skipRenovationCompany(); } else if (canSkipExhibitHall) { onClickSkipEvent = () => moves.skipExhibitHall(); } else { diff --git a/src/game/establishments/main.ts b/src/game/establishments/main.ts index c298e49..c07ede0 100644 --- a/src/game/establishments/main.ts +++ b/src/game/establishments/main.ts @@ -220,8 +220,9 @@ export const setRenovationCount = (G: MachikoroG, player: number, est: Establish /** * @param G - * @returns An unowned red, blue, or green establishment, or null if none - * exists. This is useful for "skipping" the Renovation Company action. + * @returns A non-Major establishment that is in use but no player currently + * owns, or null if none exists. This is useful for "skipping" the Renovation + * Company action. */ export const unownedRedBlueGreenEst = (G: MachikoroG): Establishment | null => { const ests = getAllInUse(G.version, G.expansions).filter((est) => est.color !== EstColor.Purple); diff --git a/src/game/log/parsers.ts b/src/game/log/parsers.ts index 0a8ae26..010c2a6 100644 --- a/src/game/log/parsers.ts +++ b/src/game/log/parsers.ts @@ -2,7 +2,6 @@ // Parsing log entries. // -import { assertUnreachable } from 'common/typescript'; import { coinPlural } from '../display'; import type { MachikoroG } from '../types'; @@ -29,6 +28,7 @@ const LogEventType = { Office: 'Office', DemolitionCompany: 'DemolitionCompany', MovingCompany: 'MovingCompany', + RenovationCompany: 'RenovationCompany', Park: 'Park', ExhibitHall: 'ExhibitHall', TechStartup: 'TechStartup', @@ -52,6 +52,7 @@ export type LogEvent = | Office | DemolitionCompany | MovingCompany + | RenovationCompany | Park | ExhibitHall | TechStartup @@ -65,38 +66,39 @@ export type LogEvent = * @returns String to display for the log event. */ export const parseLogEvent = (logEvent: LogEvent, names: string[]): string => { - if (logEvent.type === LogEventType.RollOne) { - return parseRollOne(logEvent); - } else if (logEvent.type === LogEventType.RollTwo) { - return parseRollTwo(logEvent); - } else if (logEvent.type === LogEventType.AddTwo) { - return parseAddTwo(logEvent); - } else if (logEvent.type === LogEventType.Earn) { - return parseEarn(logEvent, names); - } else if (logEvent.type === LogEventType.Take) { - return parseTake(logEvent, names); - } else if (logEvent.type === LogEventType.Buy) { - return parseBuy(logEvent); - } else if (logEvent.type === LogEventType.Office) { - return parseOffice(logEvent, names); - } else if (logEvent.type === LogEventType.DemolitionCompany) { - return parseDemolitionCompany(logEvent); - } else if (logEvent.type === LogEventType.MovingCompany) { - return parseMovingCompany(logEvent, names); - } else if (logEvent.type === LogEventType.Park) { - return parsePark(logEvent); - } else if (logEvent.type === LogEventType.ExhibitHall) { - return parseExhibitHall(logEvent); - } else if (logEvent.type === LogEventType.TechStartup) { - return parseInvestTechStartup(logEvent); - } else if (logEvent.type === LogEventType.TunaRoll) { - return parseTunaRoll(logEvent); - } else if (logEvent.type === LogEventType.EndGame) { - return parseEndGame(logEvent, names); - } else if (logEvent.type === LogEventType.OtherEvent) { - return parseOtherEvent(logEvent); - } else { - return assertUnreachable(logEvent); + switch (logEvent.type) { + case LogEventType.RollOne: + return parseRollOne(logEvent); + case LogEventType.RollTwo: + return parseRollTwo(logEvent); + case LogEventType.AddTwo: + return parseAddTwo(logEvent); + case LogEventType.Earn: + return parseEarn(logEvent, names); + case LogEventType.Take: + return parseTake(logEvent, names); + case LogEventType.Buy: + return parseBuy(logEvent); + case LogEventType.Office: + return parseOffice(logEvent, names); + case LogEventType.DemolitionCompany: + return parseDemolitionCompany(logEvent); + case LogEventType.MovingCompany: + return parseMovingCompany(logEvent, names); + case LogEventType.RenovationCompany: + return parseRenovationCompany(logEvent); + case LogEventType.Park: + return parsePark(logEvent); + case LogEventType.ExhibitHall: + return parseExhibitHall(logEvent); + case LogEventType.TechStartup: + return parseInvestTechStartup(logEvent); + case LogEventType.TunaRoll: + return parseTunaRoll(logEvent); + case LogEventType.EndGame: + return parseEndGame(logEvent, names); + case LogEventType.OtherEvent: + return parseOtherEvent(logEvent); } }; @@ -365,6 +367,31 @@ const parseMovingCompany = (logEvent: MovingCompany, names: string[]): string => // ---------------------------------------------------------------------------- +interface RenovationCompany extends BaseLogEvent { + type: typeof LogEventType.RenovationCompany; + estName: string; +} + +/** + * Log the effect of the Renovation Company establishment. + * @param G + * @param estName + */ +export const logRenovationCompany = (G: MachikoroG, estName: string): void => { + const logEvent: RenovationCompany = { type: LogEventType.RenovationCompany, estName }; + G._logBuffer.push(logEvent); +}; + +/** + * @param logEvent + * @returns Displayed log text for the Renovation Company establishment. + */ +const parseRenovationCompany = (logEvent: RenovationCompany): string => { + return `\trenovated ${logEvent.estName}`; +}; + +// ---------------------------------------------------------------------------- + interface Park extends BaseLogEvent { type: typeof LogEventType.Park; coins: number; diff --git a/src/game/machikoro.ts b/src/game/machikoro.ts index ef290b5..feb8836 100644 --- a/src/game/machikoro.ts +++ b/src/game/machikoro.ts @@ -275,15 +275,15 @@ export const canDoRenovationCompany = (G: MachikoroG, est: Establishment): boole /** * @param G - * @returns An establishment that the current player can activate with the - * Renovation Company action to effectively "skip" it, or null if such a move - * is not allowed / possible. + * @returns True if the current player can skip the Renovation Company action + * by activating it on an unowned establishment. */ -export const canSkipRenovationCompany = (G: MachikoroG): Establishment | null => { - if (G.turnState !== TurnState.RenovationCompany) { - return null; - } - return Est.unownedRedBlueGreenEst(G); +export const canSkipRenovationCompany = (G: MachikoroG): boolean => { + return ( + G.turnState === TurnState.RenovationCompany && + // an unknown establishment exists + Est.unownedRedBlueGreenEst(G) !== null + ); }; /** @@ -699,6 +699,7 @@ const doRenovationCompany: Move = (context, est: Establishment) => { // close own establishments const playerCount = Est.countOwned(G, player, est); Est.setRenovationCount(G, player, est, playerCount); + Log.logRenovationCompany(G, est.name); // get coins from opponents and close their establishments for (const opponent of getPreviousPlayers(ctx)) { @@ -716,6 +717,23 @@ const doRenovationCompany: Move = (context, est: Establishment) => { return; }; +/** + * Skip the Renovation Company action by picking an unowned establishment to + * renovate. + * @param context + * @returns + */ +const skipRenovationCompany: Move = (context) => { + const { G } = context; + if (!canSkipRenovationCompany(G)) { + return INVALID_MOVE; + } + + switchState(context); + + return; +}; + /** * Perform the Exhibit Hall (Machi Koro 1) action by picking an establishment * to activate. @@ -1673,6 +1691,7 @@ export const Machikoro: Game, SetupData> = { doDemolitionCompany, doMovingCompanyOpp, doRenovationCompany, + skipRenovationCompany, doExhibitHall, skipExhibitHall, investTechStartup,