|
|
@@ -17,7 +17,8 @@ import sglue "third-party:sokol/glue"
|
|
|
import slog "third-party:sokol/log"
|
|
|
|
|
|
// --- Constants ---
|
|
|
-MAX_ENTITIES :: 100_000
|
|
|
+MAX_ENTITIES :: 1024
|
|
|
+MAX_BOX :: 32
|
|
|
|
|
|
// --- Data Types ---
|
|
|
|
|
|
@@ -31,6 +32,49 @@ Mesh :: struct {
|
|
|
indices: [dynamic]u16,
|
|
|
}
|
|
|
|
|
|
+Item_Kind :: enum u16 {
|
|
|
+ None,
|
|
|
+ Sword,
|
|
|
+ Potion,
|
|
|
+ Stone,
|
|
|
+}
|
|
|
+
|
|
|
+Item_Info :: struct {
|
|
|
+ name: string,
|
|
|
+ max_stack: u32,
|
|
|
+}
|
|
|
+
|
|
|
+item_db := [Item_Kind]Item_Info {
|
|
|
+ .None = {"Empty", 0},
|
|
|
+ .Sword = {"Iron Sword", 1},
|
|
|
+ .Potion = {"Health Potion", 64},
|
|
|
+ .Stone = {"Cobblestone", 64},
|
|
|
+}
|
|
|
+
|
|
|
+Box_Id :: distinct u32
|
|
|
+
|
|
|
+Box_Slot :: struct {
|
|
|
+ kind: Item_Kind,
|
|
|
+ count: u32,
|
|
|
+}
|
|
|
+
|
|
|
+Box_System :: struct {
|
|
|
+ is_open: bool,
|
|
|
+ slots: #soa[dynamic]Box_Slot,
|
|
|
+}
|
|
|
+
|
|
|
+// --- API ---
|
|
|
+
|
|
|
+box_system_push :: proc(size: u32) -> Box_Id {
|
|
|
+ box := Box_System {
|
|
|
+ slots = make(#soa[dynamic]Box_Slot, 0, size),
|
|
|
+ }
|
|
|
+ id := len(state.box_sys)
|
|
|
+ append_soa(&state.box_sys, box)
|
|
|
+ return Box_Id(id)
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
Entity_Kind :: enum u8 {
|
|
|
Player,
|
|
|
Enemy,
|
|
|
@@ -38,11 +82,12 @@ Entity_Kind :: enum u8 {
|
|
|
}
|
|
|
|
|
|
Entity :: struct {
|
|
|
- pos: linalg.Vector3f32,
|
|
|
- vel: linalg.Vector3f32,
|
|
|
- color: linalg.Vector3f32,
|
|
|
- kind: Entity_Kind,
|
|
|
- scale: f32,
|
|
|
+ pos: linalg.Vector3f32,
|
|
|
+ vel: linalg.Vector3f32,
|
|
|
+ color: linalg.Vector3f32,
|
|
|
+ kind: Entity_Kind,
|
|
|
+ scale: f32,
|
|
|
+ box_id: Box_Id,
|
|
|
}
|
|
|
|
|
|
Camera :: struct {
|
|
|
@@ -52,7 +97,7 @@ Camera :: struct {
|
|
|
}
|
|
|
|
|
|
Input_State :: struct {
|
|
|
- keys: map[sapp.Keycode]bool,
|
|
|
+ keys: #sparse[sapp.Keycode]bool,
|
|
|
mouse_dx: f32,
|
|
|
mouse_dy: f32,
|
|
|
mouse_x: f32,
|
|
|
@@ -62,21 +107,23 @@ Input_State :: struct {
|
|
|
}
|
|
|
|
|
|
App_State :: struct {
|
|
|
- arena: virtual.Arena,
|
|
|
- allocator: mem.Allocator,
|
|
|
- pass_action: sg.Pass_Action,
|
|
|
- mu_ctx: mu.Context,
|
|
|
- atlas_img: sg.Image,
|
|
|
- atlas_view: sg.View,
|
|
|
- atlas_smp: sg.Sampler,
|
|
|
- ui_pip: sgl.Pipeline,
|
|
|
- scene_pip: sgl.Pipeline,
|
|
|
- mesh_cube: Mesh,
|
|
|
- entities: #soa[dynamic]Entity,
|
|
|
- dpi_scale: f32,
|
|
|
- camera: Camera,
|
|
|
- input: Input_State,
|
|
|
- selected_color: [3]f32,
|
|
|
+ arena: virtual.Arena,
|
|
|
+ allocator: mem.Allocator,
|
|
|
+ pass_action: sg.Pass_Action,
|
|
|
+ mu_ctx: mu.Context,
|
|
|
+ atlas_img: sg.Image,
|
|
|
+ atlas_view: sg.View,
|
|
|
+ atlas_smp: sg.Sampler,
|
|
|
+ ui_pip: sgl.Pipeline,
|
|
|
+ scene_pip: sgl.Pipeline,
|
|
|
+ mesh_cube: Mesh,
|
|
|
+ box_sys: #soa[dynamic]Box_System,
|
|
|
+ entities: #soa[dynamic]Entity,
|
|
|
+ dpi_scale: f32,
|
|
|
+ camera: Camera,
|
|
|
+ last_input, input: Input_State,
|
|
|
+ selected_color: [3]f32,
|
|
|
+ texture01: Texture_Asset,
|
|
|
}
|
|
|
|
|
|
state: App_State
|
|
|
@@ -171,6 +218,11 @@ system_input_player :: proc(dt: f32) {
|
|
|
if state.input.keys[.SPACE] && player.pos.y <= 0.51 {
|
|
|
player.vel.y = 10.0
|
|
|
}
|
|
|
+
|
|
|
+ if state.last_input.keys[.TAB] && !state.input.keys[.TAB] {
|
|
|
+ state.box_sys[player.box_id].is_open = !state.box_sys[player.box_id].is_open
|
|
|
+ fmt.println("box.is_open: {}", state.box_sys[player.box_id].is_open)
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
system_builder :: proc(view, proj: linalg.Matrix4f32) {
|
|
|
@@ -355,6 +407,12 @@ init :: proc "c" () {
|
|
|
sg.setup({environment = sglue.environment(), logger = {func = slog.func}})
|
|
|
sgl.setup({logger = {func = slog.func}})
|
|
|
|
|
|
+ if tex, ok := load_texture("res/images/001.png"); ok {
|
|
|
+ state.texture01 = tex
|
|
|
+ } else {
|
|
|
+ fmt.println("File not found, using fallback color")
|
|
|
+ }
|
|
|
+
|
|
|
mu.init(&state.mu_ctx)
|
|
|
state.mu_ctx.text_width = mu.default_atlas_text_width
|
|
|
state.mu_ctx.text_height = mu.default_atlas_text_height
|
|
|
@@ -397,8 +455,12 @@ init :: proc "c" () {
|
|
|
|
|
|
state.mesh_cube = make_cube_mesh(state.allocator)
|
|
|
state.entities = make(#soa[dynamic]Entity, 0, MAX_ENTITIES, state.allocator)
|
|
|
+ fmt.printfln("entity.cap: {}", cap(state.entities))
|
|
|
+
|
|
|
+ state.box_sys = make(#soa[dynamic]Box_System, 0, MAX_BOX, state.allocator)
|
|
|
|
|
|
spawn_entity({0, 5, 0}, .Player)
|
|
|
+ state.entities[0].box_id = box_system_push(32)
|
|
|
state.camera.dist = 10.0
|
|
|
state.camera.pitch = 0.5
|
|
|
|
|
|
@@ -408,6 +470,8 @@ init :: proc "c" () {
|
|
|
y := rand.float32_range(10, 50)
|
|
|
spawn_entity({x, y, z}, .Enemy)
|
|
|
}
|
|
|
+
|
|
|
+ fmt.printfln("entity.size: {}", len(state.entities))
|
|
|
}
|
|
|
|
|
|
frame :: proc "c" () {
|
|
|
@@ -423,10 +487,6 @@ frame :: proc "c" () {
|
|
|
view, proj := get_camera_matrices()
|
|
|
system_builder(view, proj)
|
|
|
|
|
|
- state.input.mouse_dx = 0
|
|
|
- state.input.mouse_dy = 0
|
|
|
- state.input.left_clicked = false
|
|
|
-
|
|
|
mu.begin(&state.mu_ctx)
|
|
|
|
|
|
if mu.begin_window(&state.mu_ctx, "Stats", {10, 10, 200, 120}) {
|
|
|
@@ -457,6 +517,23 @@ frame :: proc "c" () {
|
|
|
|
|
|
mu.end_window(&state.mu_ctx)
|
|
|
}
|
|
|
+
|
|
|
+ if state.box_sys[state.entities[0].box_id].is_open {
|
|
|
+ w := i32(sapp.widthf() / 2) / i32(state.dpi_scale) - (200 / 2)
|
|
|
+ h := i32(sapp.heightf() / 2) / i32(state.dpi_scale) - (120 / 2)
|
|
|
+ if mu.begin_window(&state.mu_ctx, "Box", {w, h, 200, 120}) {
|
|
|
+ // 1. Рисуем загруженную картинку
|
|
|
+ mu.label(&state.mu_ctx, "Loaded Texture:")
|
|
|
+ mu.image(&state.mu_ctx, state.texture01.view.id, 64, 64)
|
|
|
+ mu.image_button(&state.mu_ctx, state.texture01.view.id, 64, 64)
|
|
|
+
|
|
|
+ // 2. Рисуем отсутствующую картинку (ID = 0) -> будет красный квадрат
|
|
|
+ mu.label(&state.mu_ctx, "Missing Texture (Fallback):")
|
|
|
+ mu.image(&state.mu_ctx, 0, 64, 64, {255, 0, 255, 255})
|
|
|
+ mu.end_window(&state.mu_ctx)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
mu.end(&state.mu_ctx)
|
|
|
|
|
|
system_render(view, proj)
|
|
|
@@ -466,6 +543,17 @@ frame :: proc "c" () {
|
|
|
sgl.draw()
|
|
|
sg.end_pass()
|
|
|
sg.commit()
|
|
|
+
|
|
|
+ // 3. Подготовка к СЛЕДУЮЩЕМУ кадру (End of Frame)
|
|
|
+
|
|
|
+ // Сначала сохраняем текущее состояние как "прошлое"
|
|
|
+ state.last_input = state.input
|
|
|
+
|
|
|
+ // Затем сбрасываем дельты (движение мыши и клики живут 1 кадр)
|
|
|
+ // Но НЕ сбрасываем keys, так как клавиши могут быть зажаты долго
|
|
|
+ state.input.mouse_dx = 0
|
|
|
+ state.input.mouse_dy = 0
|
|
|
+ state.input.left_clicked = false
|
|
|
}
|
|
|
|
|
|
// --- Render Impl ---
|
|
|
@@ -536,6 +624,12 @@ render_ui :: proc() {
|
|
|
true,
|
|
|
)
|
|
|
sgl.begin_quads()
|
|
|
+ case ^mu.Command_Image:
|
|
|
+ if c.id == 0 {
|
|
|
+ draw_rect(c.rect, c.color)
|
|
|
+ } else {
|
|
|
+ draw_image_rect(c.rect, c.id, c.color)
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
sgl.end()
|
|
|
@@ -546,6 +640,34 @@ render_ui :: proc() {
|
|
|
sgl.pop_pipeline()
|
|
|
}
|
|
|
|
|
|
+draw_image_rect :: proc(r: mu.Rect, view_id: u32, c: mu.Color) {
|
|
|
+ sgl.end()
|
|
|
+
|
|
|
+ view := sg.View {
|
|
|
+ id = view_id,
|
|
|
+ }
|
|
|
+
|
|
|
+ sgl.enable_texture()
|
|
|
+ sgl.texture(view, state.atlas_smp)
|
|
|
+
|
|
|
+ sgl.begin_quads()
|
|
|
+
|
|
|
+ x0, y0 := f32(r.x), f32(r.y)
|
|
|
+ x1, y1 := f32(r.x + r.w), f32(r.y + r.h)
|
|
|
+
|
|
|
+ sgl.c4b(c.r, c.g, c.b, c.a)
|
|
|
+ sgl.v2f_t2f(x0, y0, 0, 0)
|
|
|
+ sgl.v2f_t2f(x1, y0, 1, 0)
|
|
|
+ sgl.v2f_t2f(x1, y1, 1, 1)
|
|
|
+ sgl.v2f_t2f(x0, y1, 0, 1)
|
|
|
+
|
|
|
+ sgl.end()
|
|
|
+
|
|
|
+ sgl.enable_texture()
|
|
|
+ sgl.texture(state.atlas_view, state.atlas_smp)
|
|
|
+ sgl.begin_quads()
|
|
|
+}
|
|
|
+
|
|
|
draw_rect :: proc(r: mu.Rect, c: mu.Color) {
|
|
|
push_quad(r, mu.default_atlas[mu.DEFAULT_ATLAS_WHITE], c)
|
|
|
}
|