diff --git a/src/CotMG/DummyGift.ts b/src/CotMG/DummyGift.ts new file mode 100644 index 000000000..79b541e72 --- /dev/null +++ b/src/CotMG/DummyGift.ts @@ -0,0 +1,67 @@ +import { ActiveFragment } from "./ActiveFragment"; +import { IStaneksGift } from "./IStaneksGift"; + +export class DummyGift implements IStaneksGift { + storedCycles = 0; + fragments: ActiveFragment[] = []; + _width: number; + _height: number; + + constructor(width: number, height: number, fragments: ActiveFragment[]) { + this.fragments = fragments; + this._width = width; + this._height = height; + } + + width(): number { + return this._width; + } + height(): number { + return this._height; + } + charge(): any { + throw new Error("unimplemented for dummy gift"); + } + process(): any { + throw new Error("unimplemented for dummy gift"); + } + effect(): any { + throw new Error("unimplemented for dummy gift"); + } + canPlace(): any { + throw new Error("unimplemented for dummy gift"); + } + place(): any { + throw new Error("unimplemented for dummy gift"); + } + findFragment(): any { + throw new Error("unimplemented for dummy gift"); + } + fragmentAt(worldX: number, worldY: number): ActiveFragment | undefined { + for (const aFrag of this.fragments) { + if (aFrag.fullAt(worldX, worldY)) { + return aFrag; + } + } + + return undefined; + } + delete(): any { + throw new Error("unimplemented for dummy gift"); + } + clear(): any { + throw new Error("unimplemented for dummy gift"); + } + count(): any { + throw new Error("unimplemented for dummy gift"); + } + inBonus(): any { + throw new Error("unimplemented for dummy gift"); + } + prestigeAugmentation(): any { + throw new Error("unimplemented for dummy gift"); + } + prestigeSourceFile(): any { + throw new Error("unimplemented for dummy gift"); + } +} diff --git a/src/CotMG/Helper.tsx b/src/CotMG/Helper.tsx index c8f07c388..26e686853 100644 --- a/src/CotMG/Helper.tsx +++ b/src/CotMG/Helper.tsx @@ -12,3 +12,26 @@ export function loadStaneksGift(saveString: string): void { staneksGift = new StaneksGift(); } } + +export function zeros(dimensions: number[]): any { + const array = []; + + for (let i = 0; i < dimensions[0]; ++i) { + array.push(dimensions.length == 1 ? 0 : zeros(dimensions.slice(1))); + } + + return array; +} + +export function calculateGrid(gift: IStaneksGift): number[][] { + const newgrid = zeros([gift.width(), gift.height()]) as unknown as number[][]; + for (let i = 0; i < gift.width(); i++) { + for (let j = 0; j < gift.height(); j++) { + const fragment = gift.fragmentAt(i, j); + if (!fragment) continue; + newgrid[i][j] = 1; + } + } + + return newgrid; +} diff --git a/src/CotMG/ui/DummyGrid.tsx b/src/CotMG/ui/DummyGrid.tsx new file mode 100644 index 000000000..a31646cc6 --- /dev/null +++ b/src/CotMG/ui/DummyGrid.tsx @@ -0,0 +1,33 @@ +import { Box, Table } from "@mui/material"; +import * as React from "react"; +import { ActiveFragment } from "../ActiveFragment"; +import { DummyGift } from "../DummyGift"; +import { Grid } from "./Grid"; +import { calculateGrid, zeros } from "../Helper"; + +interface IProps { + width: number; + height: number; + fragments: ActiveFragment[]; +} + +export function DummyGrid(props: IProps): React.ReactElement { + const gift = new DummyGift(props.width, props.height, props.fragments); + const activeGrid = calculateGrid(gift); + const ghostGrid = zeros([props.width, props.height]); + return ( + + + undefined} + click={() => undefined} + /> +
+
+ ); +} diff --git a/src/CotMG/ui/Grid.tsx b/src/CotMG/ui/Grid.tsx new file mode 100644 index 000000000..479681bc4 --- /dev/null +++ b/src/CotMG/ui/Grid.tsx @@ -0,0 +1,60 @@ +import { Table, TableBody, TableRow } from "@mui/material"; +import * as React from "react"; +import { ActiveFragment } from "../ActiveFragment"; +import { IStaneksGift } from "../IStaneksGift"; +import { Cell } from "./Cell"; + +interface IProps { + width: number; + height: number; + activeGrid: number[][]; + ghostGrid: number[][]; + gift: IStaneksGift; + enter(i: number, j: number): void; + click(i: number, j: number): void; +} + +function randomColor(fragment: ActiveFragment): string { + // Can't set Math.random seed so copy casino. TODO refactor both RNG later. + let s1 = Math.pow((fragment.x + 1) * (fragment.y + 1), 10); + let s2 = s1; + let s3 = s1; + + const colors = []; + for (let i = 0; i < 3; i++) { + s1 = (171 * s1) % 30269; + s2 = (172 * s2) % 30307; + s3 = (170 * s3) % 30323; + colors.push((s1 / 30269.0 + s2 / 30307.0 + s3 / 30323.0) % 1.0); + } + + return `rgb(${colors[0] * 256}, ${colors[1] * 256}, ${colors[2] * 256})`; +} + +export function Grid(props: IProps): React.ReactElement { + function color(worldX: number, worldY: number): string { + if (props.ghostGrid[worldX][worldY] && props.activeGrid[worldX][worldY]) return "red"; + if (props.ghostGrid[worldX][worldY]) return "white"; + + if (props.activeGrid[worldX][worldY]) { + const fragment = props.gift.fragmentAt(worldX, worldY); + if (!fragment) throw new Error("ActiveFragment should not be null"); + return randomColor(fragment); + } + return ""; + } + + // switch the width/length to make axis consistent. + const elems = []; + for (let j = 0; j < props.height; j++) { + const cells = []; + for (let i = 0; i < props.width; i++) { + cells.push( + props.enter(i, j)} onClick={() => props.click(i, j)} color={color(i, j)} />, + ); + } + elems.push({cells}); + } + + return {elems}; +} diff --git a/src/CotMG/ui/MainBoard.tsx b/src/CotMG/ui/MainBoard.tsx index 3bbcfdfe4..873592b76 100644 --- a/src/CotMG/ui/MainBoard.tsx +++ b/src/CotMG/ui/MainBoard.tsx @@ -1,62 +1,20 @@ import * as React from "react"; import { Fragment, NoneFragment } from "../Fragment"; -import { ActiveFragment } from "../ActiveFragment"; import { FragmentType } from "../FragmentType"; import { IStaneksGift } from "../IStaneksGift"; -import { Cell } from "./Cell"; import { FragmentInspector } from "./FragmentInspector"; import { FragmentSelector } from "./FragmentSelector"; import Box from "@mui/material/Box"; import Button from "@mui/material/Button"; -import TableRow from "@mui/material/TableRow"; -import TableBody from "@mui/material/TableBody"; import { Table } from "../../ui/React/Table"; - -function zeros(dimensions: number[]): any { - const array = []; - - for (let i = 0; i < dimensions[0]; ++i) { - array.push(dimensions.length == 1 ? 0 : zeros(dimensions.slice(1))); - } - - return array; -} - -function randomColor(fragment: ActiveFragment): string { - // Can't set Math.random seed so copy casino. TODO refactor both RNG later. - let s1 = Math.pow((fragment.x + 1) * (fragment.y + 1), 10); - let s2 = s1; - let s3 = s1; - - const colors = []; - for (let i = 0; i < 3; i++) { - s1 = (171 * s1) % 30269; - s2 = (172 * s2) % 30307; - s3 = (170 * s3) % 30323; - colors.push((s1 / 30269.0 + s2 / 30307.0 + s3 / 30323.0) % 1.0); - } - - return `rgb(${colors[0] * 256}, ${colors[1] * 256}, ${colors[2] * 256})`; -} +import { Grid } from "./Grid"; +import { zeros, calculateGrid } from "../Helper"; interface IProps { gift: IStaneksGift; } export function MainBoard(props: IProps): React.ReactElement { - function calculateGrid(gift: IStaneksGift): any { - const newgrid = zeros([gift.width(), gift.height()]); - for (let i = 0; i < gift.width(); i++) { - for (let j = 0; j < gift.height(); j++) { - const fragment = gift.fragmentAt(i, j); - if (!fragment) continue; - newgrid[i][j] = 1; - } - } - - return newgrid; - } - const [grid, setGrid] = React.useState(calculateGrid(props.gift)); const [ghostGrid, setGhostGrid] = React.useState(zeros([props.gift.width(), props.gift.height()])); const [pos, setPos] = React.useState([0, 0]); @@ -96,44 +54,11 @@ export function MainBoard(props: IProps): React.ReactElement { setGrid(calculateGrid(props.gift)); } - function color(worldX: number, worldY: number): string { - if (ghostGrid[worldX][worldY] && grid[worldX][worldY]) return "red"; - if (ghostGrid[worldX][worldY]) return "white"; - - if (grid[worldX][worldY]) { - const fragment = props.gift.fragmentAt(worldX, worldY); - if (!fragment) throw new Error("ActiveFragment should not be null"); - return randomColor(fragment); - } - return ""; - } - function clear(): void { props.gift.clear(); setGrid(zeros([props.gift.width(), props.gift.height()])); } - // switch the width/length to make axis consistent. - const elems = []; - for (let j = 0; j < props.gift.height(); j++) { - const cells = []; - for (let i = 0; i < props.gift.width(); i++) { - cells.push( - moveGhost(i, j, rotation)} - onClick={() => clickAt(i, j)} - color={color(i, j)} - />, - ); - } - elems.push( - - {cells} - , - ); - } - function updateSelectedFragment(fragment: Fragment): void { setSelectedFragment(fragment); const newgrid = zeros([props.gift.width(), props.gift.height()]); @@ -162,7 +87,15 @@ export function MainBoard(props: IProps): React.ReactElement { - {elems} + moveGhost(i, j, rotation)} + click={(i, j) => clickAt(i, j)} + />
diff --git a/src/CotMG/ui/StaneksGiftRoot.tsx b/src/CotMG/ui/StaneksGiftRoot.tsx index a7a3b8bf2..72cc3b050 100644 --- a/src/CotMG/ui/StaneksGiftRoot.tsx +++ b/src/CotMG/ui/StaneksGiftRoot.tsx @@ -7,6 +7,13 @@ import { IStaneksGift } from "../IStaneksGift"; import { Info } from "@mui/icons-material"; import { dialogBoxCreate } from "../../ui/React/DialogBox"; import Typography from "@mui/material/Typography"; +import Box from "@mui/material/Box"; +import { Table } from "@mui/material"; +import { Grid } from "./Grid"; +import { DummyGift } from "../DummyGift"; +import { ActiveFragment } from "../ActiveFragment"; +import { Fragments } from "../Fragment"; +import { DummyGrid } from "./DummyGrid"; type IProps = { staneksGift: IStaneksGift; @@ -64,13 +71,99 @@ export function StaneksGiftRoot({ staneksGift }: IProps): React.ReactElement { charged. - {/* - TODO: - Board showing a booster fragment touching a single stat fragment on one edge, labeled as providing a 10% bonus to the stat fragment. - Board showing a booster fragment touching a single stat fragment on many edges, labeled as providing a 10% bonus to the stat fragment. - Board showing a booster fragment diagonal from a single stat fragment along multiple corners, labeled as providing no bonus to the stat fragment. - Board showing a booster fragment touching multiple stat fragments, labeled as providing a 10% bonus to each stat fragment. - */} + f.id === 5) ?? Fragments[0], + }), + new ActiveFragment({ + x: 0, + y: 2, + rotation: 0, + fragment: Fragments.find((f) => f.id === 101) ?? Fragments[0], + }), + ]} + /> + + This boost provides a bonus to the touching fragment + + + f.id === 100) ?? Fragments[0], + }), + new ActiveFragment({ + x: 0, + y: 0, + rotation: 2, + fragment: Fragments.find((f) => f.id === 1) ?? Fragments[0], + }), + ]} + /> + + Even though the booster touches many tiles, the bonus is only applied once. + + + f.id === 5) ?? Fragments[0], + }), + new ActiveFragment({ + x: 2, + y: 0, + rotation: 0, + fragment: Fragments.find((f) => f.id === 105) ?? Fragments[0], + }), + ]} + /> + + Even though the booster touches many tiles, the bonus is only applied once. + + + f.id === 27) ?? Fragments[0], + }), + new ActiveFragment({ + x: 0, + y: 1, + rotation: 2, + fragment: Fragments.find((f) => f.id === 100) ?? Fragments[0], + }), + new ActiveFragment({ + x: 2, + y: 0, + rotation: 1, + fragment: Fragments.find((f) => f.id === 30) ?? Fragments[0], + }), + ]} + /> + + This booster provides bonus to all fragment it touches. +