Files
odin-iso-test/src3/ui.odin
T
2026-08-03 21:11:00 +03:00

156 lines
4.9 KiB
Odin

package main
import rl "vendor:raylib"
ui_draw_world_hp_bar :: proc(b: ^BaseEntity, actor: ^Actor) {
hp_ratio := f32(actor.hp) / f32(actor.max_hp)
bar_w := b.col_size * 3
bar_pos := b.pos + rl.Vector2{-bar_w / 2, -b.col_size - 10}
rl.DrawRectangleV(bar_pos, {bar_w, 4}, {60, 60, 60, 255})
rl.DrawRectangleV(bar_pos, {bar_w * hp_ratio, 4}, {220, 40, 40, 255})
}
// ─── Инвентарь: геометрия слотов ───
INV_SLOT_SIZE :: 44
INV_PAD :: 6
INV_COLS :: 4
inv_panel_rect :: proc() -> rl.Rectangle {
w := INV_COLS * (INV_SLOT_SIZE + INV_PAD) + INV_PAD
h := (INV_SIZE / INV_COLS) * (INV_SLOT_SIZE + INV_PAD) + INV_PAD
return rl.Rectangle{f32(SCREEN_W) - f32(w) - 12, 60, f32(w), f32(h)}
}
inv_slot_rect :: proc(i: int) -> rl.Rectangle {
panel := inv_panel_rect()
col := i % INV_COLS
row := i / INV_COLS
x := panel.x + INV_PAD + f32(col) * (INV_SLOT_SIZE + INV_PAD)
y := panel.y + INV_PAD + f32(row) * (INV_SLOT_SIZE + INV_PAD)
return rl.Rectangle{x, y, INV_SLOT_SIZE, INV_SLOT_SIZE}
}
ui_inventory_slot_at :: proc(mouse: rl.Vector2) -> int {
if !rl.CheckCollisionPointRec(mouse, inv_panel_rect()) do return -1
for i in 0 ..< INV_SIZE {
if rl.CheckCollisionPointRec(mouse, inv_slot_rect(i)) do return i
}
return -1
}
// ─── Отрисовка ───
ui_draw :: proc(state: ^GameState) {
_, p := get_player(state)
if p == nil do return
// HP + XP
hp_ratio := f32(p.hp) / f32(p.max_hp)
rl.DrawRectangle(12, 12, 300, 22, {40, 40, 40, 255})
rl.DrawRectangle(12, 12, i32(300 * hp_ratio), 22, {200, 40, 40, 255})
rl.DrawText(rl.TextFormat("%d/%d HP", p.hp, p.max_hp), 22, 16, 14, rl.RAYWHITE)
xp_ratio := f32(p.xp) / f32(p.xp_next)
rl.DrawRectangle(12, 38, 300, 12, {40, 40, 40, 255})
rl.DrawRectangle(12, 38, i32(300 * xp_ratio), 12, {80, 160, 220, 255})
rl.DrawText(rl.TextFormat("Lvl %d %d/%d XP Kills: %d", p.level, p.xp, p.xp_next, state.kills), 22, 40, 10, rl.WHITE)
// статы
rl.DrawText(rl.TextFormat("DMG %d CRIT %d%% SPD x%.2f", player_total_dmg(p), i32(player_crit(p) * 100), 1 / player_atk_cd(p)), 12, 56, 14, {220, 220, 220, 255})
// навыки
for i in 0 ..< SKILL_COUNT {
s := p.skills[i]
rect := rl.Rectangle{f32(12 + i * 56), f32(SCREEN_H - 60), 48, 48}
rl.DrawRectangleRec(rect, {50, 50, 60, 255})
key: cstring = "Q"
switch s.kind {
case .Dash:
key = "Q"
case .Spin:
key = "W"
case .Heal:
key = "E"
}
rl.DrawText(key, i32(rect.x) + 6, i32(rect.y) + 4, 14, rl.RAYWHITE)
if s.timer > 0 {
ratio := s.timer / s.cd
rl.DrawRectangleRec(rect, {0, 0, 0, u8(160 * ratio)})
rl.DrawText(rl.TextFormat("%.1f", s.timer), i32(rect.x) + 8, i32(rect.y) + 28, 12, rl.RAYWHITE)
}
}
// инвентарь
panel := inv_panel_rect()
rl.DrawRectangleRec(panel, {35, 35, 40, 220})
rl.DrawRectangleLinesEx(panel, 1, {90, 90, 90, 255})
for i in 0 ..< INV_SIZE {
rect := inv_slot_rect(i)
rl.DrawRectangleRec(rect, {55, 55, 60, 255})
rl.DrawRectangleLinesEx(rect, 1, {90, 90, 90, 255})
if i < int(p.inv_count) {
item := p.inventory[i]
rl.DrawCircleV({rect.x + rect.width / 2, rect.y + rect.height / 2}, 10, item_color(item.kind))
rl.DrawText(rl.TextFormat("%d", item.level), i32(rect.x) + 4, i32(rect.y) + 28, 10, rl.WHITE)
}
}
// тултип выбранного (под курсором)
if slot := ui_inventory_slot_at(rl.GetMousePosition()); slot >= 0 && slot < int(p.inv_count) {
item := p.inventory[slot]
draw_item_tooltip(item, rl.GetMousePosition())
}
// лог
for i in 0 ..< state.log_count {
rl.DrawText(rl.TextFormat("%s", state.log[i]), 12, SCREEN_H - 160 + i * 18, 14, {200, 200, 200, 255})
}
rl.DrawText("LMB attack Q dash W spin E heal", 12, SCREEN_H - 28, 12, {120, 120, 120, 255})
}
draw_item_tooltip :: proc(item: Item, at: rl.Vector2) {
lines: [8]cstring
count := 0
lines[count] = rl.TextFormat("%s lvl %d", item.name, item.level)
count += 1
if item.dmg > 0 {
lines[count] = rl.TextFormat("Damage: %d", item.dmg)
count += 1
}
if item.crit > 0 {
lines[count] = rl.TextFormat("Crit: %d%%", i32(item.crit * 100))
count += 1
}
if item.atk_spd > 0 {
lines[count] = rl.TextFormat("Atk speed: +%d%%", i32(item.atk_spd * 100))
count += 1
}
if item.armor > 0 {
lines[count] = rl.TextFormat("Armor: %d", item.armor)
count += 1
}
if item.hp_bonus > 0 {
lines[count] = rl.TextFormat("+HP: %d", item.hp_bonus)
count += 1
}
if item.heal > 0 {
lines[count] = rl.TextFormat("Heals: %d", item.heal)
count += 1
}
tip_x := at.x + 16
tip_y := at.y + 16
if tip_x + 180 > SCREEN_W do tip_x = at.x - 196
if tip_y + f32(count) * 18 + 8 > SCREEN_H do tip_y = at.y - f32(count) * 18 - 8
rl.DrawRectangle(i32(tip_x), i32(tip_y), 180, i32(count * 18 + 8), {20, 20, 25, 235})
rl.DrawRectangleLines(i32(tip_x), i32(tip_y), 180, i32(count * 18 + 8), {150, 150, 150, 255})
for i in 0 ..< count {
rl.DrawText(lines[i], i32(tip_x) + 6, i32(tip_y) + 6 + i32(i) * 18, 13, rl.RAYWHITE)
}
}