diff --git a/src/api/BoardScoreTable/BoardScoreTable.tsx b/src/api/BoardScoreTable/BoardScoreTable.tsx index d03eca2..bf2aba4 100644 --- a/src/api/BoardScoreTable/BoardScoreTable.tsx +++ b/src/api/BoardScoreTable/BoardScoreTable.tsx @@ -3,6 +3,7 @@ import { useEffect, useState } from 'react'; import './BoardScoreTable.scss'; import GameStorage from '../utils/GameStorage'; import { getFunctionForWinMode, WinMode } from '../types/WinMode'; +import ExtensionButtons from '../ExtensionButtons/ExtensionButtons'; interface BoardScoreTableProps { definition: any; @@ -24,9 +25,8 @@ function BoardScoreTable({ onCellChange, }: BoardScoreTableProps) { const playerSizes = Array.from(Array(playerSize).keys()); - const rows = definition.rows || []; + const [rows, setRows] = useState(definition.rows || []); const tableStyle = parseTableStyle(definition); - const [tableMatrix, setTableMatrix] = useState( GameStorage.getGameMatrix( definition.title, @@ -106,87 +106,108 @@ function BoardScoreTable({ }; return ( - - - - - {playerSizes.map((index) => ( - - ))} - - - - {rows.map( - (row: any, index: number) => - (rounds === -1 || index < rounds) && ( - <> - {gameSettings.showHelp && row.icon && ( + <> + { + const extRows = extensionDefinition.rows || []; + extRows.forEach((row: any) => { + row.__extName = extensionName; + }); + setRows([...rows, ...extRows]); + }} + onExtensionOff={(extensionName) => { + setRows( + rows.filter( + (row: any) => row.__extName !== extensionName + ) + ); + }} + /> +
- { - const newPlayerNames = playerNames.slice(); - newPlayerNames[index] = - e.target.value.toUpperCase(); - setPlayerNames(newPlayerNames); - GameStorage.setPlayerNames( - definition.title, - newPlayerNames - ); - }} - /> -
+ + + + {playerSizes.map((index) => ( + + ))} + + + + {rows.map( + (row: any, index: number) => + (rounds === -1 || index < rounds) && ( + <> + {gameSettings.showHelp && row.icon && ( + + + + )} - + key={index} + style={getStyleFromRow( + row, + definition, + index + )}> + + {playerSizes.map((playerIndex) => ( + + ))} - )} - - - {playerSizes.map((playerIndex) => ( - - ))} - - - ) - )} - - - {totalRow.map((value, playerIndex) => ( - - ))} - - -
+ { + const newPlayerNames = + playerNames.slice(); + newPlayerNames[index] = + e.target.value.toUpperCase(); + setPlayerNames(newPlayerNames); + GameStorage.setPlayerNames( + definition.title, + newPlayerNames + ); + }} + /> +
+ {row.name} + + {row.description + ? ' - ' + + row.description + : ''} + +
- {row.name} - - {row.description - ? ' - ' + row.description - : ''} - -
- {isNaN(value) ? 0 : value} -
+ + ) + )} + + + {totalRow.map((value, playerIndex) => ( + + {isNaN(value) ? 0 : value} + + ))} + + + + ); } export default BoardScoreTable; diff --git a/src/api/ExtensionButtons/ExtensionButtons.scss b/src/api/ExtensionButtons/ExtensionButtons.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/api/ExtensionButtons/ExtensionButtons.tsx b/src/api/ExtensionButtons/ExtensionButtons.tsx new file mode 100644 index 0000000..c5e7338 --- /dev/null +++ b/src/api/ExtensionButtons/ExtensionButtons.tsx @@ -0,0 +1,78 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { useEffect, useState } from 'react'; +import './ExtensionButtons.scss'; +import GameStorage from '../utils/GameStorage'; + +interface ExtensionButtonsProps { + definition: any; + onExtensionOn?: (extensionName: string, extensionDefinition: any) => void; + onExtensionOff?: (extensionName: string) => void; +} + +/** + * This is a ExtensionButtons component + * @author cophilot + * @version 1.0.0 + * @created 2024-7-21 + */ +function ExtensionButtons({ + definition, + onExtensionOn, + onExtensionOff, +}: ExtensionButtonsProps) { + const extensionsDefinition = definition.extensions || {}; + const extensionsNames = Object.keys(extensionsDefinition); + + const [selectedExtensions, setSelectedExtensions] = useState( + GameStorage.getSelectedExtension(definition.title, []) + ); + + const handleExtensionClick = (extensionName: string) => { + let newSelectedExtensions = [...selectedExtensions]; + if (selectedExtensions.includes(extensionName)) { + newSelectedExtensions = selectedExtensions.filter( + (name) => name !== extensionName + ); + onExtensionOff?.(extensionName); + } else { + newSelectedExtensions.push(extensionName); + onExtensionOn?.(extensionName, extensionsDefinition[extensionName]); + } + + GameStorage.setSelectedExtension( + definition.title, + newSelectedExtensions + ); + setSelectedExtensions(newSelectedExtensions); + }; + + useEffect(() => { + GameStorage.getSelectedExtension(definition.title, []).forEach( + (extensionName: string) => { + onExtensionOn?.( + extensionName, + extensionsDefinition[extensionName] + ); + } + ); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + return ( +
+ {extensionsNames.length > 0 &&

Extensions

} + {extensionsNames.map((name) => ( + + ))} +
+ ); +} +export default ExtensionButtons; diff --git a/src/api/utils/GameStorage.ts b/src/api/utils/GameStorage.ts index eda43fa..8a9931e 100644 --- a/src/api/utils/GameStorage.ts +++ b/src/api/utils/GameStorage.ts @@ -52,6 +52,26 @@ export default class GameStorage { ); } + static getSelectedExtension(gameTitle: string, fallback: string[] = []) { + const selectedExtension = localStorage.getItem( + GameStorage.getStorageKeyFromTitle(gameTitle, 'selected-extension') + ); + if (selectedExtension === null) { + return fallback; + } + return JSON.parse(selectedExtension); + } + + static setSelectedExtension( + gameTitle: string, + selectedExtension: string[] + ) { + localStorage.setItem( + GameStorage.getStorageKeyFromTitle(gameTitle, 'selected-extension'), + JSON.stringify(selectedExtension) + ); + } + static getGameMatrix(gameTitle: string, fallback: number[][] = []) { const matrix = localStorage.getItem( GameStorage.getStorageKeyFromTitle(gameTitle, 'matrix') diff --git a/src/games/wingspan/assets/bird.png b/src/games/wingspan/assets/bird.png index 24fa584..89f613e 100644 Binary files a/src/games/wingspan/assets/bird.png and b/src/games/wingspan/assets/bird.png differ diff --git a/src/games/wingspan/assets/bonus.png b/src/games/wingspan/assets/bonus.png index e422353..0c911c7 100644 Binary files a/src/games/wingspan/assets/bonus.png and b/src/games/wingspan/assets/bonus.png differ diff --git a/src/games/wingspan/assets/eggs.png b/src/games/wingspan/assets/eggs.png index acc5d54..efd44cd 100644 Binary files a/src/games/wingspan/assets/eggs.png and b/src/games/wingspan/assets/eggs.png differ diff --git a/src/games/wingspan/assets/nectar.png b/src/games/wingspan/assets/nectar.png new file mode 100644 index 0000000..f0d3c22 Binary files /dev/null and b/src/games/wingspan/assets/nectar.png differ diff --git a/src/games/wingspan/assets/resources.png b/src/games/wingspan/assets/resources.png index db9061a..79e2713 100644 Binary files a/src/games/wingspan/assets/resources.png and b/src/games/wingspan/assets/resources.png differ diff --git a/src/games/wingspan/assets/round-goals.png b/src/games/wingspan/assets/round-goals.png index f93af06..af5dc82 100644 Binary files a/src/games/wingspan/assets/round-goals.png and b/src/games/wingspan/assets/round-goals.png differ diff --git a/src/games/wingspan/assets/tucked-card.png b/src/games/wingspan/assets/tucked-card.png index 62c50a6..af08e56 100644 Binary files a/src/games/wingspan/assets/tucked-card.png and b/src/games/wingspan/assets/tucked-card.png differ diff --git a/src/games/wingspan/definition.ts b/src/games/wingspan/definition.ts index 727b97d..0dc7678 100644 --- a/src/games/wingspan/definition.ts +++ b/src/games/wingspan/definition.ts @@ -6,6 +6,7 @@ import roundGoalIcon from './assets/round-goals.png'; import bonusIcon from './assets/bonus.png'; import tuckedCardIcon from './assets/tucked-card.png'; import birdIcon from './assets/bird.png'; +import nectarIcon from './assets/nectar.png'; export default function getDefinition() { return { @@ -20,28 +21,46 @@ export default function getDefinition() { rows: [ { name: 'Birds', + description: 'Birds in the players collection', icon: birdIcon, }, { name: 'Bonus cards', + description: 'Bonus cards in the players collection', icon: bonusIcon, }, { name: 'End-of-round goals', + icon: roundGoalIcon, }, { name: 'Eggs on cards', + description: '1 point per egg on cards', icon: eggIcon, }, { name: 'Food on cards', + description: '1 point per food token on cards', icon: resourceIcon, }, { name: 'Tucked cards', + description: '1 point per card tucked under another card', icon: tuckedCardIcon, }, ], + extensions: { + Oceania: { + rows: [ + { + name: 'Nectar', + description: + '5 points for the player with the most nectar and 2 points for the player with the second most nectar per region', + icon: nectarIcon, + }, + ], + }, + }, }; }