mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2025-02-19 19:33:42 +01:00
ui work
This commit is contained in:
@ -51,7 +51,7 @@ export class ActiveFragment {
|
|||||||
|
|
||||||
fragment(): Fragment {
|
fragment(): Fragment {
|
||||||
const fragment = FragmentById(this.id);
|
const fragment = FragmentById(this.id);
|
||||||
if (fragment === null) throw "ActiveFragment id refers to unknown Fragment.";
|
if (fragment === null) throw new Error("ActiveFragment id refers to unknown Fragment.");
|
||||||
return fragment;
|
return fragment;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,7 +68,7 @@ export class ActiveFragment {
|
|||||||
copy(): ActiveFragment {
|
copy(): 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 "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, fragment: fragment });
|
||||||
c.charge = this.charge;
|
c.charge = this.charge;
|
||||||
c.heat = this.heat;
|
c.heat = this.heat;
|
||||||
|
@ -3,287 +3,336 @@ import { FragmentType } from "./FragmentType";
|
|||||||
export const Fragments: Fragment[] = [];
|
export const Fragments: Fragment[] = [];
|
||||||
|
|
||||||
export class Fragment {
|
export class Fragment {
|
||||||
id: number;
|
id: number;
|
||||||
shape: boolean[][];
|
shape: boolean[][];
|
||||||
type: FragmentType;
|
type: FragmentType;
|
||||||
power: number;
|
power: number;
|
||||||
limit: number;
|
limit: number;
|
||||||
|
|
||||||
constructor(id: number, shape: boolean[][], type: FragmentType, power: number, limit: number) {
|
constructor(id: number, shape: boolean[][], type: FragmentType, power: number, limit: number) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.shape = shape;
|
this.shape = shape;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.power = power;
|
this.power = power;
|
||||||
this.limit = limit;
|
this.limit = limit;
|
||||||
|
}
|
||||||
|
|
||||||
|
fullAt(x: number, y: number): boolean {
|
||||||
|
if (y < 0) return false;
|
||||||
|
if (y >= this.shape.length) 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];
|
||||||
|
}
|
||||||
|
|
||||||
|
width(): number {
|
||||||
|
// check every line for robustness.
|
||||||
|
return Math.max(...this.shape.map((line) => line.length));
|
||||||
|
}
|
||||||
|
|
||||||
|
height(): number {
|
||||||
|
return this.shape.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
// List of direct neighboors of this fragment.
|
||||||
|
neighboors(): number[][] {
|
||||||
|
const candidates: number[][] = [];
|
||||||
|
|
||||||
|
const add = (x: number, y: number): void => {
|
||||||
|
if (this.fullAt(x, y)) return;
|
||||||
|
if (candidates.some((coord) => coord[0] === x && coord[1] === y)) return;
|
||||||
|
candidates.push([x, y]);
|
||||||
|
};
|
||||||
|
for (let y = 0; y < this.shape.length; y++) {
|
||||||
|
for (let x = 0; x < this.shape[y].length; x++) {
|
||||||
|
// This cell is full, add all it's neighboors.
|
||||||
|
if (!this.shape[y][x]) continue;
|
||||||
|
add(x - 1, y);
|
||||||
|
add(x + 1, y);
|
||||||
|
add(x, y - 1);
|
||||||
|
add(x, y + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const cells: number[][] = [];
|
||||||
|
for (const candidate of candidates) {
|
||||||
|
if (cells.some((cell) => cell[0] === candidate[0] && cell[1] === candidate[1])) continue;
|
||||||
|
cells.push(candidate);
|
||||||
}
|
}
|
||||||
|
|
||||||
fullAt(x: number, y: number): boolean {
|
return cells;
|
||||||
if(y < 0) return false;
|
}
|
||||||
if(y >= this.shape.length) 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];
|
|
||||||
}
|
|
||||||
|
|
||||||
width() {
|
copy(): Fragment {
|
||||||
// check every line for robustness.
|
return new Fragment(
|
||||||
return Math.max(...this.shape.map(line => line.length));
|
this.id,
|
||||||
}
|
this.shape.map((a) => a.slice()),
|
||||||
|
this.type,
|
||||||
height() {
|
this.power,
|
||||||
return this.shape.length;
|
this.limit,
|
||||||
}
|
);
|
||||||
|
}
|
||||||
// List of direct neighboors of this fragment.
|
|
||||||
neighboors(): number[][] {
|
|
||||||
let candidates: number[][] = [];
|
|
||||||
|
|
||||||
const add = (x: number, y: number): void => {
|
|
||||||
if(this.fullAt(x, y)) return;
|
|
||||||
if(candidates.some(coord => coord[0] === x && coord[1] === y)) return;
|
|
||||||
candidates.push([x, y]);
|
|
||||||
};
|
|
||||||
for(let y = 0; y < this.shape.length; y++) {
|
|
||||||
for(let x = 0; x < this.shape[y].length; x++) {
|
|
||||||
// This cell is full, add all it's neighboors.
|
|
||||||
if(!this.shape[y][x]) continue;
|
|
||||||
add(x-1, y);
|
|
||||||
add(x+1, y);
|
|
||||||
add(x, y-1);
|
|
||||||
add(x, y+1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const cells: number[][] = [];
|
|
||||||
for(const candidate of candidates) {
|
|
||||||
if(cells.some(cell => cell[0] === candidate[0] && cell[1] === candidate[1])) continue;
|
|
||||||
cells.push(candidate);
|
|
||||||
}
|
|
||||||
|
|
||||||
return cells;
|
|
||||||
}
|
|
||||||
|
|
||||||
copy(): Fragment {
|
|
||||||
return new Fragment(this.id, this.shape.map(a => a.slice()), this.type, this.power, this.limit);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function FragmentById(id: number): Fragment | null {
|
export function FragmentById(id: number): Fragment | null {
|
||||||
for(const fragment of Fragments) {
|
for (const fragment of Fragments) {
|
||||||
if(fragment.id === id) return fragment;
|
if (fragment.id === id) return fragment;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
(function () {
|
||||||
|
const _ = false;
|
||||||
|
const X = true;
|
||||||
|
Fragments.push(
|
||||||
|
new Fragment(
|
||||||
|
0, // id
|
||||||
|
[
|
||||||
|
// shape
|
||||||
|
[X, X, X],
|
||||||
|
[_, _, X],
|
||||||
|
[_, _, X],
|
||||||
|
],
|
||||||
|
FragmentType.Hacking, // type
|
||||||
|
10,
|
||||||
|
1, // limit
|
||||||
|
),
|
||||||
|
);
|
||||||
|
Fragments.push(
|
||||||
|
new Fragment(
|
||||||
|
1, // id
|
||||||
|
[
|
||||||
|
// shape
|
||||||
|
[_, X, _],
|
||||||
|
[X, X, X],
|
||||||
|
[_, X, _],
|
||||||
|
],
|
||||||
|
FragmentType.Hacking, // type
|
||||||
|
10,
|
||||||
|
1, // limit
|
||||||
|
),
|
||||||
|
);
|
||||||
|
Fragments.push(
|
||||||
|
new Fragment(
|
||||||
|
2, // id
|
||||||
|
[
|
||||||
|
// shape
|
||||||
|
[X, X, X],
|
||||||
|
[X, _, X],
|
||||||
|
[X, X, X],
|
||||||
|
],
|
||||||
|
FragmentType.Booster, // type
|
||||||
|
500,
|
||||||
|
3, // limit
|
||||||
|
),
|
||||||
|
);
|
||||||
|
Fragments.push(
|
||||||
|
new Fragment(
|
||||||
|
3, // id
|
||||||
|
[
|
||||||
|
// shape
|
||||||
|
[X, X],
|
||||||
|
[X, X],
|
||||||
|
],
|
||||||
|
FragmentType.Cooling, // type
|
||||||
|
200,
|
||||||
|
Infinity, // limit
|
||||||
|
),
|
||||||
|
);
|
||||||
|
Fragments.push(
|
||||||
|
new Fragment(
|
||||||
|
4, // id
|
||||||
|
[
|
||||||
|
// shape
|
||||||
|
[X],
|
||||||
|
],
|
||||||
|
FragmentType.Cooling, // type
|
||||||
|
50,
|
||||||
|
1, // limit
|
||||||
|
),
|
||||||
|
);
|
||||||
|
Fragments.push(
|
||||||
|
new Fragment(
|
||||||
|
5, // id
|
||||||
|
[
|
||||||
|
// shape
|
||||||
|
[X, X],
|
||||||
|
],
|
||||||
|
FragmentType.HackingSpeed, // type
|
||||||
|
50,
|
||||||
|
1, // limit
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
(function() {
|
Fragments.push(
|
||||||
const _ = false;
|
new Fragment(
|
||||||
const X = true;
|
6, // id
|
||||||
Fragments.push(new Fragment(
|
[
|
||||||
0, // id
|
[X, _],
|
||||||
[ // shape
|
[X, X],
|
||||||
[X,X,X],
|
], // shape
|
||||||
[_,_,X],
|
FragmentType.HackingMoney, // type
|
||||||
[_,_,X],
|
10, // power
|
||||||
],
|
1, // limit
|
||||||
FragmentType.Hacking, // type
|
),
|
||||||
10,
|
);
|
||||||
1, // limit
|
Fragments.push(
|
||||||
));
|
new Fragment(
|
||||||
Fragments.push(new Fragment(
|
7, // id
|
||||||
1, // id
|
[
|
||||||
[ // shape
|
[X, X],
|
||||||
[_,X,_],
|
[X, X],
|
||||||
[X,X,X],
|
], // shape
|
||||||
[_,X,_],
|
FragmentType.HackingGrow, // type
|
||||||
],
|
30, // power
|
||||||
FragmentType.Hacking, // type
|
1, // limit
|
||||||
10,
|
),
|
||||||
1, // limit
|
);
|
||||||
));
|
Fragments.push(
|
||||||
Fragments.push(new Fragment(
|
new Fragment(
|
||||||
2, // id
|
8, // id
|
||||||
[ // shape
|
[
|
||||||
[X,X,X],
|
[X, X, X],
|
||||||
[X,_,X],
|
[_, X, _],
|
||||||
[X,X,X],
|
[X, X, X],
|
||||||
],
|
], // shape
|
||||||
FragmentType.Booster, // type
|
FragmentType.Hacking, // type
|
||||||
500,
|
50, // power
|
||||||
3, // limit
|
1, // limit
|
||||||
));
|
),
|
||||||
Fragments.push(new Fragment(
|
);
|
||||||
3, // id
|
Fragments.push(
|
||||||
[ // shape
|
new Fragment(
|
||||||
[X,X],
|
10, // id
|
||||||
[X,X],
|
[
|
||||||
],
|
[X, X],
|
||||||
FragmentType.Cooling, // type
|
[_, X],
|
||||||
200,
|
], // shape
|
||||||
Infinity, // limit
|
FragmentType.Strength, // type
|
||||||
));
|
50, // power
|
||||||
Fragments.push(new Fragment(
|
1, // limit
|
||||||
4, // id
|
),
|
||||||
[ // shape
|
);
|
||||||
[X],
|
Fragments.push(
|
||||||
],
|
new Fragment(
|
||||||
FragmentType.Cooling, // type
|
12, // id
|
||||||
50,
|
[
|
||||||
1, // limit
|
[_, X],
|
||||||
));
|
[X, X],
|
||||||
Fragments.push(new Fragment(
|
], // shape
|
||||||
5, // id
|
FragmentType.Defense, // type
|
||||||
[ // shape
|
50, // power
|
||||||
[X, X],
|
1, // limit
|
||||||
],
|
),
|
||||||
FragmentType.HackingSpeed, // type
|
);
|
||||||
50,
|
Fragments.push(
|
||||||
1, // limit
|
new Fragment(
|
||||||
));
|
14, // id
|
||||||
|
[
|
||||||
Fragments.push(new Fragment(
|
[X, X],
|
||||||
6, // id
|
[X, _],
|
||||||
[
|
], // shape
|
||||||
[X, _],
|
FragmentType.Dexterity, // type
|
||||||
[X, X],
|
50, // power
|
||||||
], // shape
|
1, // limit
|
||||||
FragmentType.HackingMoney, // type
|
),
|
||||||
10, // power
|
);
|
||||||
1, // limit
|
Fragments.push(
|
||||||
));
|
new Fragment(
|
||||||
Fragments.push(new Fragment(
|
16, // id
|
||||||
7, // id
|
[
|
||||||
[
|
[X, _],
|
||||||
[X, X],
|
[X, X],
|
||||||
[X, X],
|
], // shape
|
||||||
], // shape
|
FragmentType.Agility, // type
|
||||||
FragmentType.HackingGrow, // type
|
50, // power
|
||||||
30, // power
|
1, // limit
|
||||||
1, // limit
|
),
|
||||||
));
|
);
|
||||||
Fragments.push(new Fragment(
|
Fragments.push(
|
||||||
8, // id
|
new Fragment(
|
||||||
[
|
18, // id
|
||||||
[X, X, X],
|
[
|
||||||
[_, X, _],
|
[X, X],
|
||||||
[X, X, X],
|
[X, _],
|
||||||
], // shape
|
], // shape
|
||||||
FragmentType.Hacking, // type
|
FragmentType.Charisma, // type
|
||||||
50, // power
|
50, // power
|
||||||
1, // limit
|
1, // limit
|
||||||
));
|
),
|
||||||
Fragments.push(new Fragment(
|
);
|
||||||
10, // id
|
Fragments.push(
|
||||||
[
|
new Fragment(
|
||||||
[X, X],
|
20, // id
|
||||||
[_, X],
|
[
|
||||||
], // shape
|
[X, _, _],
|
||||||
FragmentType.Strength, // type
|
[X, X, _],
|
||||||
50, // power
|
[X, X, X],
|
||||||
1, // limit
|
], // shape
|
||||||
));
|
FragmentType.HacknetMoney, // type
|
||||||
Fragments.push(new Fragment(
|
30, // power
|
||||||
12, // id
|
1, // limit
|
||||||
[
|
),
|
||||||
[_, X],
|
);
|
||||||
[X, X],
|
Fragments.push(
|
||||||
], // shape
|
new Fragment(
|
||||||
FragmentType.Defense, // type
|
21, // id
|
||||||
50, // power
|
[
|
||||||
1, // limit
|
[X, X],
|
||||||
));
|
[_, X],
|
||||||
Fragments.push(new Fragment(
|
[_, X],
|
||||||
14, // id
|
], // shape
|
||||||
[
|
FragmentType.HacknetCost, // type
|
||||||
[X, X],
|
-10, // power
|
||||||
[X, _],
|
1, // limit
|
||||||
], // shape
|
),
|
||||||
FragmentType.Dexterity, // type
|
);
|
||||||
50, // power
|
Fragments.push(
|
||||||
1, // limit
|
new Fragment(
|
||||||
));
|
25, // id
|
||||||
Fragments.push(new Fragment(
|
[
|
||||||
16, // id
|
[X, X, X],
|
||||||
[
|
[_, X, _],
|
||||||
[X, _],
|
], // shape
|
||||||
[X, X],
|
FragmentType.Rep, // type
|
||||||
], // shape
|
100, // power
|
||||||
FragmentType.Agility, // type
|
1, // limit
|
||||||
50, // power
|
),
|
||||||
1, // limit
|
);
|
||||||
));
|
Fragments.push(
|
||||||
Fragments.push(new Fragment(
|
new Fragment(
|
||||||
18, // id
|
27, // id
|
||||||
[
|
[
|
||||||
[X, X],
|
[X, _],
|
||||||
[X, _],
|
[_, X],
|
||||||
], // shape
|
], // shape
|
||||||
FragmentType.Charisma, // type
|
FragmentType.WorkMoney, // type
|
||||||
50, // power
|
20, // power
|
||||||
1, // limit
|
1, // limit
|
||||||
));
|
),
|
||||||
Fragments.push(new Fragment(
|
);
|
||||||
20, // id
|
Fragments.push(
|
||||||
[
|
new Fragment(
|
||||||
[X, _, _],
|
28, // id
|
||||||
[X, X, _],
|
[[X, X]], // shape
|
||||||
[X, X, X],
|
FragmentType.Crime, // type
|
||||||
], // shape
|
20, // power
|
||||||
FragmentType.HacknetMoney, // type
|
1, // limit
|
||||||
30, // power
|
),
|
||||||
1, // limit
|
);
|
||||||
));
|
Fragments.push(
|
||||||
Fragments.push(new Fragment(
|
new Fragment(
|
||||||
21, // id
|
30, // id
|
||||||
[
|
[
|
||||||
[X, X],
|
[X, X, X],
|
||||||
[_, X],
|
[X, X, X],
|
||||||
[_, X],
|
[X, X, X],
|
||||||
], // shape
|
], // shape
|
||||||
FragmentType.HacknetCost, // type
|
FragmentType.Bladeburner, // type
|
||||||
-10, // power
|
50, // power
|
||||||
1, // limit
|
1, // limit
|
||||||
));
|
),
|
||||||
Fragments.push(new Fragment(
|
);
|
||||||
25, // id
|
|
||||||
[
|
|
||||||
[X, X, X],
|
|
||||||
[_, X, _],
|
|
||||||
], // shape
|
|
||||||
FragmentType.Rep, // type
|
|
||||||
100, // power
|
|
||||||
1, // limit
|
|
||||||
));
|
|
||||||
Fragments.push(new Fragment(
|
|
||||||
27, // id
|
|
||||||
[
|
|
||||||
[X, _],
|
|
||||||
[_, X],
|
|
||||||
], // shape
|
|
||||||
FragmentType.WorkMoney, // type
|
|
||||||
20, // power
|
|
||||||
1, // limit
|
|
||||||
));
|
|
||||||
Fragments.push(new Fragment(
|
|
||||||
28, // id
|
|
||||||
[
|
|
||||||
[X, X],
|
|
||||||
], // shape
|
|
||||||
FragmentType.Crime, // type
|
|
||||||
20, // power
|
|
||||||
1, // limit
|
|
||||||
));
|
|
||||||
Fragments.push(new Fragment(
|
|
||||||
30, // id
|
|
||||||
[
|
|
||||||
[X, X, X],
|
|
||||||
[X, X, X],
|
|
||||||
[X, X, X],
|
|
||||||
], // shape
|
|
||||||
FragmentType.Bladeburner, // type
|
|
||||||
50, // power
|
|
||||||
1, // limit
|
|
||||||
));
|
|
||||||
})();
|
})();
|
||||||
|
|
||||||
export const NoneFragment = new Fragment(-2, [], FragmentType.None, 0, Infinity);
|
export const NoneFragment = new Fragment(-2, [], FragmentType.None, 0, Infinity);
|
||||||
|
@ -9,9 +9,11 @@ import Typography from "@mui/material/Typography";
|
|||||||
|
|
||||||
type IProps = {
|
type IProps = {
|
||||||
fragment: ActiveFragment | null;
|
fragment: ActiveFragment | null;
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function FragmentInspector(props: IProps) {
|
export function FragmentInspector(props: IProps): React.ReactElement {
|
||||||
const [, setC] = useState(new Date());
|
const [, setC] = useState(new Date());
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -38,6 +40,7 @@ export function FragmentInspector(props: IProps) {
|
|||||||
<br />
|
<br />
|
||||||
[X, Y] N/A
|
[X, Y] N/A
|
||||||
<br />
|
<br />
|
||||||
|
[X, Y] {props.x}, {props.y}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Paper>
|
</Paper>
|
||||||
);
|
);
|
||||||
@ -68,8 +71,9 @@ export function FragmentInspector(props: IProps) {
|
|||||||
<br />
|
<br />
|
||||||
Effect: {effect}
|
Effect: {effect}
|
||||||
<br />
|
<br />
|
||||||
[X, Y] {props.fragment.x}, {props.fragment.y}
|
root [X, Y] {props.fragment.x}, {props.fragment.y}
|
||||||
<br />
|
<br />
|
||||||
|
[X, Y] {props.x}, {props.y}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Paper>
|
</Paper>
|
||||||
);
|
);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { Fragment, Fragments, NoneFragment } from "../Fragment";
|
import { Fragment, NoneFragment } from "../Fragment";
|
||||||
import { ActiveFragment } from "../ActiveFragment";
|
import { ActiveFragment } from "../ActiveFragment";
|
||||||
import { FragmentType } from "../FragmentType";
|
import { FragmentType } from "../FragmentType";
|
||||||
import { IStaneksGift } from "../IStaneksGift";
|
import { IStaneksGift } from "../IStaneksGift";
|
||||||
@ -7,6 +7,9 @@ import { Cell } from "./Cell";
|
|||||||
import { FragmentInspector } from "./FragmentInspector";
|
import { FragmentInspector } from "./FragmentInspector";
|
||||||
import { FragmentSelector } from "./FragmentSelector";
|
import { FragmentSelector } from "./FragmentSelector";
|
||||||
import Button from "@mui/material/Button";
|
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 {
|
function zeros(dimensions: number[]): any {
|
||||||
const array = [];
|
const array = [];
|
||||||
@ -116,9 +119,9 @@ export function Grid(props: GridProps): React.ReactElement {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
elems.push(
|
elems.push(
|
||||||
<div key={j} className="staneksgift_row">
|
<TableRow key={j} className="staneksgift_row">
|
||||||
{cells}
|
{cells}
|
||||||
</div>,
|
</TableRow>,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -130,10 +133,12 @@ export function Grid(props: GridProps): React.ReactElement {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{elems}
|
<Table>
|
||||||
|
<TableBody>{elems}</TableBody>
|
||||||
|
</Table>
|
||||||
<FragmentSelector gift={props.gift} selectFragment={updateSelectedFragment} />
|
<FragmentSelector gift={props.gift} selectFragment={updateSelectedFragment} />
|
||||||
<Button onClick={clear}>Clear</Button>
|
<Button onClick={clear}>Clear</Button>
|
||||||
<FragmentInspector fragment={props.gift.fragmentAt(pos[0], pos[1])} />
|
<FragmentInspector x={pos[0]} y={pos[1]} fragment={props.gift.fragmentAt(pos[0], pos[1])} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user