156 lines
4.9 KiB
Odin
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)
|
|
}
|
|
}
|