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 {
|
export interface IActiveFragmentParams {
|
||||||
x: number;
|
x: number;
|
||||||
y: number;
|
y: number;
|
||||||
|
rotation: number;
|
||||||
fragment: Fragment;
|
fragment: Fragment;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ActiveFragment {
|
export class ActiveFragment {
|
||||||
id: number;
|
id: number;
|
||||||
charge: number;
|
charge: number;
|
||||||
|
rotation: number;
|
||||||
x: number;
|
x: number;
|
||||||
y: number;
|
y: number;
|
||||||
|
|
||||||
@ -21,13 +23,14 @@ export class ActiveFragment {
|
|||||||
this.id = params.fragment.id;
|
this.id = params.fragment.id;
|
||||||
this.x = params.x;
|
this.x = params.x;
|
||||||
this.y = params.y;
|
this.y = params.y;
|
||||||
this.charge = 1;
|
this.charge = 0;
|
||||||
if (noCharge.includes(params.fragment.type)) this.charge = 0;
|
this.rotation = params.rotation;
|
||||||
} else {
|
} else {
|
||||||
this.id = -1;
|
this.id = -1;
|
||||||
this.x = -1;
|
this.x = -1;
|
||||||
this.y = -1;
|
this.y = -1;
|
||||||
this.charge = -1;
|
this.charge = -1;
|
||||||
|
this.rotation = -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,7 +42,8 @@ export class ActiveFragment {
|
|||||||
const dy: number = other.y - this.y;
|
const dy: number = other.y - this.y;
|
||||||
for (let j = 0; j < thisFragment.shape.length; j++) {
|
for (let j = 0; j < thisFragment.shape.length; j++) {
|
||||||
for (let i = 0; i < thisFragment.shape[j].length; i++) {
|
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 {
|
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[][] {
|
neighboors(): number[][] {
|
||||||
return this.fragment()
|
return this.fragment()
|
||||||
.neighboors()
|
.neighboors(this.rotation)
|
||||||
.map((cell) => [this.x + cell[0], this.y + cell[1]]);
|
.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.
|
// We have to do a round trip because the constructor.
|
||||||
const fragment = FragmentById(this.id);
|
const fragment = FragmentById(this.id);
|
||||||
if (fragment === null) throw new Error("ActiveFragment id refers to unknown Fragment.");
|
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;
|
c.charge = this.charge;
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
@ -17,30 +17,44 @@ export class Fragment {
|
|||||||
this.limit = limit;
|
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 < 0) return false;
|
||||||
if (y >= this.shape.length) return false;
|
if (y >= this.height(rotation)) return false;
|
||||||
if (x < 0) return false;
|
if (x < 0) return false;
|
||||||
if (x >= this.shape[y].length) return false;
|
if (x >= this.width(rotation)) return false;
|
||||||
// Yes it's ordered y first.
|
// start xy, modifier xy
|
||||||
return this.shape[y][x];
|
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 {
|
width(rotation: number): number {
|
||||||
// check every line for robustness.
|
if (rotation % 2 === 0) return this.shape[0].length;
|
||||||
return Math.max(...this.shape.map((line) => line.length));
|
|
||||||
}
|
|
||||||
|
|
||||||
height(): number {
|
|
||||||
return this.shape.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.
|
// List of direct neighboors of this fragment.
|
||||||
neighboors(): number[][] {
|
neighboors(rotation: number): number[][] {
|
||||||
const candidates: number[][] = [];
|
const candidates: number[][] = [];
|
||||||
|
|
||||||
const add = (x: number, y: number): void => {
|
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;
|
if (candidates.some((coord) => coord[0] === x && coord[1] === y)) return;
|
||||||
candidates.push([x, y]);
|
candidates.push([x, y]);
|
||||||
};
|
};
|
||||||
|
@ -10,8 +10,8 @@ export interface IStaneksGift {
|
|||||||
charge(worldX: number, worldY: number, ram: number): number;
|
charge(worldX: number, worldY: number, ram: number): number;
|
||||||
process(p: IPlayer, n: number): void;
|
process(p: IPlayer, n: number): void;
|
||||||
effect(fragment: ActiveFragment): number;
|
effect(fragment: ActiveFragment): number;
|
||||||
canPlace(x: number, y: number, fragment: Fragment): boolean;
|
canPlace(x: number, y: number, rotation: number, fragment: Fragment): boolean;
|
||||||
place(x: number, y: number, fragment: Fragment): boolean;
|
place(x: number, y: number, rotation: number, fragment: Fragment): boolean;
|
||||||
fragmentAt(worldX: number, worldY: number): ActiveFragment | null;
|
fragmentAt(worldX: number, worldY: number): ActiveFragment | null;
|
||||||
deleteAt(worldX: number, worldY: number): boolean;
|
deleteAt(worldX: number, worldY: number): boolean;
|
||||||
clear(): void;
|
clear(): void;
|
||||||
|
@ -70,20 +70,20 @@ export class StaneksGift implements IStaneksGift {
|
|||||||
return CalculateEffect(fragment.charge, fragment.fragment().power, boost);
|
return CalculateEffect(fragment.charge, fragment.fragment().power, boost);
|
||||||
}
|
}
|
||||||
|
|
||||||
canPlace(x: number, y: number, fragment: Fragment): boolean {
|
canPlace(x: number, y: number, rotation: number, fragment: Fragment): boolean {
|
||||||
if (x + fragment.width() > this.width()) return false;
|
if (x + fragment.width(0) > this.width()) return false;
|
||||||
if (y + fragment.height() > this.height()) return false;
|
if (y + fragment.height(0) > this.height()) return false;
|
||||||
if (this.count(fragment) >= fragment.limit) 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) {
|
for (const aFrag of this.fragments) {
|
||||||
if (aFrag.collide(newFrag)) return false;
|
if (aFrag.collide(newFrag)) return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
place(x: number, y: number, fragment: Fragment): boolean {
|
place(x: number, y: number, rotation: number, fragment: Fragment): boolean {
|
||||||
if (!this.canPlace(x, y, fragment)) return false;
|
if (!this.canPlace(x, y, rotation, fragment)) return false;
|
||||||
this.fragments.push(new ActiveFragment({ x: x, y: y, fragment: fragment }));
|
this.fragments.push(new ActiveFragment({ x: x, y: y, rotation: rotation, fragment: fragment }));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,10 +27,14 @@ function FragmentOption(props: IOptionProps): React.ReactElement {
|
|||||||
<Box display="flex">
|
<Box display="flex">
|
||||||
<Box sx={{ mx: 2 }}>
|
<Box sx={{ mx: 2 }}>
|
||||||
<FragmentPreview
|
<FragmentPreview
|
||||||
width={props.fragment.width()}
|
width={props.fragment.width(0)}
|
||||||
height={props.fragment.height()}
|
height={props.fragment.height(0)}
|
||||||
colorAt={(x, y) => {
|
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>
|
</Box>
|
||||||
|
@ -39,11 +39,11 @@ function randomColor(fragment: ActiveFragment): string {
|
|||||||
return `rgb(${colors[0] * 256}, ${colors[1] * 256}, ${colors[2] * 256})`;
|
return `rgb(${colors[0] * 256}, ${colors[1] * 256}, ${colors[2] * 256})`;
|
||||||
}
|
}
|
||||||
|
|
||||||
type GridProps = {
|
interface IProps {
|
||||||
gift: IStaneksGift;
|
gift: IStaneksGift;
|
||||||
};
|
}
|
||||||
|
|
||||||
export function Grid(props: GridProps): React.ReactElement {
|
export function MainBoard(props: IProps): React.ReactElement {
|
||||||
function calculateGrid(gift: IStaneksGift): any {
|
function calculateGrid(gift: IStaneksGift): any {
|
||||||
const newgrid = zeros([gift.width(), gift.height()]);
|
const newgrid = zeros([gift.width(), gift.height()]);
|
||||||
for (let i = 0; i < gift.width(); i++) {
|
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 [grid, setGrid] = React.useState(calculateGrid(props.gift));
|
||||||
const [ghostGrid, setGhostGrid] = React.useState(zeros([props.gift.width(), props.gift.height()]));
|
const [ghostGrid, setGhostGrid] = React.useState(zeros([props.gift.width(), props.gift.height()]));
|
||||||
const [pos, setPos] = React.useState([0, 0]);
|
const [pos, setPos] = React.useState([0, 0]);
|
||||||
|
const [rotation, setRotation] = React.useState(0);
|
||||||
const [selectedFragment, setSelectedFragment] = React.useState(NoneFragment);
|
const [selectedFragment, setSelectedFragment] = React.useState(NoneFragment);
|
||||||
|
|
||||||
function moveGhost(worldX: number, worldY: number): void {
|
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()]);
|
const newgrid = zeros([props.gift.width(), props.gift.height()]);
|
||||||
for (let i = 0; i < selectedFragment.shape.length; i++) {
|
for (let y = 0; y < selectedFragment.height(rotation); y++) {
|
||||||
for (let j = 0; j < selectedFragment.shape[i].length; j++) {
|
for (let x = 0; x < selectedFragment.width(rotation); x++) {
|
||||||
if (!selectedFragment.shape[i][j]) continue;
|
console.log([x, y]);
|
||||||
if (worldX + j > newgrid.length - 1) continue;
|
if (!selectedFragment.fullAt(x, y, rotation, true)) continue;
|
||||||
if (worldY + i > newgrid[worldX + j].length - 1) continue;
|
if (worldX + x > newgrid.length - 1) continue;
|
||||||
newgrid[worldX + j][worldY + i] = 1;
|
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) {
|
if (selectedFragment.type == FragmentType.Delete) {
|
||||||
deleteAt(worldX, worldY);
|
deleteAt(worldX, worldY);
|
||||||
} else {
|
} else {
|
||||||
if (!props.gift.canPlace(worldX, worldY, selectedFragment)) return;
|
if (!props.gift.canPlace(worldX, worldY, rotation, selectedFragment)) return;
|
||||||
props.gift.place(worldX, worldY, selectedFragment);
|
props.gift.place(worldX, worldY, rotation, selectedFragment);
|
||||||
}
|
}
|
||||||
setGrid(calculateGrid(props.gift));
|
setGrid(calculateGrid(props.gift));
|
||||||
}
|
}
|
||||||
@ -130,6 +133,25 @@ export function Grid(props: GridProps): React.ReactElement {
|
|||||||
setGhostGrid(newgrid);
|
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 (
|
return (
|
||||||
<>
|
<>
|
||||||
<Button onClick={clear}>Clear</Button>
|
<Button onClick={clear}>Clear</Button>
|
@ -2,7 +2,7 @@ import React, { useState, useEffect } from "react";
|
|||||||
import { convertTimeMsToTimeElapsedString } from "../../utils/StringHelperFunctions";
|
import { convertTimeMsToTimeElapsedString } from "../../utils/StringHelperFunctions";
|
||||||
import { CONSTANTS } from "../../Constants";
|
import { CONSTANTS } from "../../Constants";
|
||||||
import { StaneksGiftEvents } from "../StaneksGiftEvents";
|
import { StaneksGiftEvents } from "../StaneksGiftEvents";
|
||||||
import { Grid } from "./Grid";
|
import { MainBoard } from "./MainBoard";
|
||||||
import { IStaneksGift } from "../IStaneksGift";
|
import { IStaneksGift } from "../IStaneksGift";
|
||||||
import Typography from "@mui/material/Typography";
|
import Typography from "@mui/material/Typography";
|
||||||
|
|
||||||
@ -30,7 +30,7 @@ export function StaneksGiftRoot({ staneksGift }: IProps): React.ReactElement {
|
|||||||
Bonus time: {convertTimeMsToTimeElapsedString(CONSTANTS._idleSpeed * staneksGift.storedCycles)}
|
Bonus time: {convertTimeMsToTimeElapsedString(CONSTANTS._idleSpeed * staneksGift.storedCycles)}
|
||||||
</Typography>
|
</Typography>
|
||||||
)}
|
)}
|
||||||
<Grid gift={staneksGift} />
|
<MainBoard gift={staneksGift} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -12,8 +12,8 @@ export interface INetscriptStanek {
|
|||||||
fragmentDefinitions(): any;
|
fragmentDefinitions(): any;
|
||||||
placedFragments(): any;
|
placedFragments(): any;
|
||||||
clear(): void;
|
clear(): void;
|
||||||
canPlace(worldX: number, worldY: number, fragmentId: number): boolean;
|
canPlace(worldX: number, worldY: number, rotation: number, fragmentId: number): boolean;
|
||||||
place(worldX: number, worldY: number, fragmentId: number): boolean;
|
place(worldX: number, worldY: number, rotation: number, fragmentId: number): boolean;
|
||||||
fragmentAt(worldX: number, worldY: number): any;
|
fragmentAt(worldX: number, worldY: number): any;
|
||||||
deleteAt(worldX: number, worldY: number): boolean;
|
deleteAt(worldX: number, worldY: number): boolean;
|
||||||
}
|
}
|
||||||
@ -55,19 +55,19 @@ export function NetscriptStanek(
|
|||||||
//checkStanekAPIAccess("clear");
|
//checkStanekAPIAccess("clear");
|
||||||
staneksGift.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"));
|
helper.updateDynamicRam("canPlace", getRamCost("stanek", "canPlace"));
|
||||||
//checkStanekAPIAccess("canPlace");
|
//checkStanekAPIAccess("canPlace");
|
||||||
const fragment = FragmentById(fragmentId);
|
const fragment = FragmentById(fragmentId);
|
||||||
if (!fragment) throw helper.makeRuntimeErrorMsg("stanek.canPlace", `Invalid fragment id: ${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"));
|
helper.updateDynamicRam("place", getRamCost("stanek", "place"));
|
||||||
//checkStanekAPIAccess("place");
|
//checkStanekAPIAccess("place");
|
||||||
const fragment = FragmentById(fragmentId);
|
const fragment = FragmentById(fragmentId);
|
||||||
if (!fragment) throw helper.makeRuntimeErrorMsg("stanek.place", `Invalid fragment id: ${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 {
|
fragmentAt: function (worldX: any, worldY: any): any {
|
||||||
helper.updateDynamicRam("fragmentAt", getRamCost("stanek", "fragmentAt"));
|
helper.updateDynamicRam("fragmentAt", getRamCost("stanek", "fragmentAt"));
|
||||||
|
Loading…
Reference in New Issue
Block a user