mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-11-11 10:13:52 +01:00
rotation!
This commit is contained in:
parent
c0420d1787
commit
092d5146b4
@ -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;
|
||||
}
|
||||
|
@ -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]);
|
||||
};
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -27,10 +27,14 @@ function FragmentOption(props: IOptionProps): React.ReactElement {
|
||||
<Box display="flex">
|
||||
<Box sx={{ mx: 2 }}>
|
||||
<FragmentPreview
|
||||
width={props.fragment.width()}
|
||||
height={props.fragment.height()}
|
||||
width={props.fragment.width(0)}
|
||||
height={props.fragment.height(0)}
|
||||
colorAt={(x, y) => {
|
||||
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";
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
|
@ -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 (
|
||||
<>
|
||||
<Button onClick={clear}>Clear</Button>
|
@ -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)}
|
||||
</Typography>
|
||||
)}
|
||||
<Grid gift={staneksGift} />
|
||||
<MainBoard gift={staneksGift} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -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"));
|
||||
|
Loading…
Reference in New Issue
Block a user