This commit is contained in:
2026-03-27 10:32:53 -03:00
parent 188b7a4372
commit 582e5fc34c
8 changed files with 163 additions and 127 deletions
+40 -24
View File
@@ -2,6 +2,7 @@ import { BlockRegistry, EverythingRegistry } from "$/common/everything_registry.
import { Container, Inventory, ItemStack } from "../inventory.ts"; import { Container, Inventory, ItemStack } from "../inventory.ts";
import { PlayerComponent } from "../player.ts"; import { PlayerComponent } from "../player.ts";
import { GuiFurnace } from "$/client/gui/gui_furnace.ts"; import { GuiFurnace } from "$/client/gui/gui_furnace.ts";
import { register_block_item } from "../../common/utils.ts";
function can_craft(container: Container) { function can_craft(container: Container) {
const input = container.get_item(0); const input = container.get_item(0);
@@ -66,50 +67,65 @@ interface TileChestData {
fuel: number; fuel: number;
} }
EverythingRegistry.register<BlockRegistry<TileChestData>>("blocks", "bworld:furnace", { const block = EverythingRegistry.register<BlockRegistry>("blocks", "bworld:furnace", {
textures: "bworld:furnace", id: "bworld:furnace",
has_collision: false, textures: { front: "bworld:furnace", side: "bworld:stone" },
has_collision: true,
drop_table: "bworld:furnace",
on_interact(world, tile) { on_interact(dimension, block) {
const [player] = world.get_tag("player")!; const [player] = dimension.world.get_tag("player")!;
const player_component = player.get(PlayerComponent)!; const player_component = player.get(PlayerComponent)!;
if (tile.data && tile.data.inventory) { const block_data = dimension.get_block_data<TileChestData>(block.x, block.y, block.z);
const gui = new GuiFurnace(tile.data.inventory, player_component.player_inventory, tile.data); if (block_data && block_data.data.inventory) {
const gui = new GuiFurnace(block_data.data.inventory, player_component.player_inventory, block_data.data);
player_component.screens.push(gui); player_component.screens.push(gui);
} }
}, },
on_create(_, tile) { on_create(dimension, block) {
tile.data = { dimension.add_block_data({
inventory: new Inventory(new Container(3)), id: block.id,
progress: 0, x: block.x,
fuel: 0, y: block.y,
}; z: block.z,
data: {
inventory: new Inventory(new Container(3)),
progress: 0,
fuel: 0,
},
});
}, },
on_tick(_, tile) { on_break() {
if (!tile.data) { // TODO: remove block data
},
on_tick(dimension, block) {
const block_data = dimension.get_block_data<TileChestData>(block.x, block.y, block.z);
if (!block_data) {
return; return;
} }
if (tile.data.fuel > 0) { if (block_data.data.fuel > 0) {
tile.data.fuel -= 1; block_data.data.fuel -= 1;
} }
const container = tile.data.inventory.container; const container = block_data.data.inventory.container;
if (can_craft(container)) { if (can_craft(container)) {
if (tile.data.fuel === 0 && has_fuel(container)) { if (block_data.data.fuel === 0 && has_fuel(container)) {
consume_fuel(container); consume_fuel(container);
tile.data.fuel = 200; block_data.data.fuel = 200;
} }
if (tile.data.fuel > 0) { if (block_data.data.fuel > 0) {
tile.data.progress += 1; block_data.data.progress += 1;
if (tile.data.progress >= 100) { if (block_data.data.progress >= 100) {
tile.data.progress = 0; block_data.data.progress = 0;
craft(container, new ItemStack("bworld:coal")); craft(container, new ItemStack("bworld:coal"));
} }
} }
} }
}, },
}); });
register_block_item(block);
+3 -3
View File
@@ -2,11 +2,11 @@ import { BlockRegistry, EverythingRegistry } from "$/common/everything_registry.
import { PlayerComponent } from "../player.ts"; import { PlayerComponent } from "../player.ts";
EverythingRegistry.register<BlockRegistry>("blocks", "bworld:grass", { EverythingRegistry.register<BlockRegistry>("blocks", "bworld:grass", {
id: "bworld:grass",
textures: { top: "bworld:grass_top", bottom: "bworld:dirt", "side": "bworld:grass_side" }, textures: { top: "bworld:grass_top", bottom: "bworld:dirt", "side": "bworld:grass_side" },
has_collision: false, has_collision: false,
drop_table: "bworld:dirt", drop_table: "bworld:dirt",
/*on_interact(world, tile) {
on_interact(world, tile) {
const [player] = world.get_tag("player")!; const [player] = world.get_tag("player")!;
const player_inventory = player.get(PlayerComponent)!.player_inventory; const player_inventory = player.get(PlayerComponent)!.player_inventory;
const item = player_inventory.container.get_item(player_inventory.hotbar_selected); const item = player_inventory.container.get_item(player_inventory.hotbar_selected);
@@ -17,5 +17,5 @@ EverythingRegistry.register<BlockRegistry>("blocks", "bworld:grass", {
} else if (item?.type_id === "bworld:furnace") { } else if (item?.type_id === "bworld:furnace") {
// world.dimension.add_block(world, { x: tile.x, y: tile.y, id: "bworld:furnace", tickable: true }); // world.dimension.add_block(world, { x: tile.x, y: tile.y, id: "bworld:furnace", tickable: true });
} }
}, },*/
}); });
+47 -7
View File
@@ -9,13 +9,19 @@ import { PlayerComponent } from "../player.ts";
import { gl, Texture } from "../renderer/mod.ts"; import { gl, Texture } from "../renderer/mod.ts";
import { Camera } from "./camera.ts"; import { Camera } from "./camera.ts";
export interface Block<T = unknown> { export interface BlockData<T = unknown> {
id: string;
x: number; x: number;
y: number; y: number;
z: number; z: number;
data: T;
}
export interface Block {
id: string; id: string;
data?: T; x: number;
tickable?: boolean; y: number;
z: number;
} }
export const CHUNK_SIZE = 16; export const CHUNK_SIZE = 16;
@@ -26,6 +32,7 @@ export interface Chunk {
x: number; x: number;
z: number; z: number;
blocks: Int16Array; blocks: Int16Array;
blocks_data: BlockData[];
generated: boolean; generated: boolean;
dirty: boolean; dirty: boolean;
opaque_vertex_buffer?: WebGLBuffer; opaque_vertex_buffer?: WebGLBuffer;
@@ -47,7 +54,14 @@ export class Dimension extends Component {
} }
add_chunk(x: number, z: number) { add_chunk(x: number, z: number) {
const chunk = { x, z, blocks: new Int16Array(CHUNK_AREA * CHUNK_HEIGHT), dirty: true, generated: false }; const chunk = {
x,
z,
blocks: new Int16Array(CHUNK_AREA * CHUNK_HEIGHT),
blocks_data: [],
dirty: true,
generated: false,
};
this.chunks.push(chunk); this.chunks.push(chunk);
return chunk; return chunk;
} }
@@ -98,6 +112,32 @@ export class Dimension extends Component {
return chunk.blocks[index]; return chunk.blocks[index];
} }
add_block_data(block_data: BlockData) {
const block_chunk_x = Math.floor(block_data.x / CHUNK_SIZE);
const block_chunk_z = Math.floor(block_data.z / CHUNK_SIZE);
let chunk = this.get_chunk(block_chunk_x, block_chunk_z);
if (!chunk) {
chunk = this.add_chunk(block_chunk_x, block_chunk_z);
}
chunk.blocks_data.push(block_data);
}
get_block_data<T>(x: number, y: number, z: number) {
const chunk_x = Math.floor(x / CHUNK_SIZE);
const chunk_z = Math.floor(z / CHUNK_SIZE);
const chunk = this.get_chunk(chunk_x, chunk_z);
if (!chunk) {
return undefined;
}
return chunk.blocks_data.find((block_data) =>
block_data.x === x && block_data.y === y && block_data.z === z
) as BlockData<T>;
}
break_block(x: number, y: number, z: number) { break_block(x: number, y: number, z: number) {
const block_chunk_x = Math.floor(x / CHUNK_SIZE); const block_chunk_x = Math.floor(x / CHUNK_SIZE);
const block_chunk_z = Math.floor(z / CHUNK_SIZE); const block_chunk_z = Math.floor(z / CHUNK_SIZE);
@@ -145,9 +185,9 @@ export class Dimension extends Component {
} }
} }
// if (block_info?.on_create) { if (block_info?.on_break) {
// block_info?.on_create(this, block); block_info.on_break(this, { x, y, z, id: block_info.id });
// } }
} }
index_to_xyz(index: number) { index_to_xyz(index: number) {
+10 -1
View File
@@ -44,7 +44,16 @@ export class ContainerSlot {
return this.#item_stack?.type_id; return this.#item_stack?.type_id;
} }
get amount() { set amount(new_amount: number) {
if (this.#item_stack) {
this.#item_stack.amount = new_amount;
if (this.#item_stack.amount <= 0) {
this.#item_stack = undefined;
}
}
}
get amount(): number | undefined {
return this.#item_stack?.amount; return this.#item_stack?.amount;
} }
+14 -68
View File
@@ -1,18 +1,12 @@
import { System } from "$/common/ecs/mod.ts"; import { System } from "$/common/ecs/mod.ts";
import { ClientWorld } from "../client_world.ts"; import { ClientWorld } from "../client_world.ts";
import { Dimension } from "../components/dimension.ts"; import { Dimension } from "../components/dimension.ts";
import { Camera, screen_to_world } from "../components/camera.ts"; import { TICK_DELTA } from "$/common/constants.ts";
import { InputManager } from "../input_manager.ts";
import { canvas } from "../renderer/mod.ts";
import { TICK_DELTA, TILE_SIZE } from "$/common/constants.ts";
import { BlockRegistry, EverythingRegistry } from "$/common/everything_registry.ts"; import { BlockRegistry, EverythingRegistry } from "$/common/everything_registry.ts";
import { Position } from "$/common/components/position.ts";
import { distance_point_rectangle } from "../../common/utils.ts";
export class DimensionLogicSystem extends System { export class DimensionLogicSystem extends System {
update(world: ClientWorld, delta: number): void { update(world: ClientWorld, delta: number): void {
/*const dimension_entity = world.get_entities().values().find((ent) => ent.get(Dimension)); const dimension = world.dimension;
const dimension = dimension_entity!.get(Dimension)!;
dimension.second_timer += delta; dimension.second_timer += delta;
dimension.tick_timer += delta; dimension.tick_timer += delta;
@@ -23,74 +17,26 @@ export class DimensionLogicSystem extends System {
if (dimension.tick_timer >= TICK_DELTA) { if (dimension.tick_timer >= TICK_DELTA) {
this.handle_tick(world, dimension); this.handle_tick(world, dimension);
} }
const [player] = world.get_tag("player")!;
const player_position = player.get(Position)!;
const camera = player.get(Camera)!;
const mouse = InputManager.get_mouse_position();
const world_mouse = screen_to_world(
mouse.x,
mouse.y,
camera,
canvas.width,
canvas.height,
);
const tile_x = Math.floor(world_mouse.x / TILE_SIZE);
const tile_y = Math.floor(world_mouse.y / TILE_SIZE);
const tile = dimension.tiles.findLast((tile) => tile.x === tile_x && tile.y === tile_y);
if (tile) {
const tile_info = EverythingRegistry.get<TileRegistry>("blocks", tile.id);
if (!tile_info) {
return;
}
const distance_to_player = distance_point_rectangle(
player_position.x + (72 / 2),
player_position.y + (72 / 2),
tile.x * TILE_SIZE,
tile.y * TILE_SIZE,
TILE_SIZE,
TILE_SIZE,
);
if (distance_to_player > TILE_SIZE * 2) {
return;
}
if (tile_info.on_interact || tile_info.on_click) {
if (tile_info.on_interact && InputManager.is_mouse_pressed(2)) {
InputManager.consume_mouse(2);
tile_info.on_interact(world, tile);
}
if (tile_info.on_click && InputManager.is_mouse_pressed(0)) {
InputManager.consume_mouse(0);
tile_info.on_click(world, tile);
}
}
}*/
} }
handle_second(world: ClientWorld, dimension: Dimension) { handle_second(world: ClientWorld, dimension: Dimension) {
const tickables = dimension.blocks.filter((t) => t.tickable); for (const chunk of dimension.chunks) {
for (const tickable of tickables) { for (const tickable of chunk.blocks_data) {
const tile_info = EverythingRegistry.get<BlockRegistry>("blocks", tickable.id); const tile_info = EverythingRegistry.get<BlockRegistry>("blocks", tickable.id);
if (tile_info && tile_info.on_second) { if (tile_info && tile_info.on_second) {
tile_info.on_second(world, tickable, dimension.second_timer); tile_info.on_second(world.dimension, tickable, dimension.second_timer);
}
} }
} }
dimension.second_timer = 0; dimension.second_timer = 0;
} }
handle_tick(world: ClientWorld, dimension: Dimension) { handle_tick(world: ClientWorld, dimension: Dimension) {
const tickables = dimension.blocks.filter((t) => t.tickable); for (const chunk of dimension.chunks) {
for (const tickable of tickables) { for (const tickable of chunk.blocks_data) {
const tile_info = EverythingRegistry.get<BlockRegistry>("blocks", tickable.id); const tile_info = EverythingRegistry.get<BlockRegistry>("blocks", tickable.id);
if (tile_info && tile_info.on_tick) { if (tile_info && tile_info.on_tick) {
tile_info.on_tick(world, tickable, dimension.tick_timer); tile_info.on_tick(world.dimension, tickable, dimension.tick_timer);
}
} }
} }
dimension.tick_timer = 0; dimension.tick_timer = 0;
+35 -18
View File
@@ -8,6 +8,7 @@ import { Position } from "../../common/components/position.ts";
import { PlayerComponent } from "../player.ts"; import { PlayerComponent } from "../player.ts";
import { GuiPlayerInventory } from "../gui/gui_player_inventory.ts"; import { GuiPlayerInventory } from "../gui/gui_player_inventory.ts";
import { CollisionCuboid } from "../components/collision.ts"; import { CollisionCuboid } from "../components/collision.ts";
import { BlockRegistry, EverythingRegistry, ItemRegistry } from "$/common/everything_registry.ts";
export class PlayerControlsSystem extends System { export class PlayerControlsSystem extends System {
constructor() { constructor() {
@@ -96,28 +97,44 @@ export class PlayerControlsSystem extends System {
InputManager.set_mouse_grabbed(player_component.screens.length === 0); InputManager.set_mouse_grabbed(player_component.screens.length === 0);
const block = world.dimension.get_looked_block(world.dimension, camera); const block = world.dimension.get_looked_block(world.dimension, camera);
if (block) { if (block && player_component.screens.length === 0) {
if (InputManager.is_mouse_pressed(0)) { if (InputManager.is_mouse_pressed(0)) {
world.dimension.break_block(block.x, block.y, block.z); world.dimension.break_block(block.x, block.y, block.z);
} else if (InputManager.is_mouse_pressed(2)) { } else if (InputManager.is_mouse_pressed(2)) {
const inventory = player_component.player_inventory; // interact if possible
const hotbar_slot = inventory.container.get_slot(inventory.hotbar_selected); const block_info = EverythingRegistry.get_by_id<BlockRegistry>("blocks", block.block);
if (hotbar_slot.has_item()) { if (block_info && block_info.on_interact) {
const FACE_OFFSETS = { block_info.on_interact(world.dimension, {
top: { x: 0, y: 1, z: 0 }, x: block.x,
bottom: { x: 0, y: -1, z: 0 }, y: block.y,
north: { x: 0, y: 0, z: -1 }, z: block.z,
south: { x: 0, y: 0, z: 1 }, id: block_info.id,
west: { x: -1, y: 0, z: 0 },
east: { x: 1, y: 0, z: 0 },
};
const offset = FACE_OFFSETS[block.face];
world.dimension.add_block({
x: block.x + offset.x,
y: block.y + offset.y,
z: block.z + offset.z,
id: "bworld:glass",
}); });
} else {
const inventory = player_component.player_inventory;
const hotbar_slot = inventory.container.get_slot(inventory.hotbar_selected);
if (hotbar_slot && hotbar_slot.has_item()) {
const item = hotbar_slot.get_item()!;
const item_info = EverythingRegistry.get<ItemRegistry>("items", item.type_id);
if (item_info && item_info.block_id) {
const FACE_OFFSETS = {
top: { x: 0, y: 1, z: 0 },
bottom: { x: 0, y: -1, z: 0 },
north: { x: 0, y: 0, z: -1 },
south: { x: 0, y: 0, z: 1 },
west: { x: -1, y: 0, z: 0 },
east: { x: 1, y: 0, z: 0 },
};
const offset = FACE_OFFSETS[block.face];
world.dimension.add_block({
x: block.x + offset.x,
y: block.y + offset.y,
z: block.z + offset.z,
id: item_info.block_id,
});
hotbar_slot.amount! -= 1;
}
}
} }
} }
} }
+12 -6
View File
@@ -62,7 +62,7 @@ interface TextureFront {
side: string; side: string;
} }
export interface BlockRegistry<T = unknown | undefined> { export interface BlockRegistry {
id: string; id: string;
textures: string | TextureSideTopBottom | TextureFront; textures: string | TextureSideTopBottom | TextureFront;
transparent?: boolean; transparent?: boolean;
@@ -71,15 +71,21 @@ export interface BlockRegistry<T = unknown | undefined> {
has_collision: boolean; has_collision: boolean;
drop_table?: string; drop_table?: string;
on_create?(dimension: Dimension, block: Block<T>): void; on_create?(dimension: Dimension, block: Block): void;
on_click?(dimension: Dimension, block: Block<T>): void; on_break?(dimension: Dimension, block: Block): void;
on_interact?(dimension: Dimension, block: Block<T>): void; on_click?(dimension: Dimension, block: Block): void;
on_tick?(dimension: Dimension, block: Block<T>, tick_delta: number): void; on_interact?(dimension: Dimension, block: Block): void;
on_second?(dimension: Dimension, block: Block<T>, second_delta: number): void; on_tick?(dimension: Dimension, block: Block, tick_delta: number): void;
on_second?(dimension: Dimension, block: Block, second_delta: number): void;
} }
export interface ItemRegistry<T = unknown | undefined> { export interface ItemRegistry<T = unknown | undefined> {
texture_id: string | ((item: ItemStack<T>) => string); texture_id: string | ((item: ItemStack<T>) => string);
block_id?: string;
place?(dimension: Dimension, block: Block): void;
on_create?(item: ItemStack<T>): void; on_create?(item: ItemStack<T>): void;
get_lore?(item: ItemStack<T>): string; get_lore?(item: ItemStack<T>): string;
} }
+2
View File
@@ -46,11 +46,13 @@ export function distance_point_point(ax: number, ay: number, az: number, bx: num
} }
export function register_block_item(block: BlockRegistry) { export function register_block_item(block: BlockRegistry) {
// TODO: handle block textures
let texture_id = "bworld:missing"; let texture_id = "bworld:missing";
if (typeof block.textures === "string") { if (typeof block.textures === "string") {
texture_id = block.textures; texture_id = block.textures;
} }
EverythingRegistry.register<ItemRegistry>("items", block.id, { EverythingRegistry.register<ItemRegistry>("items", block.id, {
texture_id, texture_id,
block_id: block.id,
}); });
} }