diff --git a/client/blocks/furnace.ts b/client/blocks/furnace.ts index f526b02..e106a95 100644 --- a/client/blocks/furnace.ts +++ b/client/blocks/furnace.ts @@ -80,7 +80,9 @@ const block = EverythingRegistry.register("blocks", "bworld:furna 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); + return true; } + return false; }, on_create(dimension, block) { dimension.add_block_data({ diff --git a/client/blocks/glass.ts b/client/blocks/glass.ts index 32a800c..9b30bef 100644 --- a/client/blocks/glass.ts +++ b/client/blocks/glass.ts @@ -2,6 +2,7 @@ import { BlockRegistry, EverythingRegistry } from "$/common/everything_registry. import { register_block_item } from "$/common/utils.ts"; const block = EverythingRegistry.register("blocks", "bworld:glass", { + id: "bworld:glass", textures: "bworld:glass", has_collision: true, transparent: true, diff --git a/client/blocks/grass.ts b/client/blocks/grass.ts index 12354aa..9c808e8 100644 --- a/client/blocks/grass.ts +++ b/client/blocks/grass.ts @@ -14,6 +14,8 @@ EverythingRegistry.register("blocks", "bworld:grass", { if (item?.type_id === "bworld:hoe") { block.id = "bworld:hoed_dirt"; dimension.add_block(block); + return true; } + return false; }, }); diff --git a/client/components/player_controls.ts b/client/components/player_controls.ts index 47d4a22..0e1d383 100644 --- a/client/components/player_controls.ts +++ b/client/components/player_controls.ts @@ -23,6 +23,7 @@ export class PlayerControls extends Component { hotbar_9: KeyCode = "Digit9"; open_inventory: KeyCode = "KeyE"; + open_chat: KeyCode = "KeyT"; open_debug: KeyCode = "F3"; } diff --git a/client/gui/gui_chat.ts b/client/gui/gui_chat.ts new file mode 100644 index 0000000..dd5f1dd --- /dev/null +++ b/client/gui/gui_chat.ts @@ -0,0 +1,131 @@ +import { GuiScreen } from "./gui_screen.ts"; +import { canvas, draw_rect, draw_text, measure_text } from "$/client/renderer/mod.ts"; +import { InputManager } from "../input_manager.ts"; +import { ClientWorld } from "../client_world.ts"; +import { PlayerComponent } from "../player.ts"; +import { ItemStack } from "../inventory.ts"; + +export class GuiChat extends GuiScreen { + world: ClientWorld; + + text_typed = ""; + caret = 0; + key_repeat_timer = 0; + show_caret = true; + + constructor(world: ClientWorld) { + super(); + this.world = world; + } + + override on_render(): void { + draw_rect(0, 0, canvas.width, canvas.height, [0, 0, 0, 0.8]); + + const y = canvas.height - 32; + draw_rect(0, y, canvas.width, canvas.height, [0, 0, 0, 0.4]); + + draw_text(this.text_typed, 0, y, 2, [1, 1, 1]); + + if (this.show_caret) { + const before_text = this.text_typed.substring(0, this.caret); + const caret_x = 0 + measure_text(before_text, 2); + + draw_rect(caret_x, y + 4, 1, 32 - 4); + } + } + + override on_tick(_delta: number): void { + const now = performance.now(); + const repeat_delay = 400; + const repeat_rate = 40; + + const allow_repeat = () => { + if (InputManager.is_key_pressed("Backspace")) { + this.key_repeat_timer = now; + return true; + } + + if (InputManager.is_key_down("Backspace")) { + if (now - this.key_repeat_timer > repeat_delay) { + this.key_repeat_timer = now - (repeat_delay - repeat_rate); + return true; + } + } + + return false; + }; + + if (allow_repeat()) { + if (this.caret > 0) { + this.text_typed = this.text_typed.slice(0, this.caret - 1) + this.text_typed.slice(this.caret); + this.caret -= 1; + } + } + + if (InputManager.is_key_pressed("Delete")) { + this.text_typed = this.text_typed.slice(0, this.caret) + this.text_typed.slice(this.caret + 1); + } + + if (InputManager.is_key_pressed("ArrowLeft")) { + this.caret = Math.max(0, this.caret - 1); + } + + if (InputManager.is_key_pressed("ArrowRight")) { + this.caret = Math.min(this.text_typed.length, this.caret + 1); + } + + if (InputManager.is_key_pressed("Home")) { + this.caret = 0; + } + + if (InputManager.is_key_pressed("End")) { + this.caret = this.text_typed.length; + } + + const typed = InputManager.get_typed_characters(); + + for (const char of typed) { + this.text_typed = this.text_typed.slice(0, this.caret) + char + this.text_typed.slice(this.caret); + this.caret += 1; + } + + if (InputManager.is_key_pressed("Enter")) { + this.submit(); + } + } + override on_close(): void {} + + submit() { + if (this.text_typed.startsWith("/")) { + this.command(); + } else { + // we dont have multiplayer lol? + } + this.text_typed = ""; + + const [player] = this.world.get_tag("player")!; + const player_component = player.get(PlayerComponent); + if (player_component) { + player_component.pop_screen(); + } + } + + command() { + if (this.text_typed.startsWith("/give")) { + let [_, item_id, quantity] = this.text_typed.split(" "); + + if (!item_id.includes(":")) { + item_id = `bworld:${item_id}`; + } + if (quantity === undefined) { + quantity = "1"; + } + + const [player] = this.world.get_tag("player")!; + const player_component = player.get(PlayerComponent); + if (player_component) { + player_component.player_inventory.container.add_item(new ItemStack(item_id, Number(quantity))); + } + } + } +} diff --git a/client/gui/gui_chest.ts b/client/gui/gui_chest.ts index 9452a9a..ef70bab 100644 --- a/client/gui/gui_chest.ts +++ b/client/gui/gui_chest.ts @@ -1,5 +1,5 @@ import { Inventory, PlayerInventory } from "../inventory.ts"; -import { add_player_hotbar, add_player_inventory, GuiScreen, Slot } from "./gui_screen.ts"; +import { add_player_hotbar, add_player_inventory, GuiInventoryScreen, Slot } from "./gui_screen.ts"; import { canvas, draw_rect, Texture } from "$/client/renderer/mod.ts"; import { AssetManager } from "../assets.ts"; import { SLOT_SIZE } from "../../common/constants.ts"; @@ -7,7 +7,7 @@ import { draw_nine_slice } from "../systems/rendering/render_utils.ts"; const PADDING = 10; -export class GuiChest extends GuiScreen { +export class GuiChest extends GuiInventoryScreen { override inventory_width = PADDING * 2 + SLOT_SIZE * 9; override inventory_height = PADDING * 4 + SLOT_SIZE * 7; diff --git a/client/gui/gui_furnace.ts b/client/gui/gui_furnace.ts index 363bb14..1a53bab 100644 --- a/client/gui/gui_furnace.ts +++ b/client/gui/gui_furnace.ts @@ -1,5 +1,5 @@ import { Inventory, PlayerInventory } from "../inventory.ts"; -import { add_player_hotbar, add_player_inventory, GuiScreen, Slot } from "./gui_screen.ts"; +import { add_player_hotbar, add_player_inventory, GuiInventoryScreen, Slot } from "./gui_screen.ts"; import { canvas, draw_rect, draw_text, Texture } from "$/client/renderer/mod.ts"; import { AssetManager } from "../assets.ts"; import { SLOT_SIZE } from "../../common/constants.ts"; @@ -7,7 +7,7 @@ import { draw_nine_slice } from "../systems/rendering/render_utils.ts"; const PADDING = 10; -export class GuiFurnace extends GuiScreen { +export class GuiFurnace extends GuiInventoryScreen { override inventory_width = PADDING * 2 + SLOT_SIZE * 9; override inventory_height = PADDING * 4 + SLOT_SIZE * 7; diff --git a/client/gui/gui_player_inventory.ts b/client/gui/gui_player_inventory.ts index cff89c9..6ea4318 100644 --- a/client/gui/gui_player_inventory.ts +++ b/client/gui/gui_player_inventory.ts @@ -1,5 +1,5 @@ import { Container, Inventory, ItemStack, PlayerInventory } from "../inventory.ts"; -import { add_player_hotbar, add_player_inventory, GuiScreen, Slot } from "./gui_screen.ts"; +import { add_player_hotbar, add_player_inventory, GuiInventoryScreen, Slot } from "./gui_screen.ts"; import { canvas, draw_rect, Texture } from "$/client/renderer/mod.ts"; import { AssetManager } from "../assets.ts"; import { SLOT_SIZE } from "../../common/constants.ts"; @@ -33,7 +33,7 @@ const recipes: CraftingRecipe[] = [ const PADDING = 10; -export class GuiPlayerInventory extends GuiScreen { +export class GuiPlayerInventory extends GuiInventoryScreen { override inventory_width = PADDING * 2 + SLOT_SIZE * 9; override inventory_height = PADDING * 2 + SLOT_SIZE * 4 + PADDING + SLOT_SIZE * 3 + PADDING; diff --git a/client/gui/gui_screen.ts b/client/gui/gui_screen.ts index 1ee17fc..dc610b9 100644 --- a/client/gui/gui_screen.ts +++ b/client/gui/gui_screen.ts @@ -25,14 +25,22 @@ export class Slot { } } -export class GuiScreen | undefined> { +export abstract class GuiScreen { + x: number = 0; + y: number = 0; + + abstract on_tick(_delta: number): void; + abstract on_render(): void; + abstract on_close(): void; +} + +export class GuiInventoryScreen | undefined> + extends GuiScreen { inventory: Inv; player_inventory: PlayerInventory; properties: Properties; slots: Slot[] = []; - x: number = 0; - y: number = 0; inventory_width: number = 500; inventory_height: number = 500; @@ -41,6 +49,7 @@ export class GuiScreen 0) { - input_x /= size; - input_z /= size; - } + const size = Math.hypot(input_x, input_z); + if (size > 0) { + input_x /= size; + input_z /= size; + } - const sin = Math.sin(camera.yaw); - const cos = Math.cos(camera.yaw); + const sin = Math.sin(camera.yaw); + const cos = Math.cos(camera.yaw); - const forwardX = sin; - const forwardZ = cos; + const forwardX = sin; + const forwardZ = cos; - const rightX = cos; - const rightZ = -sin; + const rightX = cos; + const rightZ = -sin; - const speed_modifier = InputManager.is_key_down(controls.sprint_key) ? 1.75 : 1; + const speed_modifier = InputManager.is_key_down(controls.sprint_key) ? 1.75 : 1; - velocity.vx = (forwardX * input_z + rightX * input_x) * controls.move_speed * speed_modifier; - velocity.vz = (forwardZ * input_z + rightZ * input_x) * controls.move_speed * speed_modifier; + velocity.vx = (forwardX * input_z + rightX * input_x) * controls.move_speed * speed_modifier; + velocity.vz = (forwardZ * input_z + rightZ * input_x) * controls.move_speed * speed_modifier; - const cuboid = player.get(CollisionCuboid); + const cuboid = player.get(CollisionCuboid); - if (InputManager.is_key_down("Space") && cuboid?.colliding_y === 1) { - velocity.vy += controls.jump_force; + if (InputManager.is_key_down("Space") && cuboid?.colliding_y === 1) { + velocity.vy += controls.jump_force; + } } if (InputManager.is_key_pressed(controls.open_inventory)) { if (player_component.screens.length === 0) { player_component.screens.push(new GuiPlayerInventory(player_component.player_inventory)); - } else { + } else if (player_component.screens.at(-1) instanceof GuiInventoryScreen) { player_component.pop_screen(); } } + if (InputManager.is_key_pressed(controls.open_chat)) { + if (player_component.screens.length === 0) { + player_component.screens.push(new GuiChat(world)); + } + } + + if (InputManager.is_key_pressed("Escape")) { + player_component.pop_screen(); + } + if (InputManager.is_key_pressed(controls.open_debug)) { world.debugging = !world.debugging; } @@ -103,14 +117,13 @@ export class PlayerControlsSystem extends System { } else if (InputManager.is_mouse_pressed(2)) { // interact if possible const block_info = EverythingRegistry.get_by_id("blocks", block.block); - if (block_info && block_info.on_interact) { - block_info.on_interact(world.dimension, { - x: block.x, - y: block.y, - z: block.z, - id: block_info.id, - }); - } else { + const interacted = block_info?.on_interact?.(world.dimension, { + x: block.x, + y: block.y, + z: block.z, + id: block_info.id, + }); + if (!interacted) { const inventory = player_component.player_inventory; const hotbar_slot = inventory.container.get_slot(inventory.hotbar_selected); if (hotbar_slot && hotbar_slot.has_item()) { diff --git a/common/everything_registry.ts b/common/everything_registry.ts index 15ffcd1..945cbb2 100644 --- a/common/everything_registry.ts +++ b/common/everything_registry.ts @@ -74,7 +74,7 @@ export interface BlockRegistry { on_create?(dimension: Dimension, block: Block): void; on_break?(dimension: Dimension, block: Block): void; on_click?(dimension: Dimension, block: Block): void; - on_interact?(dimension: Dimension, block: Block): void; + on_interact?(dimension: Dimension, block: Block): boolean; on_tick?(dimension: Dimension, block: Block, tick_delta: number): void; on_second?(dimension: Dimension, block: Block, second_delta: number): void; }