summaryrefslogtreecommitdiff
path: root/hud.ha
diff options
context:
space:
mode:
authorLassi Pulkkinen <lassi@pulk.fi>2024-10-31 03:11:21 +0200
committerLassi Pulkkinen <lassi@pulk.fi>2024-10-31 03:51:35 +0200
commitae44478b30d890fe0fb04022f44d474dcdcc3f9d (patch)
tree5f462459ae4b47d22114eed717d1382d08cf4dfe /hud.ha
Initial commit (import old repo)HEADmain
Diffstat (limited to 'hud.ha')
-rw-r--r--hud.ha258
1 files changed, 258 insertions, 0 deletions
diff --git a/hud.ha b/hud.ha
new file mode 100644
index 0000000..d39dc3d
--- /dev/null
+++ b/hud.ha
@@ -0,0 +1,258 @@
+use gl::*;
+use glm;
+use math;
+use strconv;
+use strings;
+use trace;
+use types;
+
+let TEXTURE_GUI_ICONS = 0u;
+
+def HUD_WIDTH = 182.0f32;
+def HUD_HEALTH_YOFFSET = 39.0f32;
+
+fn hud_load() void = {
+ TEXTURE_GUI_ICONS = texture_upload(
+ textures_find("minecraft:gui/icons") as *Texture,
+ &trace::root);
+};
+
+const LAYER_HUD = Layer {
+ blocks_input = &layer_hud_blocks_input,
+ input = &layer_hud_input,
+ is_opaque = &layer_hud_is_opaque,
+ render = &layer_hud_render,
+};
+
+fn layer_hud_blocks_input() bool = {
+ return false;
+};
+
+fn layer_hud_input(event: InputEvent) bool = {
+ return false;
+};
+
+fn layer_hud_is_opaque() bool = {
+ return false;
+};
+
+fn layer_hud_render() void = {
+ if (CLIENT_STATE != ClientState::GAME) {
+ return;
+ };
+
+ let (width, height) = drawable_size();
+ let gui_width = width / gui_scale();
+ let gui_height = height / gui_scale();
+
+ const trans = gui_proj();
+
+ // crosshair
+
+ if (GAMEMODE != Gamemode::SPECTATOR) {
+ glEnable(GL_BLEND);
+ glBlendEquation(GL_FUNC_ADD);
+ glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ONE_MINUS_SRC_COLOR);
+
+ render_rect_textured(trans,
+ ((gui_width - 15) / 2): f32, ((gui_height - 15) / 2): f32,
+ 15.0, 15.0,
+ [255...],
+ 0.0, 0.0, 15.0, 15.0,
+ TEXTURE_GUI_ICONS, 256, 256);
+ };
+
+ glEnable(GL_BLEND);
+ glBlendEquation(GL_FUNC_ADD);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ const left = gui_width: f32 * 0.5 - HUD_WIDTH * 0.5;
+ const right = gui_width: f32 * 0.5 + HUD_WIDTH * 0.5;
+
+ const survival = GAMEMODE == Gamemode::SURVIVAL
+ || GAMEMODE == Gamemode::ADVENTURE;
+
+ // xp bar
+
+ if (survival) {
+ const progress = 0.0f32; // TODO
+ const progress = math::ceilf64(progress * HUD_WIDTH): f32;
+ const y = gui_height: f32 - 29.0;
+ const height = 5.0f32;
+ render_rect_textured(trans, left, y, HUD_WIDTH, height,
+ [255...],
+ 0.0, 64.0, HUD_WIDTH, height,
+ TEXTURE_GUI_ICONS, 256, 256);
+
+ render_rect_textured(trans, left, y, progress, height,
+ [255...],
+ 0.0, 69.0, progress, height,
+ TEXTURE_GUI_ICONS, 256, 256);
+
+ const level = 0u32; // TODO
+ if (level != 0) {
+ // XXX: let's not take risks here with the static
+ // alloc...
+ const text = strings::dup(strconv::u32tos(level));
+ defer free(text);
+ const font = fonts_find("minecraft:default") as *Font;
+ const metrics = font_measure(font, text);
+ // TODO: harec weirdness...
+ let x = (gui_width: f32 - metrics.width) * 0.5;
+ const y = gui_height: f32 - 35.0;
+ // TODO: this is how minecraft does it; however it looks
+ // ugly with high-res fonts (unifont included!). should
+ // probably take font resolution into account, or use a
+ // shader thing or something.
+ const text_trans = glm::translation_make(&[x - 1.0, y, 0.0]);
+ const text_trans = glm::m4_mul(trans, &text_trans);
+ render_text(text, font, &text_trans, [0...]);
+ const text_trans = glm::translation_make(&[x + 1.0, y, 0.0]);
+ const text_trans = glm::m4_mul(trans, &text_trans);
+ render_text(text, font, &text_trans, [0...]);
+ const text_trans = glm::translation_make(&[x, y - 1.0, 0.0]);
+ const text_trans = glm::m4_mul(trans, &text_trans);
+ render_text(text, font, &text_trans, [0...]);
+ const text_trans = glm::translation_make(&[x, y + 1.0, 0.0]);
+ const text_trans = glm::m4_mul(trans, &text_trans);
+ render_text(text, font, &text_trans, [0...]);
+ const text_trans = glm::translation_make(&[x, y, 0.0]);
+ const text_trans = glm::m4_mul(trans, &text_trans);
+ render_text(text, font, &text_trans, [128, 255, 32, 255]);
+ };
+ };
+
+ // icon bars left
+
+ const x = left;
+ let y = gui_height: f32 - HUD_HEALTH_YOFFSET;
+
+ if (survival) {
+ // XXX: absorption really screwed over any attempt at simplicity
+ // here...
+
+ const health = math::ceilf64(PLAYER_HEALTH): f32;
+ const health = if (health > 0.0) health else 0.0f32;
+ const health = health: u32;
+ const max_health = 20u32; // TODO
+ const max_health = if (health > max_health)
+ health else max_health;
+ const nhearts = (max_health >> 1) + (max_health & 1);
+ const nhealth_rows = (nhearts + 9) / 10;
+
+ const extra_health = math::ceilf64(0.0f32): f32; // TODO
+ const extra_health = extra_health: u32;
+ const nextra_hearts = (extra_health >> 1) + (extra_health & 1);
+ const nextra_health_rows = (nextra_hearts + 9) / 10;
+
+ const nrows = nhealth_rows + nextra_health_rows;
+ const row_height = if (nrows <= 9) 12 - nrows else 3u32;
+ const row_height = row_height: f32;
+
+ hud_render_icon_bar(trans,
+ extra_health, 0,
+ x, y - nhealth_rows: f32 * row_height, 8.0, -row_height,
+ 16.0, 0.0, 160.0, 0.0, 169.0, 0.0);
+ hud_render_icon_bar(trans,
+ health, max_health,
+ x, y, 8.0, -row_height,
+ 16.0, 0.0, 52.0, 0.0, 61.0, 0.0);
+ y -= 10.0 + (nrows - 1): f32 * row_height;
+ };
+
+ const armor = 0u32; // TODO
+ const max_armor = 20u32; // TODO
+ if (survival && armor != 0) {
+ y -= hud_render_simple_icon_bar(trans,
+ armor, max_armor, x, y, 8.0,
+ 16.0, 9.0, 34.0, 9.0, 25.0, 9.0);
+ };
+
+ // icon bars right
+
+ const x = right - 9.0;
+ let y = gui_height: f32 - HUD_HEALTH_YOFFSET;
+
+ if (false) { // TODO
+ const vehicle_health = 20u32; // TODO
+ const max_vehicle_health = 20u32; // TODO
+ y -= hud_render_simple_icon_bar(trans,
+ vehicle_health, max_vehicle_health, x, y, -8.0,
+ 52.0, 9.0, 88.0, 9.0, 97.0, 9.0);
+ };
+
+ if (survival) {
+ const food = PLAYER_FOOD: u32;
+ const max_food = 20u32; // TODO
+ y -= hud_render_simple_icon_bar(trans,
+ food, max_food, x, y, -8.0,
+ 16.0, 27.0, 52.0, 27.0, 61.0, 27.0);
+ };
+
+ const air = 300u32; // TODO
+ const max_air = 300u32; // TODO
+ if (survival && air != max_air) { // TODO: always show when underwater
+ const air_rem = (air * 10 + max_air - 1) % max_air;
+ const air_bubbles = (air * 10 + max_air - 1) / max_air;
+ const air_bubbles = if (air_rem < 20)
+ air_bubbles * 2 - 1 else air_bubbles * 2;
+ y -= hud_render_simple_icon_bar(trans,
+ air_bubbles, 20, x, y, -8.0,
+ 70.0, 18.0, 16.0, 18.0, 25.0, 18.0);
+ };
+};
+
+fn hud_render_simple_icon_bar(
+ trans: *glm::m4,
+ amount: u32, max: u32,
+ x: f32, y: f32, xstep: f32,
+ ubg: f32, vbg: f32,
+ ufull: f32, vfull: f32,
+ uhalf: f32, vhalf: f32,
+) f32 = {
+ const max = if (amount > max) amount else max;
+ const nicons = (max >> 1) + (max & 1);
+ const nrows = (nicons + 9) / 10;
+ const row_height = if (nrows <= 9) 12 - nrows else 3u32;
+ const row_height = row_height: f32;
+ hud_render_icon_bar(trans, amount, max,
+ x, y, xstep, -row_height,
+ ubg, vbg, ufull, vfull, uhalf, vhalf);
+ return 10.0 + row_height * (nrows - 1): f32;
+};
+
+fn hud_render_icon_bar(
+ trans: *glm::m4,
+ amount: u32, max: u32,
+ x: f32, y: f32, xstep: f32, ystep: f32,
+ ubg: f32, vbg: f32,
+ ufull: f32, vfull: f32,
+ uhalf: f32, vhalf: f32,
+) void = {
+ const max = if (amount > max) amount else max;
+ const nicons = (max >> 1) + (max & 1);
+
+ for (let i = nicons; i > 0) {
+ i -= 1;
+
+ const x_ = x + (i % 10): f32 * xstep;
+ const y_ = y + (i / 10): f32 * ystep;
+
+ render_rect_textured(trans, x_, y_, 9.0, 9.0,
+ [255...],
+ ubg, vbg, 9.0, 9.0,
+ TEXTURE_GUI_ICONS, 256, 256);
+
+ if (i * 2 >= amount) {
+ continue;
+ };
+
+ let (ufg, vfg) = if (i * 2 + 1 == amount)
+ (uhalf, vhalf) else (ufull, vfull);
+
+ render_rect_textured(trans, x_, y_, 9.0, 9.0,
+ [255...],
+ ufg, vfg, 9.0, 9.0,
+ TEXTURE_GUI_ICONS, 256, 256);
+ };
+};