diff --git a/src/CotMG/ActiveFragment.ts b/src/CotMG/ActiveFragment.ts index 58bea71f3..68ec5f2bb 100644 --- a/src/CotMG/ActiveFragment.ts +++ b/src/CotMG/ActiveFragment.ts @@ -7,12 +7,14 @@ const noCharge = [FragmentType.None, FragmentType.Delete, FragmentType.Booster]; export interface IActiveFragmentParams { x: number; y: number; + rotation: number; fragment: Fragment; } export class ActiveFragment { id: number; charge: number; + rotation: number; x: number; y: number; @@ -21,13 +23,14 @@ export class ActiveFragment { this.id = params.fragment.id; this.x = params.x; this.y = params.y; - this.charge = 1; - if (noCharge.includes(params.fragment.type)) this.charge = 0; + this.charge = 0; + this.rotation = params.rotation; } else { this.id = -1; this.x = -1; this.y = -1; this.charge = -1; + this.rotation = -1; } } @@ -39,7 +42,8 @@ export class ActiveFragment { const dy: number = other.y - this.y; for (let j = 0; j < thisFragment.shape.length; j++) { for (let i = 0; i < thisFragment.shape[j].length; i++) { - if (thisFragment.fullAt(i, j) && otherFragment.fullAt(i - dx, j - dy)) return true; + if (thisFragment.fullAt(i, j, this.rotation) && otherFragment.fullAt(i - dx, j - dy, other.rotation)) + return true; } } @@ -53,12 +57,12 @@ export class ActiveFragment { } fullAt(worldX: number, worldY: number): boolean { - return this.fragment().fullAt(worldX - this.x, worldY - this.y); + return this.fragment().fullAt(worldX - this.x, worldY - this.y, this.rotation); } neighboors(): number[][] { return this.fragment() - .neighboors() + .neighboors(this.rotation) .map((cell) => [this.x + cell[0], this.y + cell[1]]); } @@ -66,7 +70,7 @@ export class ActiveFragment { // We have to do a round trip because the constructor. const fragment = FragmentById(this.id); if (fragment === null) throw new Error("ActiveFragment id refers to unknown Fragment."); - const c = new ActiveFragment({ x: this.x, y: this.y, fragment: fragment }); + const c = new ActiveFragment({ x: this.x, y: this.y, rotation: this.rotation, fragment: fragment }); c.charge = this.charge; return c; } diff --git a/src/CotMG/Fragment.ts b/src/CotMG/Fragment.ts index 148b02862..86a5819db 100644 --- a/src/CotMG/Fragment.ts +++ b/src/CotMG/Fragment.ts @@ -17,30 +17,44 @@ export class Fragment { this.limit = limit; } - fullAt(x: number, y: number): boolean { + fullAt(x: number, y: number, rotation: number, debug = false): boolean { if (y < 0) return false; - if (y >= this.shape.length) return false; + if (y >= this.height(rotation)) return false; if (x < 0) return false; - if (x >= this.shape[y].length) return false; - // Yes it's ordered y first. - return this.shape[y][x]; + if (x >= this.width(rotation)) return false; + // start xy, modifier xy + let [sx, sy, mx, my] = [0, 0, 1, 1]; + if (rotation === 1) { + [sx, sy, mx, my] = [this.width(rotation) - 1, 0, -1, 1]; + } else if (rotation === 2) { + [sx, sy, mx, my] = [this.width(rotation) - 1, this.height(rotation) - 1, -1, -1]; + } else if (rotation === 3) { + [sx, sy, mx, my] = [0, this.height(rotation) - 1, 1, -1]; + } + let [qx, qy] = [sx + mx * x, sy + my * y]; + if (rotation % 2 === 1) [qx, qy] = [qy, qx]; + if (debug) { + console.log("q " + [qx, qy]); + } + return this.shape[qy][qx]; } - width(): number { - // check every line for robustness. - return Math.max(...this.shape.map((line) => line.length)); - } - - height(): number { + width(rotation: number): number { + if (rotation % 2 === 0) return this.shape[0].length; return this.shape.length; } + height(rotation: number): number { + if (rotation % 2 === 0) return this.shape.length; + return this.shape[0].length; + } + // List of direct neighboors of this fragment. - neighboors(): number[][] { + neighboors(rotation: number): number[][] { const candidates: number[][] = []; const add = (x: number, y: number): void => { - if (this.fullAt(x, y)) return; + if (this.fullAt(x, y, rotation)) return; if (candidates.some((coord) => coord[0] === x && coord[1] === y)) return; candidates.push([x, y]); }; diff --git a/src/CotMG/IStaneksGift.ts b/src/CotMG/IStaneksGift.ts index 08a9e5249..bb0d47425 100644 --- a/src/CotMG/IStaneksGift.ts +++ b/src/CotMG/IStaneksGift.ts @@ -10,8 +10,8 @@ export interface IStaneksGift { charge(worldX: number, worldY: number, ram: number): number; process(p: IPlayer, n: number): void; effect(fragment: ActiveFragment): number; - canPlace(x: number, y: number, fragment: Fragment): boolean; - place(x: number, y: number, fragment: Fragment): boolean; + canPlace(x: number, y: number, rotation: number, fragment: Fragment): boolean; + place(x: number, y: number, rotation: number, fragment: Fragment): boolean; fragmentAt(worldX: number, worldY: number): ActiveFragment | null; deleteAt(worldX: number, worldY: number): boolean; clear(): void; diff --git a/src/CotMG/StaneksGift.ts b/src/CotMG/StaneksGift.ts index e679ad8f2..3db4d8340 100644 --- a/src/CotMG/StaneksGift.ts +++ b/src/CotMG/StaneksGift.ts @@ -70,20 +70,20 @@ export class StaneksGift implements IStaneksGift { return CalculateEffect(fragment.charge, fragment.fragment().power, boost); } - canPlace(x: number, y: number, fragment: Fragment): boolean { - if (x + fragment.width() > this.width()) return false; - if (y + fragment.height() > this.height()) return false; + canPlace(x: number, y: number, rotation: number, fragment: Fragment): boolean { + if (x + fragment.width(0) > this.width()) return false; + if (y + fragment.height(0) > this.height()) return false; if (this.count(fragment) >= fragment.limit) return false; - const newFrag = new ActiveFragment({ x: x, y: y, fragment: fragment }); + const newFrag = new ActiveFragment({ x: x, y: y, rotation: rotation, fragment: fragment }); for (const aFrag of this.fragments) { if (aFrag.collide(newFrag)) return false; } return true; } - place(x: number, y: number, fragment: Fragment): boolean { - if (!this.canPlace(x, y, fragment)) return false; - this.fragments.push(new ActiveFragment({ x: x, y: y, fragment: fragment })); + place(x: number, y: number, rotation: number, fragment: Fragment): boolean { + if (!this.canPlace(x, y, rotation, fragment)) return false; + this.fragments.push(new ActiveFragment({ x: x, y: y, rotation: rotation, fragment: fragment })); return true; } diff --git a/src/CotMG/ui/FragmentSelector.tsx b/src/CotMG/ui/FragmentSelector.tsx index bb7069884..b2cb37e15 100644 --- a/src/CotMG/ui/FragmentSelector.tsx +++ b/src/CotMG/ui/FragmentSelector.tsx @@ -27,10 +27,14 @@ function FragmentOption(props: IOptionProps): React.ReactElement { { - return !props.fragment.fullAt(x, y) ? "" : props.fragment.type === FragmentType.Booster ? "blue" : "green"; + return !props.fragment.fullAt(x, y, 0) + ? "" + : props.fragment.type === FragmentType.Booster + ? "blue" + : "green"; }} /> diff --git a/src/CotMG/ui/Grid.tsx b/src/CotMG/ui/MainBoard.tsx similarity index 75% rename from src/CotMG/ui/Grid.tsx rename to src/CotMG/ui/MainBoard.tsx index edbe45748..1d5022a58 100644 --- a/src/CotMG/ui/Grid.tsx +++ b/src/CotMG/ui/MainBoard.tsx @@ -39,11 +39,11 @@ function randomColor(fragment: ActiveFragment): string { return `rgb(${colors[0] * 256}, ${colors[1] * 256}, ${colors[2] * 256})`; } -type GridProps = { +interface IProps { gift: IStaneksGift; -}; +} -export function Grid(props: GridProps): React.ReactElement { +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++) { @@ -60,16 +60,19 @@ export function Grid(props: GridProps): React.ReactElement { 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]); + const [rotation, setRotation] = React.useState(0); const [selectedFragment, setSelectedFragment] = React.useState(NoneFragment); function moveGhost(worldX: number, worldY: number): void { + if (selectedFragment.type === FragmentType.None || selectedFragment.type === FragmentType.Delete) return; const newgrid = zeros([props.gift.width(), props.gift.height()]); - for (let i = 0; i < selectedFragment.shape.length; i++) { - for (let j = 0; j < selectedFragment.shape[i].length; j++) { - if (!selectedFragment.shape[i][j]) continue; - if (worldX + j > newgrid.length - 1) continue; - if (worldY + i > newgrid[worldX + j].length - 1) continue; - newgrid[worldX + j][worldY + i] = 1; + for (let y = 0; y < selectedFragment.height(rotation); y++) { + for (let x = 0; x < selectedFragment.width(rotation); x++) { + console.log([x, y]); + if (!selectedFragment.fullAt(x, y, rotation, true)) continue; + if (worldX + x > newgrid.length - 1) continue; + if (worldY + y > newgrid[worldX + x].length - 1) continue; + newgrid[worldX + x][worldY + y] = 1; } } @@ -86,8 +89,8 @@ export function Grid(props: GridProps): React.ReactElement { if (selectedFragment.type == FragmentType.Delete) { deleteAt(worldX, worldY); } else { - if (!props.gift.canPlace(worldX, worldY, selectedFragment)) return; - props.gift.place(worldX, worldY, selectedFragment); + if (!props.gift.canPlace(worldX, worldY, rotation, selectedFragment)) return; + props.gift.place(worldX, worldY, rotation, selectedFragment); } setGrid(calculateGrid(props.gift)); } @@ -130,6 +133,25 @@ export function Grid(props: GridProps): React.ReactElement { setGhostGrid(newgrid); } + React.useEffect(() => { + function doRotate(this: Document, event: KeyboardEvent): void { + if (event.key === "q") { + setRotation((rotation - 1 + 4) % 4); + console.log((rotation - 1 + 4) % 4); + } + if (event.key === "e") { + setRotation((rotation + 1) % 4); + console.log((rotation + 1) % 4); + } + } + document.addEventListener("keydown", doRotate); + return () => document.removeEventListener("keydown", doRotate); + }); + + // try { + // console.log(selectedFragment); + // } catch (err) {} + return ( <> diff --git a/src/CotMG/ui/StaneksGiftRoot.tsx b/src/CotMG/ui/StaneksGiftRoot.tsx index c9fed9f29..f03f06395 100644 --- a/src/CotMG/ui/StaneksGiftRoot.tsx +++ b/src/CotMG/ui/StaneksGiftRoot.tsx @@ -2,7 +2,7 @@ import React, { useState, useEffect } from "react"; import { convertTimeMsToTimeElapsedString } from "../../utils/StringHelperFunctions"; import { CONSTANTS } from "../../Constants"; import { StaneksGiftEvents } from "../StaneksGiftEvents"; -import { Grid } from "./Grid"; +import { MainBoard } from "./MainBoard"; import { IStaneksGift } from "../IStaneksGift"; import Typography from "@mui/material/Typography"; @@ -30,7 +30,7 @@ export function StaneksGiftRoot({ staneksGift }: IProps): React.ReactElement { Bonus time: {convertTimeMsToTimeElapsedString(CONSTANTS._idleSpeed * staneksGift.storedCycles)} )} - + ); } diff --git a/src/NetscriptFunctions/Stanek.ts b/src/NetscriptFunctions/Stanek.ts index f3816421d..3be6a2a6d 100644 --- a/src/NetscriptFunctions/Stanek.ts +++ b/src/NetscriptFunctions/Stanek.ts @@ -12,8 +12,8 @@ export interface INetscriptStanek { fragmentDefinitions(): any; placedFragments(): any; clear(): void; - canPlace(worldX: number, worldY: number, fragmentId: number): boolean; - place(worldX: number, worldY: number, fragmentId: number): boolean; + canPlace(worldX: number, worldY: number, rotation: number, fragmentId: number): boolean; + place(worldX: number, worldY: number, rotation: number, fragmentId: number): boolean; fragmentAt(worldX: number, worldY: number): any; deleteAt(worldX: number, worldY: number): boolean; } @@ -55,19 +55,19 @@ export function NetscriptStanek( //checkStanekAPIAccess("clear"); staneksGift.clear(); }, - canPlace: function (worldX: any, worldY: any, fragmentId: any): any { + canPlace: function (worldX: any, worldY: any, rotation: any, fragmentId: any): any { helper.updateDynamicRam("canPlace", getRamCost("stanek", "canPlace")); //checkStanekAPIAccess("canPlace"); const fragment = FragmentById(fragmentId); if (!fragment) throw helper.makeRuntimeErrorMsg("stanek.canPlace", `Invalid fragment id: ${fragmentId}`); - return staneksGift.canPlace(worldX, worldY, fragment); + return staneksGift.canPlace(worldX, worldY, rotation, fragment); }, - place: function (worldX: any, worldY: any, fragmentId: any): any { + place: function (worldX: any, worldY: any, rotation: any, fragmentId: any): any { helper.updateDynamicRam("place", getRamCost("stanek", "place")); //checkStanekAPIAccess("place"); const fragment = FragmentById(fragmentId); if (!fragment) throw helper.makeRuntimeErrorMsg("stanek.place", `Invalid fragment id: ${fragmentId}`); - return staneksGift.place(worldX, worldY, fragment); + return staneksGift.place(worldX, worldY, rotation, fragment); }, fragmentAt: function (worldX: any, worldY: any): any { helper.updateDynamicRam("fragmentAt", getRamCost("stanek", "fragmentAt"));