removed a lot of logs and started A* implementation
This commit is contained in:
parent
82c9d8374a
commit
6e33c8a99c
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@ -2,6 +2,7 @@
|
|||||||
"cSpell.words": [
|
"cSpell.words": [
|
||||||
"Alea",
|
"Alea",
|
||||||
"amogus",
|
"amogus",
|
||||||
"PIXI"
|
"PIXI",
|
||||||
|
"spritesheet"
|
||||||
]
|
]
|
||||||
}
|
}
|
@ -5,18 +5,21 @@ import { getSpriteFromAtlas } from "./Utils/Sprites.utils";
|
|||||||
import { profileFPS } from "./Profiler/Profiler";
|
import { profileFPS } from "./Profiler/Profiler";
|
||||||
import { createKeyboardBinding, inputControllerTick } from "./InputController/InputController";
|
import { createKeyboardBinding, inputControllerTick } from "./InputController/InputController";
|
||||||
import { BC_APP, BC_BUILDING_PLACEHOLDERS, BC_CAMERA, BC_CURRENT_SCENE, BC_SPRITES_SETTINGS, BC_VIEWPORT, BC_WORLD, PRNG, setBC_APP, setBC_CURRENT_SCENE, setBC_NPC_LAYER, setBC_SELECTION, setBC_VIEWPORT, setBC_WORLD } from "./GlobalVariables/GlobalVariables";
|
import { BC_APP, BC_BUILDING_PLACEHOLDERS, BC_CAMERA, BC_CURRENT_SCENE, BC_SPRITES_SETTINGS, BC_VIEWPORT, BC_WORLD, PRNG, setBC_APP, setBC_CURRENT_SCENE, setBC_NPC_LAYER, setBC_SELECTION, setBC_VIEWPORT, setBC_WORLD } from "./GlobalVariables/GlobalVariables";
|
||||||
import { clampNumber, interpolate, } from "./Utils/Math.utils";
|
import { Point2D, PointInt2D, clampNumber, interpolate, } from "./Utils/Math.utils";
|
||||||
import { calculateViewportFromCamera, moveHorizontally, moveVertically, screenToWorldCoordinates } from "./Camera/Camera";
|
import { calculateViewportFromCamera, moveHorizontally, moveVertically, screenToWorldCoordinates } from "./Camera/Camera";
|
||||||
|
|
||||||
import { handleBuildingsIncome, incBuildingCount } from "./Buildings/Buildings";
|
import { handleBuildingsIncome, incBuildingCount } from "./Buildings/Buildings";
|
||||||
import { handleDayNightCycle } from "./World/DayNightCycle";
|
import { handleDayNightCycle } from "./World/DayNightCycle";
|
||||||
import { ambientDay, ambientMusic, ambientNight, handleSounds } from "./Sound/Sound";
|
import { ambientDay, ambientMusic, ambientNight, handleSounds } from "./Sound/Sound";
|
||||||
import { gameStateObjectsCleaner } from "./GameState/GameState";
|
import { addGameObjectToGameState, gameStateObjectsCleaner } from "./GameState/GameState";
|
||||||
import { tickHandler } from "./TickHandler/TickHandler";
|
import { tickHandler } from "./TickHandler/TickHandler";
|
||||||
import { GameScene } from "./GameScene/GameScene";
|
import { GameScene } from "./GameScene/GameScene";
|
||||||
import { createFirstWorldChunks, getWorldChunkAt } from "./WorldGeneration/WorldGen";
|
import { createFirstWorldChunks, getTileAt, getWorldChunkAt, worldCoordinatesToChunkIndex } from "./WorldGeneration/WorldGen";
|
||||||
import { ChunkStorageTypes } from "./WorldGeneration/WorldChunk/WorldGenChunk";
|
import { ChunkStorageTypes } from "./WorldGeneration/WorldChunk/WorldGenChunk";
|
||||||
import { WorldChunksVisibilityUpdater } from "./WorldGeneration/WorldChunksVisibilityUpdater/WorldChunksVisibilityUpdater";
|
import { WorldChunksVisibilityUpdater } from "./WorldGeneration/WorldChunksVisibilityUpdater/WorldChunksVisibilityUpdater";
|
||||||
|
import { NPCProto } from "./NPC/NPCProto/NPCProto";
|
||||||
|
import { NPCController } from "./NPC/NPCController/NPCController";
|
||||||
|
import { PathFinder } from "./Utils/PathFinding.util";
|
||||||
|
|
||||||
export function generateWorld() {
|
export function generateWorld() {
|
||||||
|
|
||||||
@ -56,6 +59,13 @@ function setupInGameSelector() {
|
|||||||
t.y = BC_SPRITES_SETTINGS.defaultSize * BC_SPRITES_SETTINGS.scale * Math.floor(t.y / (BC_SPRITES_SETTINGS.defaultSize * BC_SPRITES_SETTINGS.scale));
|
t.y = BC_SPRITES_SETTINGS.defaultSize * BC_SPRITES_SETTINGS.scale * Math.floor(t.y / (BC_SPRITES_SETTINGS.defaultSize * BC_SPRITES_SETTINGS.scale));
|
||||||
sprite0.position.set(t.x, t.y);
|
sprite0.position.set(t.x, t.y);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// BC_VIEWPORT.onpointerdown = (e) =>
|
||||||
|
// {
|
||||||
|
// let t = screenToWorldCoordinates(e.data.global.x, e.data.global.y);
|
||||||
|
// npccc.moveTo(new Point2D(getTileAt(t.x, t.y, ChunkStorageTypes.TYPE_TERRAIN).worldPosition.getX(), getTileAt(t.x, t.y, ChunkStorageTypes.TYPE_TERRAIN).worldPosition.getY()));
|
||||||
|
// }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function initGame() {
|
export async function initGame() {
|
||||||
@ -97,9 +107,10 @@ export async function initGame() {
|
|||||||
|
|
||||||
app.ticker.add(inputControllerTick);
|
app.ticker.add(inputControllerTick);
|
||||||
app.ticker.add(calculateViewportFromCamera);
|
app.ticker.add(calculateViewportFromCamera);
|
||||||
// console.log(WorldChunksVisibilityUpdater.handleWorldChunksVisibility);
|
//static functions shitty binding
|
||||||
app.ticker.add(()=>{WorldChunksVisibilityUpdater.handleWorldChunksVisibility();});
|
app.ticker.add(WorldChunksVisibilityUpdater.handleWorldChunksVisibility.bind(WorldChunksVisibilityUpdater));
|
||||||
app.ticker.add(()=>{WorldChunksVisibilityUpdater.handleWorldChunkFilling();});
|
app.ticker.add(WorldChunksVisibilityUpdater.handleWorldChunkFilling.bind(WorldChunksVisibilityUpdater));
|
||||||
|
//end of shit
|
||||||
app.ticker.add(profileFPS);
|
app.ticker.add(profileFPS);
|
||||||
app.ticker.add(handleBuildingsIncome);
|
app.ticker.add(handleBuildingsIncome);
|
||||||
|
|
||||||
@ -107,8 +118,11 @@ export async function initGame() {
|
|||||||
createKeyboardBinding("KeyS", "Hold", [moveVertically]);
|
createKeyboardBinding("KeyS", "Hold", [moveVertically]);
|
||||||
createKeyboardBinding("KeyA", "Hold", [moveHorizontally]);
|
createKeyboardBinding("KeyA", "Hold", [moveHorizontally]);
|
||||||
createKeyboardBinding("KeyD", "Hold", [moveHorizontally]);
|
createKeyboardBinding("KeyD", "Hold", [moveHorizontally]);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let npccc;
|
||||||
|
|
||||||
function startGame() {
|
function startGame() {
|
||||||
setupGlobalInput();
|
setupGlobalInput();
|
||||||
setupInGameSelector();
|
setupInGameSelector();
|
||||||
@ -117,9 +131,23 @@ function startGame() {
|
|||||||
ambientDay.play();
|
ambientDay.play();
|
||||||
ambientNight.play();
|
ambientNight.play();
|
||||||
ambientMusic.play();
|
ambientMusic.play();
|
||||||
// addNPCToWorld(BC_CAMERA.position.x, BC_CAMERA.position.y, {type: "slave"});
|
|
||||||
BC_APP.ticker.add(gameStateObjectsCleaner);
|
BC_APP.ticker.add(gameStateObjectsCleaner);
|
||||||
BC_APP.ticker.add(tickHandler);
|
BC_APP.ticker.add(tickHandler);
|
||||||
|
|
||||||
createFirstWorldChunks();
|
createFirstWorldChunks();
|
||||||
|
|
||||||
|
let testNPCController = new NPCController();
|
||||||
|
let testNPC = new NPCProto(true, testNPCController);
|
||||||
|
testNPC.spriteSheetPath = "assets/images/characters/char0.png";
|
||||||
|
testNPC.frame = new PIXI.Rectangle(0, 0, 16, 16);
|
||||||
|
testNPC.controller = testNPCController;
|
||||||
|
testNPCController.controlledNPC = testNPC;
|
||||||
|
addGameObjectToGameState(testNPCController);
|
||||||
|
BC_CURRENT_SCENE.addObjectToSceneWithInitialization(testNPC);
|
||||||
|
testNPC.drawObject.zIndex = 4;
|
||||||
|
testNPC.drawObject.scale.set(BC_SPRITES_SETTINGS.scale, BC_SPRITES_SETTINGS.scale);
|
||||||
|
npccc = testNPCController;
|
||||||
|
let pf = new PathFinder();
|
||||||
|
let dest = pf.search(new PointInt2D(), new PointInt2D(10, 10));
|
||||||
|
testNPCController.moveTo(new Point2D(dest.worldPosition.getX(), dest.worldPosition.getY()));
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ export class GameObject {
|
|||||||
// markedAsInitialized = false;
|
// markedAsInitialized = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GameObject id
|
*
|
||||||
* @param {Boolean} tickAble
|
* @param {Boolean} tickAble
|
||||||
*/
|
*/
|
||||||
constructor(tickAble = false)
|
constructor(tickAble = false)
|
||||||
|
@ -17,6 +17,7 @@ export class GameScene {
|
|||||||
* @param {SceneObject} sceneObject
|
* @param {SceneObject} sceneObject
|
||||||
*/
|
*/
|
||||||
addObjectToScene(sceneObject) {
|
addObjectToScene(sceneObject) {
|
||||||
|
sceneObject.onSpawn();
|
||||||
this.#SceneObjects.set(sceneObject.minorId, sceneObject);
|
this.#SceneObjects.set(sceneObject.minorId, sceneObject);
|
||||||
BC_VIEWPORT.addChild(sceneObject.drawObject);
|
BC_VIEWPORT.addChild(sceneObject.drawObject);
|
||||||
};
|
};
|
||||||
|
@ -50,8 +50,6 @@ export function gameStateObjectsCleaner() {
|
|||||||
counter++;
|
counter++;
|
||||||
}
|
}
|
||||||
CleaningQueue = new Array();
|
CleaningQueue = new Array();
|
||||||
// console.log("removed: " + counter + " objects; "+"Total tick objects: " + TickGameObjects.size + "; Total objects: " + GameObjects.size);
|
|
||||||
// console.log(TickGameObjects);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -8,12 +8,10 @@ const _keyboard_current_states = {};
|
|||||||
|
|
||||||
window.addEventListener("keydown", (event) => {
|
window.addEventListener("keydown", (event) => {
|
||||||
_keyboard_current_states[event.code] = true;
|
_keyboard_current_states[event.code] = true;
|
||||||
// console.log(_keyboard_current_states);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
window.addEventListener("keyup", (event) => {
|
window.addEventListener("keyup", (event) => {
|
||||||
_keyboard_current_states[event.code] = false;
|
_keyboard_current_states[event.code] = false;
|
||||||
// console.log(_keyboard_current_states);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -69,9 +67,3 @@ export function inputControllerTick(deltaTime) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// export function _debugKeyboardStates()
|
|
||||||
// {
|
|
||||||
// console.log(_keyboard_current_states);
|
|
||||||
// console.log(_keyboard_controller_bindings);
|
|
||||||
// }
|
|
||||||
|
29
src/Game/NPC/NPCController/NPCController.js
Normal file
29
src/Game/NPC/NPCController/NPCController.js
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import { GameObject } from "../../GameObject/GameObject";
|
||||||
|
import { Point2D } from "../../Utils/Math.utils";
|
||||||
|
import { NPCProto } from "../NPCProto/NPCProto";
|
||||||
|
/**
|
||||||
|
* NPCController defines NPC behavior. Many NPC can have same NPCController for the same behavior.
|
||||||
|
*/
|
||||||
|
export class NPCController extends GameObject
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* NPC controlled by this controller
|
||||||
|
* @type NPCProto
|
||||||
|
*/
|
||||||
|
controlledNPC = null;
|
||||||
|
|
||||||
|
constructor(tickAble = false)
|
||||||
|
{
|
||||||
|
super(tickAble);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* moves NPC to position
|
||||||
|
* @param {Point2D} position
|
||||||
|
*/
|
||||||
|
moveTo(position)
|
||||||
|
{
|
||||||
|
this.controlledNPC.currentPosition = position;
|
||||||
|
this.controlledNPC.currentPosition = position;
|
||||||
|
}
|
||||||
|
};
|
60
src/Game/NPC/NPCProto/NPCProto.js
Normal file
60
src/Game/NPC/NPCProto/NPCProto.js
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
import { Rectangle } from "../../../pixi/pixi.mjs";
|
||||||
|
import { SceneObject } from "../../SceneObjects/SceneObject";
|
||||||
|
import { Point2D, interpolate } from "../../Utils/Math.utils";
|
||||||
|
import { getSpriteFromAtlas } from "../../Utils/Sprites.utils";
|
||||||
|
import { NPCController } from "../NPCController/NPCController";
|
||||||
|
|
||||||
|
export class NPCProto extends SceneObject
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* NPCController ref
|
||||||
|
* @type NPCController
|
||||||
|
*/
|
||||||
|
controller = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* NPC position in world
|
||||||
|
*/
|
||||||
|
currentPosition = new Point2D();
|
||||||
|
|
||||||
|
_positionInterpolationSpeed = 10;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* path to NPC spritesheet
|
||||||
|
*/
|
||||||
|
spriteSheetPath = "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* frame to cut from spritesheet
|
||||||
|
*/
|
||||||
|
frame = new Rectangle();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* creates new NPC object
|
||||||
|
* @param {Boolean} tickAble
|
||||||
|
* @param {NPCController} controller
|
||||||
|
*/
|
||||||
|
constructor(tickAble = true, controller)
|
||||||
|
{
|
||||||
|
super(tickAble);
|
||||||
|
this.controller = controller;
|
||||||
|
};
|
||||||
|
|
||||||
|
onInit()
|
||||||
|
{
|
||||||
|
super.onInit();
|
||||||
|
this.drawObject = getSpriteFromAtlas(this.spriteSheetPath, this.frame);
|
||||||
|
};
|
||||||
|
|
||||||
|
tick(ticker)
|
||||||
|
{
|
||||||
|
this._positionInterpolation(ticker.deltaMS / 1000 * this._positionInterpolationSpeed);
|
||||||
|
};
|
||||||
|
|
||||||
|
_positionInterpolation(delta)
|
||||||
|
{
|
||||||
|
let {x, y} = this.drawObject.position;
|
||||||
|
this.drawObject.position.set(interpolate(x, this.currentPosition.getX(), delta), interpolate(y, this.currentPosition.getY(), delta));
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
@ -1,5 +1,6 @@
|
|||||||
import { GameObject } from "../GameObject/GameObject";
|
import { GameObject } from "../GameObject/GameObject";
|
||||||
import { Container } from "../../pixi/pixi.mjs";
|
import { Container } from "../../pixi/pixi.mjs";
|
||||||
|
import { Point2D } from "../Utils/Math.utils";
|
||||||
|
|
||||||
export class SceneObject extends GameObject {
|
export class SceneObject extends GameObject {
|
||||||
/**
|
/**
|
||||||
@ -13,6 +14,15 @@ export class SceneObject extends GameObject {
|
|||||||
*/
|
*/
|
||||||
props = {};
|
props = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* world position for scene object
|
||||||
|
*/
|
||||||
|
worldPosition = new Point2D();
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {Boolean} tickAble
|
||||||
|
*/
|
||||||
constructor(tickAble)
|
constructor(tickAble)
|
||||||
{
|
{
|
||||||
super(tickAble);
|
super(tickAble);
|
||||||
|
@ -61,7 +61,6 @@ let nightAmbientSoundCue = new NumberCue(
|
|||||||
);
|
);
|
||||||
|
|
||||||
export function handleSounds(tick) {
|
export function handleSounds(tick) {
|
||||||
// console.log(tick);
|
|
||||||
let p = getDayPhase();
|
let p = getDayPhase();
|
||||||
ambientDay.volume(dayAmbientSoundCue.getValueAt(p) * globalSoundVolume * ambientSoundVolume * 0.6);
|
ambientDay.volume(dayAmbientSoundCue.getValueAt(p) * globalSoundVolume * ambientSoundVolume * 0.6);
|
||||||
ambientNight.volume(nightAmbientSoundCue.getValueAt(p) * globalSoundVolume * ambientSoundVolume * 0.6);
|
ambientNight.volume(nightAmbientSoundCue.getValueAt(p) * globalSoundVolume * ambientSoundVolume * 0.6);
|
||||||
|
@ -2,32 +2,86 @@ import { PRNG } from "../GlobalVariables/GlobalVariables";
|
|||||||
|
|
||||||
export class Point2D
|
export class Point2D
|
||||||
{
|
{
|
||||||
|
#x = 0;
|
||||||
|
#y = 0;
|
||||||
/**
|
/**
|
||||||
* Point2D object
|
* Point2D object
|
||||||
* @param {Number} x
|
* @param {Number} x
|
||||||
* @param {Number} y
|
* @param {Number} y
|
||||||
*/
|
*/
|
||||||
constructor(x = 0.0, y = 0.0)
|
constructor(x = 0, y = 0)
|
||||||
{
|
{
|
||||||
this._x = x;
|
this.#x = Math.floor(x);
|
||||||
this._y = y;
|
this.#y = Math.floor(y);
|
||||||
}
|
}
|
||||||
|
|
||||||
getX() {
|
getX() {
|
||||||
return this._x;
|
return this.#x;
|
||||||
};
|
};
|
||||||
|
|
||||||
setX(x) {
|
setX(x) {
|
||||||
this._x = x;
|
this.#x = x;
|
||||||
};
|
};
|
||||||
|
|
||||||
getY() {
|
getY() {
|
||||||
return this._y;
|
return this.#y;
|
||||||
};
|
};
|
||||||
|
|
||||||
setY(y) {
|
setY(y) {
|
||||||
this._y = y;
|
this.#y = y;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {Point2D} a
|
||||||
|
* @param {Point2D} b
|
||||||
|
*/
|
||||||
|
static isEqual(a, b)
|
||||||
|
{
|
||||||
|
return (a.getX() === b.getX() && a.getY() === b.getY());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PointInt2D
|
||||||
|
{
|
||||||
|
#x = 0;
|
||||||
|
#y = 0;
|
||||||
|
/**
|
||||||
|
* Point2D object
|
||||||
|
* @param {Number} x
|
||||||
|
* @param {Number} y
|
||||||
|
*/
|
||||||
|
constructor(x = 0, y = 0)
|
||||||
|
{
|
||||||
|
this.#x = x;
|
||||||
|
this.#y = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
getX() {
|
||||||
|
return this.#x;
|
||||||
|
};
|
||||||
|
|
||||||
|
setX(x) {
|
||||||
|
this.#x = Math.floor(x);
|
||||||
|
};
|
||||||
|
|
||||||
|
getY() {
|
||||||
|
return this.#y;
|
||||||
|
};
|
||||||
|
|
||||||
|
setY(y) {
|
||||||
|
this.#y = Math.floor(y);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {PointInt2D} a
|
||||||
|
* @param {PointInt2D} b
|
||||||
|
*/
|
||||||
|
static isEqual(a, b)
|
||||||
|
{
|
||||||
|
return (a.getX() === b.getX() && a.getY() === b.getY());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
95
src/Game/Utils/PathFinding.util.js
Normal file
95
src/Game/Utils/PathFinding.util.js
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
import { BC_TERRAIN_SETTINGS } from "../GlobalVariables/GlobalVariables";
|
||||||
|
import { ChunkStorageTypes } from "../WorldGeneration/WorldChunk/WorldGenChunk";
|
||||||
|
import { getTileAt } from "../WorldGeneration/WorldGen";
|
||||||
|
import { PointInt2D } from "./Math.utils";
|
||||||
|
import { TerrainTile } from "../WorldGeneration/WorldObjects/TerrainTile/TerrainTile";
|
||||||
|
|
||||||
|
class PathFinderNode
|
||||||
|
{
|
||||||
|
position;
|
||||||
|
fScore;
|
||||||
|
neighbors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {PointInt2D} position
|
||||||
|
* @param {Number} fScore
|
||||||
|
*/
|
||||||
|
constructor(position, fScore)
|
||||||
|
{
|
||||||
|
this.position = position;
|
||||||
|
this.fScore = fScore;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export class PathFinder
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @type Array<PathFinderNode>
|
||||||
|
*/
|
||||||
|
_openSet = new Array();
|
||||||
|
_closedSet = new Array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {PointInt2D} start
|
||||||
|
* @param {PointInt2D} goal
|
||||||
|
*/
|
||||||
|
search(start, goal)
|
||||||
|
{
|
||||||
|
this._openSet.push(new PathFinderNode(start, 0));
|
||||||
|
|
||||||
|
//code
|
||||||
|
|
||||||
|
let currentNode = new PathFinderNode(new PointInt2D(), 0);
|
||||||
|
/**
|
||||||
|
* @type Map<String, Number>
|
||||||
|
*/
|
||||||
|
let gScores = new Map();
|
||||||
|
/**
|
||||||
|
* @type Map<String, Number>
|
||||||
|
*/
|
||||||
|
let fScores = new Map();
|
||||||
|
|
||||||
|
let minFScoreNodeIndex = 0;
|
||||||
|
//START LOOP WOOOOAAAW
|
||||||
|
while (this._openSet.length > 0) {
|
||||||
|
//find node with min f score
|
||||||
|
//better to rewrite it to priority queue
|
||||||
|
for (let i = 0; i < this._openSet.length; i++) {
|
||||||
|
if(this._openSet[i] < this._openSet[minFScoreNodeIndex])
|
||||||
|
minFScoreNodeIndex = i;
|
||||||
|
}
|
||||||
|
//wow! node is found and set!!
|
||||||
|
currentNode = this._openSet[minFScoreNodeIndex];
|
||||||
|
|
||||||
|
if(PointInt2D.isEqual(currentNode.position, goal))
|
||||||
|
{
|
||||||
|
//wow!!! we have found an end of the path!!! this is so cool!!!
|
||||||
|
return 1111; //return something weird stuff
|
||||||
|
}
|
||||||
|
|
||||||
|
//and now we must delete this node... what a sad situation...
|
||||||
|
this._openSet.splice(minFScoreNodeIndex, 1);
|
||||||
|
//but wait! we add it to closed set! nice!!! (why we are doing this??? idk... okay)
|
||||||
|
this._closedSet.push(currentNode);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type TerrainTile
|
||||||
|
*/
|
||||||
|
let neighbors = this._getNeighbors(currentNode.position);
|
||||||
|
return neighbors;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {PointInt2D} root
|
||||||
|
*/
|
||||||
|
_getNeighbors(root)
|
||||||
|
{
|
||||||
|
let neighborNorth = getTileAt(root.getX(), root.getY() - BC_TERRAIN_SETTINGS.tileSize*BC_TERRAIN_SETTINGS.scale, ChunkStorageTypes.TYPE_TERRAIN);
|
||||||
|
return neighborNorth;
|
||||||
|
//TODO
|
||||||
|
}
|
||||||
|
};
|
@ -40,11 +40,6 @@ export function handleDayNightCycle(tick) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
currentColor = dayColorsCue.getColorAt(getDayPhase());
|
currentColor = dayColorsCue.getColorAt(getDayPhase());
|
||||||
// currentColor = dayColor.multiplyByNumber(t);
|
|
||||||
// console.log(((Math.floor(timeElapsed) % (gameSecondsInGameMinute * gameMinutesInGameHour * gameHoursInGameDay)) / (gameSecondsInGameMinute * gameMinutesInGameHour * gameHoursInGameDay)));
|
|
||||||
// console.log(currentColor.toHex());
|
|
||||||
// console.log(((Math.floor(timeElapsed) % (gameSecondsInGameMinute * gameMinutesInGameHour * gameHoursInGameDay)) / (gameSecondsInGameMinute * gameMinutesInGameHour * gameHoursInGameDay)));
|
|
||||||
// currentColor.multiplyByNumber(((Math.floor(timeElapsed) % (gameSecondsInGameMinute * gameMinutesInGameHour * gameHoursInGameDay)) / (gameSecondsInGameMinute * gameMinutesInGameHour * gameHoursInGameDay)));
|
|
||||||
BC_VIEWPORT.tint = currentColor.toNumber();
|
BC_VIEWPORT.tint = currentColor.toNumber();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { Container } from "../../../pixi/pixi.mjs";
|
import { Container } from "../../../pixi/pixi.mjs";
|
||||||
import { SceneObject } from "../../SceneObjects/SceneObject";
|
import { SceneObject } from "../../SceneObjects/SceneObject";
|
||||||
import { worldCoordinatesToChunkLocalCoordinates } from "../WorldGen";
|
import { worldCoordinatesToChunkIndexesCoordinates, worldCoordinatesToChunkLocalCoordinates } from "../WorldGen";
|
||||||
import { TerrainTile } from "../WorldObjects/TerrainTile/TerrainTile";
|
import { TerrainTile } from "../WorldObjects/TerrainTile/TerrainTile";
|
||||||
import { VegetationTile } from "../WorldObjects/VegetationTile/VegetationTile";
|
import { VegetationTile } from "../WorldObjects/VegetationTile/VegetationTile";
|
||||||
|
|
||||||
@ -26,6 +26,10 @@ export class WorldChunk extends SceneObject
|
|||||||
* @type Map<String, BuildingTile>
|
* @type Map<String, BuildingTile>
|
||||||
*/
|
*/
|
||||||
buildingsStorage = new Map();
|
buildingsStorage = new Map();
|
||||||
|
/**
|
||||||
|
* @type Map<String, any>
|
||||||
|
*/
|
||||||
|
npcStorage = new Map();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* property to handle chunk visibility
|
* property to handle chunk visibility
|
||||||
|
@ -29,7 +29,6 @@ export class WorldChunksVisibilityUpdater {
|
|||||||
* Ticker function
|
* Ticker function
|
||||||
*/
|
*/
|
||||||
static handleWorldChunkFilling() {
|
static handleWorldChunkFilling() {
|
||||||
// console.log(tick.deltaMS);
|
|
||||||
if (this._currentIndex < 0) return;
|
if (this._currentIndex < 0) return;
|
||||||
fillWorldGenChunk(
|
fillWorldGenChunk(
|
||||||
this._chunksFillingQueue[this._currentIndex].chunk,
|
this._chunksFillingQueue[this._currentIndex].chunk,
|
||||||
@ -50,7 +49,6 @@ export class WorldChunksVisibilityUpdater {
|
|||||||
let cx = Math.floor(BC_CAMERA.position.x / w);
|
let cx = Math.floor(BC_CAMERA.position.x / w);
|
||||||
let cy = Math.floor(BC_CAMERA.position.y / h);
|
let cy = Math.floor(BC_CAMERA.position.y / h);
|
||||||
let chunkId = cx + "_" + cy;
|
let chunkId = cx + "_" + cy;
|
||||||
// console.log(this._visibleChunks);
|
|
||||||
if ((this._visibleChunks.has(chunkId) && !this._visibleChunks.get(chunkId).centralChunk) || !this._visibleChunks.has(chunkId)) {
|
if ((this._visibleChunks.has(chunkId) && !this._visibleChunks.get(chunkId).centralChunk) || !this._visibleChunks.has(chunkId)) {
|
||||||
for (const visChunk of this._visibleChunks) {
|
for (const visChunk of this._visibleChunks) {
|
||||||
visChunk[1].drawObject.visible = false;
|
visChunk[1].drawObject.visible = false;
|
||||||
@ -65,7 +63,6 @@ export class WorldChunksVisibilityUpdater {
|
|||||||
this._visibleChunks.set(t_chunkId, wChunk);
|
this._visibleChunks.set(t_chunkId, wChunk);
|
||||||
wChunk.drawObject.visible = true;
|
wChunk.drawObject.visible = true;
|
||||||
} else if (this.enableAutoWorldChunksGeneration) {
|
} else if (this.enableAutoWorldChunksGeneration) {
|
||||||
// console.log(t_chunkId);
|
|
||||||
let newChunk = new WorldChunk(false);
|
let newChunk = new WorldChunk(false);
|
||||||
BC_CURRENT_SCENE.addObjectToSceneWithInitialization(newChunk);
|
BC_CURRENT_SCENE.addObjectToSceneWithInitialization(newChunk);
|
||||||
newChunk.drawObject.position.set(w * (cx + i), h * (cy + j));
|
newChunk.drawObject.position.set(w * (cx + i), h * (cy + j));
|
||||||
|
@ -4,7 +4,7 @@ import { BC_CAMERA, BC_CHUNKS_SETTINGS, BC_CURRENT_SCENE, BC_SPRITES_SETTINGS, B
|
|||||||
import { NumberCue, RGBColor } from "../Utils/DataTypes.utils";
|
import { NumberCue, RGBColor } from "../Utils/DataTypes.utils";
|
||||||
import { ChunkStorageTypes, WorldChunk } from "./WorldChunk/WorldGenChunk";
|
import { ChunkStorageTypes, WorldChunk } from "./WorldChunk/WorldGenChunk";
|
||||||
import { TerrainTile, TerrainTileProps } from "./WorldObjects/TerrainTile/TerrainTile";
|
import { TerrainTile, TerrainTileProps } from "./WorldObjects/TerrainTile/TerrainTile";
|
||||||
import { clampNumber } from "../Utils/Math.utils";
|
import { Point2D, clampNumber } from "../Utils/Math.utils";
|
||||||
import { addGameObjectToGameState } from "../GameState/GameState";
|
import { addGameObjectToGameState } from "../GameState/GameState";
|
||||||
import { VegetationTile, VegetationTileProps } from "./WorldObjects/VegetationTile/VegetationTile";
|
import { VegetationTile, VegetationTileProps } from "./WorldObjects/VegetationTile/VegetationTile";
|
||||||
import { WorldChunksVisibilityUpdater } from "./WorldChunksVisibilityUpdater/WorldChunksVisibilityUpdater";
|
import { WorldChunksVisibilityUpdater } from "./WorldChunksVisibilityUpdater/WorldChunksVisibilityUpdater";
|
||||||
@ -28,6 +28,12 @@ const terrainTypeList = {
|
|||||||
2: "ter_grass", //grass
|
2: "ter_grass", //grass
|
||||||
3: "ter_stone", //stone
|
3: "ter_stone", //stone
|
||||||
};
|
};
|
||||||
|
const terrainNavigationCostList = {
|
||||||
|
0: 1000, //water
|
||||||
|
1: 5, //sand
|
||||||
|
2: 3, //grass
|
||||||
|
3: 2, //stone
|
||||||
|
};
|
||||||
const grassVegetationSpriteList = {
|
const grassVegetationSpriteList = {
|
||||||
0: { x: 10, y: 11 },
|
0: { x: 10, y: 11 },
|
||||||
1: { x: 11, y: 11 },
|
1: { x: 11, y: 11 },
|
||||||
@ -79,29 +85,34 @@ export function worldCoordinatesToChunkIndexesCoordinates(x, y) {
|
|||||||
let hs = BC_TERRAIN_SETTINGS.tileSize * BC_TERRAIN_SETTINGS.scale;
|
let hs = BC_TERRAIN_SETTINGS.tileSize * BC_TERRAIN_SETTINGS.scale;
|
||||||
return { x: Math.floor(x / ws), y: Math.floor(y / hs) };
|
return { x: Math.floor(x / ws), y: Math.floor(y / hs) };
|
||||||
}
|
}
|
||||||
|
|
||||||
export function worldCoordinatesToChunkLocalCoordinates(x, y) {
|
export function worldCoordinatesToChunkLocalCoordinates(x, y) {
|
||||||
let ws = BC_TERRAIN_SETTINGS.tileSize * BC_TERRAIN_SETTINGS.scale;
|
let ws = BC_TERRAIN_SETTINGS.tileSize * BC_TERRAIN_SETTINGS.scale;
|
||||||
let hs = BC_TERRAIN_SETTINGS.tileSize * BC_TERRAIN_SETTINGS.scale;
|
let hs = BC_TERRAIN_SETTINGS.tileSize * BC_TERRAIN_SETTINGS.scale;
|
||||||
if (x < 0 && y >= 0)
|
if (x < 0 && y >= 0)
|
||||||
|
{
|
||||||
return {
|
return {
|
||||||
x: BC_CHUNKS_SETTINGS.width - (Math.floor(Math.abs(x) / ws) % BC_CHUNKS_SETTINGS.width) - 1,
|
x: (BC_CHUNKS_SETTINGS.width - (Math.ceil(Math.abs(x) / ws) % BC_CHUNKS_SETTINGS.width)) % BC_CHUNKS_SETTINGS.width,
|
||||||
y: Math.floor(Math.abs(y) / hs) % BC_CHUNKS_SETTINGS.height,
|
y: Math.floor(Math.abs(y) / hs) % BC_CHUNKS_SETTINGS.height,
|
||||||
};
|
};
|
||||||
|
}
|
||||||
else if (x < 0 && y < 0)
|
else if (x < 0 && y < 0)
|
||||||
return {
|
return {
|
||||||
x: BC_CHUNKS_SETTINGS.width - (Math.floor(Math.abs(x) / ws) % BC_CHUNKS_SETTINGS.width) - 1,
|
x: (BC_CHUNKS_SETTINGS.width - (Math.ceil(Math.abs(x) / ws) % BC_CHUNKS_SETTINGS.width)) % BC_CHUNKS_SETTINGS.width,
|
||||||
y: BC_CHUNKS_SETTINGS.height - (Math.floor(Math.abs(y) / hs) % BC_CHUNKS_SETTINGS.height) - 1,
|
y: (BC_CHUNKS_SETTINGS.height - (Math.ceil(Math.abs(y) / hs) % BC_CHUNKS_SETTINGS.height)) % BC_CHUNKS_SETTINGS.height,
|
||||||
};
|
};
|
||||||
else if (x > 0 && y < 0)
|
else if (x >= 0 && y < 0)
|
||||||
return {
|
return {
|
||||||
x: Math.floor(Math.abs(x) / ws) % BC_CHUNKS_SETTINGS.width,
|
x: Math.floor(Math.abs(x) / ws) % BC_CHUNKS_SETTINGS.width,
|
||||||
y: BC_CHUNKS_SETTINGS.height - (Math.floor(Math.abs(y) / hs) % BC_CHUNKS_SETTINGS.height) - 1,
|
y: (BC_CHUNKS_SETTINGS.height - (Math.ceil(Math.abs(y) / hs) % BC_CHUNKS_SETTINGS.height)) % BC_CHUNKS_SETTINGS.height,
|
||||||
};
|
};
|
||||||
else
|
else
|
||||||
|
{
|
||||||
return {
|
return {
|
||||||
x: Math.floor(Math.abs(x) / ws) % BC_CHUNKS_SETTINGS.width,
|
x: Math.floor(Math.abs(x) / ws) % BC_CHUNKS_SETTINGS.width,
|
||||||
y: Math.floor(Math.abs(y) / hs) % BC_CHUNKS_SETTINGS.height,
|
y: Math.floor(Math.abs(y) / hs) % BC_CHUNKS_SETTINGS.height,
|
||||||
};
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getWorldChunkAt(xWorld, yWorld) {
|
export function getWorldChunkAt(xWorld, yWorld) {
|
||||||
@ -113,6 +124,20 @@ export function getWorldChunkById(id) {
|
|||||||
return WorldChunksStorage.get(id);
|
return WorldChunksStorage.get(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {Number} xWorld
|
||||||
|
* @param {Number} yWorld
|
||||||
|
* @param {ChunkStorageTypes} storageType
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function getTileAt(xWorld, yWorld, storageType) {
|
||||||
|
let t = worldCoordinatesToChunkIndex(xWorld, yWorld);
|
||||||
|
let c = WorldChunksStorage.get(t.x+"_"+t.y);
|
||||||
|
if(!c) return undefined;
|
||||||
|
return c.getFromChunk(xWorld, yWorld, storageType);
|
||||||
|
}
|
||||||
|
|
||||||
export function worldChunkExists(id) {
|
export function worldChunkExists(id) {
|
||||||
return WorldChunksStorage.has(id);
|
return WorldChunksStorage.has(id);
|
||||||
}
|
}
|
||||||
@ -154,10 +179,10 @@ export function fillWorldGenChunk(chunk, x, y) {
|
|||||||
res = Math.floor(terrainCue.getValueAt(res));
|
res = Math.floor(terrainCue.getValueAt(res));
|
||||||
|
|
||||||
let terrainTile = new TerrainTile(false);
|
let terrainTile = new TerrainTile(false);
|
||||||
// console.log(terrainTile.isTickEnabled());
|
|
||||||
terrainTile.spriteSheetPath = "assets/images/world/world_terrain_atlas.png";
|
terrainTile.spriteSheetPath = "assets/images/world/world_terrain_atlas.png";
|
||||||
terrainTile.frame = new Rectangle(terrainSpriteList[res].x, terrainSpriteList[res].y, 16, 16);
|
terrainTile.frame = new Rectangle(terrainSpriteList[res].x, terrainSpriteList[res].y, 16, 16);
|
||||||
terrainTile.props = new TerrainTileProps(terrainTypeList[res], res*5);
|
terrainTile.props = new TerrainTileProps(terrainTypeList[res], res*5, terrainNavigationCostList[res]);
|
||||||
|
terrainTile.worldPosition = new Point2D(i * BC_TERRAIN_SETTINGS.scale * BC_TERRAIN_SETTINGS.tileSize, j * BC_TERRAIN_SETTINGS.scale * BC_TERRAIN_SETTINGS.tileSize);
|
||||||
|
|
||||||
addGameObjectToGameState(terrainTile);
|
addGameObjectToGameState(terrainTile);
|
||||||
|
|
||||||
@ -166,16 +191,16 @@ export function fillWorldGenChunk(chunk, x, y) {
|
|||||||
terrainTile.drawObject.position.set(16 * BC_SPRITES_SETTINGS.scale * ii, 16 * BC_SPRITES_SETTINGS.scale * jj);
|
terrainTile.drawObject.position.set(16 * BC_SPRITES_SETTINGS.scale * ii, 16 * BC_SPRITES_SETTINGS.scale * jj);
|
||||||
terrainTile.drawObject.scale.set(BC_SPRITES_SETTINGS.scale, BC_SPRITES_SETTINGS.scale);
|
terrainTile.drawObject.scale.set(BC_SPRITES_SETTINGS.scale, BC_SPRITES_SETTINGS.scale);
|
||||||
|
|
||||||
chunk.addToChunk(terrainTile, ChunkStorageTypes.TYPE_TERRAIN, i+"_"+j);
|
chunk.addToChunk(terrainTile, ChunkStorageTypes.TYPE_TERRAIN, ii+"_"+jj);
|
||||||
|
|
||||||
if (res === 2 && PRNG() > 0.9) {
|
if (res === 2 && PRNG() > 0.9) {
|
||||||
let rv = Math.floor(PRNG() * 18);
|
let rv = Math.floor(PRNG() * 18);
|
||||||
|
|
||||||
let vegetationTile = new VegetationTile(false);
|
let vegetationTile = new VegetationTile(false);
|
||||||
// console.log(vegetationTile.isTickEnabled());
|
|
||||||
vegetationTile.spriteSheetPath = "assets/images/world/vegetation_ts.png";
|
vegetationTile.spriteSheetPath = "assets/images/world/vegetation_ts.png";
|
||||||
vegetationTile.frame = new Rectangle(16 * grassVegetationSpriteList[rv].x, 16 * grassVegetationSpriteList[rv].y, 16, 16);
|
vegetationTile.frame = new Rectangle(16 * grassVegetationSpriteList[rv].x, 16 * grassVegetationSpriteList[rv].y, 16, 16);
|
||||||
vegetationTile.props = new VegetationTileProps("vegetation", grassVegResourcesList[rv]);
|
vegetationTile.props = new VegetationTileProps("vegetation", grassVegResourcesList[rv]);
|
||||||
|
vegetationTile.worldPosition = new Point2D(i * BC_TERRAIN_SETTINGS.scale * BC_TERRAIN_SETTINGS.tileSize, j * BC_TERRAIN_SETTINGS.scale * BC_TERRAIN_SETTINGS.tileSize);
|
||||||
|
|
||||||
addGameObjectToGameState(vegetationTile);
|
addGameObjectToGameState(vegetationTile);
|
||||||
|
|
||||||
@ -184,7 +209,7 @@ export function fillWorldGenChunk(chunk, x, y) {
|
|||||||
vegetationTile.drawObject.position.set(16 * BC_SPRITES_SETTINGS.scale * ii, 16 * BC_SPRITES_SETTINGS.scale * jj);
|
vegetationTile.drawObject.position.set(16 * BC_SPRITES_SETTINGS.scale * ii, 16 * BC_SPRITES_SETTINGS.scale * jj);
|
||||||
vegetationTile.drawObject.scale.set(BC_SPRITES_SETTINGS.scale, BC_SPRITES_SETTINGS.scale);
|
vegetationTile.drawObject.scale.set(BC_SPRITES_SETTINGS.scale, BC_SPRITES_SETTINGS.scale);
|
||||||
|
|
||||||
chunk.addToChunk(vegetationTile, ChunkStorageTypes.TYPE_VEGETATION, i+"_"+j);
|
chunk.addToChunk(vegetationTile, ChunkStorageTypes.TYPE_VEGETATION, ii+"_"+jj);
|
||||||
|
|
||||||
jj++;
|
jj++;
|
||||||
continue;
|
continue;
|
||||||
@ -206,33 +231,11 @@ export function createFirstWorldChunks() {
|
|||||||
|
|
||||||
let chunk = new WorldChunk(false);
|
let chunk = new WorldChunk(false);
|
||||||
chunk.drawObject.position.set(w * chunkXCeiled, h * chunkYCeiled);
|
chunk.drawObject.position.set(w * chunkXCeiled, h * chunkYCeiled);
|
||||||
|
chunk.props = {id: chunkId};
|
||||||
BC_CURRENT_SCENE.addObjectToSceneWithInitialization(chunk);
|
BC_CURRENT_SCENE.addObjectToSceneWithInitialization(chunk);
|
||||||
|
|
||||||
fillWorldGenChunk(chunk, chunkXCeiled, chunkYCeiled);
|
fillWorldGenChunk(chunk, chunkXCeiled, chunkYCeiled);
|
||||||
WorldChunksStorage.set(chunkId, chunk);
|
WorldChunksStorage.set(chunkId, chunk);
|
||||||
// console.log(chunkId);
|
|
||||||
// let chunkRef = createWorldChunkContainer();
|
|
||||||
// chunkRef.isRenderGroup = true;
|
|
||||||
// chunkRef.position.set(w * chunkXCeiled, h * chunkYCeiled);
|
|
||||||
// console.log(w * chunkXCeiled, h * chunkYCeiled);
|
|
||||||
// let chunk0 = new WorldChunk(
|
|
||||||
// chunkRef,
|
|
||||||
// chunkRef.getChildAt(0),
|
|
||||||
// chunkRef.getChildAt(1),
|
|
||||||
// chunkRef.getChildAt(2),
|
|
||||||
// // chunkRef.getChildAt(3),
|
|
||||||
// new Vault("terrain"),
|
|
||||||
// new Vault("vegetation"),
|
|
||||||
// new Vault("buildings")
|
|
||||||
// );
|
|
||||||
|
|
||||||
// fillChunk(chunk0, chunkXCeiled, chunkYCeiled);
|
|
||||||
// WORLD_CHUNKS.set(chunkId, chunk0);
|
|
||||||
// chunkRef.visible = false;
|
|
||||||
// addToViewport(chunkRef);
|
|
||||||
// console.log(WORLD_CHUNKS)
|
|
||||||
// console.log(chunkXCeiled, chunkYCeiled);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
WorldChunksVisibilityUpdater.enableAutoWorldChunksGeneration = true;
|
WorldChunksVisibilityUpdater.enableAutoWorldChunksGeneration = true;
|
||||||
|
@ -8,20 +8,28 @@ export class TerrainTileProps
|
|||||||
* @type String
|
* @type String
|
||||||
*/
|
*/
|
||||||
type;
|
type;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type Number
|
* @type Number
|
||||||
*/
|
*/
|
||||||
temperature;
|
temperature;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type Number
|
||||||
|
*/
|
||||||
|
navigationCost = 1000;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {String} type
|
* @param {String} type
|
||||||
* @param {Number} temperature
|
* @param {Number} temperature
|
||||||
|
* @param {Number} navigationCost
|
||||||
*/
|
*/
|
||||||
constructor(type = "", temperature = 0)
|
constructor(type = "", temperature = 0, navigationCost = 0)
|
||||||
{
|
{
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.temperature = temperature;
|
this.temperature = temperature;
|
||||||
|
this.navigationCost = navigationCost;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -131,5 +131,3 @@ export function search() {
|
|||||||
//no solution by default
|
//no solution by default
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
// console.log(search());
|
|
Loading…
x
Reference in New Issue
Block a user