This commit is contained in:
2026-02-27 11:22:22 -03:00
parent 28f0422771
commit b52b7f4c44
7 changed files with 208 additions and 0 deletions
+4
View File
@@ -8,7 +8,9 @@ import { TileEditorSystem } from "$/client/systems/tile_editor_system.ts";
import { InventorySystem } from "$/client/systems/inventory_system.ts"; import { InventorySystem } from "$/client/systems/inventory_system.ts";
import { UIInteractionSystem } from "$/client/systems/ui_interaction_system.ts"; import { UIInteractionSystem } from "$/client/systems/ui_interaction_system.ts";
import { UIRenderSystem } from "$/client/systems/ui_render_system.ts"; import { UIRenderSystem } from "$/client/systems/ui_render_system.ts";
import { CropSystem } from "$/client/systems/crop_system.ts";
import { create_main_menu } from "./main_menu.ts"; import { create_main_menu } from "./main_menu.ts";
import { ClickableSystem } from "./systems/clickable_system.ts";
export class ClientWorld extends World { export class ClientWorld extends World {
canvas: HTMLCanvasElement; canvas: HTMLCanvasElement;
@@ -56,9 +58,11 @@ export class ClientWorld extends World {
// Logic systems // Logic systems
this.add_system(new UIInteractionSystem(), "main_menu"); this.add_system(new UIInteractionSystem(), "main_menu");
this.add_system(new UIInteractionSystem(), "paused"); this.add_system(new UIInteractionSystem(), "paused");
this.add_system(new ClickableSystem(), "game");
this.add_system(new InventorySystem(), "game"); this.add_system(new InventorySystem(), "game");
this.add_system(new PlayerControlsSystem(), "game"); this.add_system(new PlayerControlsSystem(), "game");
this.add_system(new MovementSystem(), "game"); this.add_system(new MovementSystem(), "game");
this.add_system(new CropSystem(), "game");
// render systems // render systems
this.add_system(new RenderSystem(this.ctx), "game"); this.add_system(new RenderSystem(this.ctx), "game");
+13
View File
@@ -13,3 +13,16 @@ export class Camera extends Component {
this.zoom = zoom; this.zoom = zoom;
} }
} }
export function screen_to_world(
screen_x: number,
screen_y: number,
camera: Camera,
screen_width: number,
screen_height: number,
) {
return {
x: (screen_x - screen_width / 2) / camera.zoom + camera.x,
y: (screen_y - screen_height / 2) / camera.zoom + camera.y,
};
}
+13
View File
@@ -0,0 +1,13 @@
import { Component } from "$/common/ecs/mod.ts";
export class ClickableSprite extends Component {
clicked = false;
button: number;
access_range: number;
constructor(button: number = 0, access_range = 2) {
super();
this.button = button;
this.access_range = access_range;
}
}
+35
View File
@@ -0,0 +1,35 @@
import { Component, Entity } from "$/common/ecs/mod.ts";
import { Position } from "$/common/components/position.ts";
import { AssetManager } from "../assets.ts";
import { Sprite } from "./sprite.ts";
import { ClickableSprite } from "./clickable.ts";
export class Crop extends Component {
current_stage = 0;
growth_time = 0;
total_stages: number;
time_to_grow: number[];
item_drop: string;
// you know ["bworld:carrot_seeds", "bworld:carrot_stage_1", "bworld:carrot_stage_2" ...]
sprite_ids: string[];
constructor(total_stages: number, time_to_grow: number[], sprite_ids: string[], item_drop: string) {
super();
this.total_stages = total_stages;
this.time_to_grow = time_to_grow;
this.sprite_ids = sprite_ids;
this.item_drop = item_drop;
}
}
export function create_crop_entity(x: number, y: number, crop: Crop) {
const crop_entity = new Entity("crop");
crop_entity.add(new Position(x, y));
crop_entity.add(new Sprite(AssetManager.instance.get("bworld:textures"), 32, 32, 0, 0, 16, 16));
crop_entity.add(crop);
crop_entity.add(
new ClickableSprite(2),
);
return crop_entity;
}
+46
View File
@@ -6,6 +6,7 @@ import { AssetManager } from "./assets.ts";
import { create_player } from "./player.ts"; import { create_player } from "./player.ts";
import { UIButton } from "./components/ui_components.ts"; import { UIButton } from "./components/ui_components.ts";
import { open_about } from "./about.ts"; import { open_about } from "./about.ts";
import { create_crop_entity, Crop } from "./components/crop.ts";
export function start_game(world: ClientWorld) { export function start_game(world: ClientWorld) {
world.state = "game"; world.state = "game";
@@ -35,4 +36,49 @@ export function start_game(world: ClientWorld) {
about_button.add(new Position(world.canvas.width / 2 - 150, world.canvas.height / 2 + 80)); about_button.add(new Position(world.canvas.width / 2 - 150, world.canvas.height / 2 + 80));
about_button.add(new UIButton("About", 320, 64, () => open_about())); about_button.add(new UIButton("About", 320, 64, () => open_about()));
world.add_entity(about_button); world.add_entity(about_button);
world.add_entity(create_crop_entity(
0,
0,
new Crop(5, [4, 4, 4, 4], [
"bworld:potato_seeds",
"bworld:potato_stage_1",
"bworld:potato_stage_2",
"bworld:potato_stage_3",
"bworld:potato_stage_4",
], "bworld:potato"),
));
world.add_entity(create_crop_entity(
32,
0,
new Crop(5, [5, 5, 5, 5], [
"bworld:carrot_seeds",
"bworld:carrot_stage_1",
"bworld:carrot_stage_2",
"bworld:carrot_stage_3",
"bworld:carrot_stage_4",
], "bworld:carrot"),
));
world.add_entity(create_crop_entity(
0,
32,
new Crop(5, [10, 10, 10, 10], [
"bworld:pumpkin_seeds",
"bworld:pumpkin_stage_1",
"bworld:pumpkin_stage_2",
"bworld:pumpkin_stage_3",
"bworld:pumpkin_stage_4",
], "bworld:pumpkin"),
));
world.add_entity(create_crop_entity(
32,
32,
new Crop(5, [7, 7, 7, 7], [
"bworld:tomato_seeds",
"bworld:tomato_stage_1",
"bworld:tomato_stage_2",
"bworld:tomato_stage_3",
"bworld:tomato_stage_4",
], "bworld:tomato"),
));
} }
+52
View File
@@ -0,0 +1,52 @@
import { System } from "$/common/ecs/mod.ts";
import { Position } from "$/common/components/position.ts";
import { Sprite } from "../components/sprite.ts";
import { ClientWorld } from "../client_world.ts";
import { ClickableSprite } from "../components/clickable.ts";
import { point_inside_rec } from "$/common/utils.ts";
import { InputManager } from "../input_manager.ts";
import { Camera, screen_to_world } from "../components/camera.ts";
export class ClickableSystem extends System {
update(world: ClientWorld, _delta: number): void {
const [player] = world.get_tag("player")!;
const camera = player.get(Camera)!;
const mouse = InputManager.get_mouse_position();
const world_mouse = screen_to_world(
mouse.x,
mouse.y,
camera,
world.canvas.width,
world.canvas.height,
);
for (const entity of world.get_entities()) {
const position = entity.get(Position);
if (!position) {
continue;
}
const clickable_sprite = entity.get(ClickableSprite);
const sprite = entity.get(Sprite);
if (clickable_sprite && sprite) {
clickable_sprite.clicked = false;
const hovered = point_inside_rec(
world_mouse.x,
world_mouse.y,
position.x,
position.y,
sprite.width,
sprite.height,
);
if (hovered && InputManager.is_mouse_pressed(clickable_sprite.button)) {
InputManager.consume_mouse(clickable_sprite.button);
clickable_sprite.clicked = true;
}
}
}
}
}
+45
View File
@@ -0,0 +1,45 @@
import { System } from "$/common/ecs/mod.ts";
import { Sprite } from "../components/sprite.ts";
import { ClientWorld } from "../client_world.ts";
import { Crop } from "$/client/components/crop.ts";
import { get_sprite_region } from "$/common/utils.ts";
import { ClickableSprite } from "../components/clickable.ts";
import { ItemStack, PlayerInventory } from "../components/inventory.ts";
export class CropSystem extends System {
update(world: ClientWorld, delta: number): void {
for (const entity of world.get_entities()) {
const crop = entity.get(Crop);
if (!crop) {
continue;
}
// finished growing, very good
if (crop.total_stages === crop.current_stage + 1) {
const clickable_sprite = entity.get(ClickableSprite);
if (clickable_sprite && clickable_sprite.clicked) {
const [player] = world.get_tag("player")!;
const inventory = player.get(PlayerInventory)!;
inventory.container.add_item(new ItemStack(crop.item_drop));
world.delete_entity(entity);
}
continue;
}
crop.growth_time += delta;
if (crop.growth_time >= crop.time_to_grow[crop.current_stage]) {
crop.current_stage += 1;
crop.growth_time = 0;
}
const sprite = entity.get(Sprite);
if (sprite) {
const region = get_sprite_region(crop.sprite_ids[crop.current_stage]);
sprite.source_x = region.x * 16;
sprite.source_y = region.y * 16;
}
}
}
}