commit bdd0b69e3cf72bcb9e842ef07da6b547811f3bbb Author: Slendi Date: Sun Sep 28 18:50:09 2025 +0300 yea Signed-off-by: Slendi diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..3550a30 --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use flake diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8a88ab8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.direnv + diff --git a/a.otf b/a.otf new file mode 100644 index 0000000..3c2372a Binary files /dev/null and b/a.otf differ diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..2ce1e2c --- /dev/null +++ b/flake.lock @@ -0,0 +1,59 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1758690382, + "narHash": "sha256-NY3kSorgqE5LMm1LqNwGne3ZLMF2/ILgLpFr1fS4X3o=", + "rev": "e643668fd71b949c53f8626614b21ff71a07379d", + "revCount": 866707, + "type": "tarball", + "url": "https://api.flakehub.com/f/pinned/NixOS/nixpkgs/0.1.866707%2Brev-e643668fd71b949c53f8626614b21ff71a07379d/0199817d-7e74-7254-9801-5432e45096c7/source.tar.gz?rev=e643668fd71b949c53f8626614b21ff71a07379d&revCount=866707" + }, + "original": { + "type": "tarball", + "url": "https://flakehub.com/f/NixOS/nixpkgs/0.1" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..b0d9ac4 --- /dev/null +++ b/flake.nix @@ -0,0 +1,73 @@ +{ + description = "Announce nixos rebuild with dark souls-like message"; + + inputs = { + nixpkgs.url = "https://flakehub.com/f/NixOS/nixpkgs/0.1"; + flake-utils.url = "github:numtide/flake-utils"; + }; + + outputs = + { + self, + nixpkgs, + flake-utils, + }: + flake-utils.lib.eachDefaultSystem ( + system: + let + pkgs = import nixpkgs { inherit system; }; + lib = pkgs.lib; + in + { + packages.default = pkgs.stdenv.mkDerivation { + pname = "souls"; + version = "0.1.0"; + src = self; + + nativeBuildInputs = [ + pkgs.odin + pkgs.makeWrapper + ]; + + buildInputs = [ + pkgs.raylib + pkgs.libuuid + pkgs.zlib + ]; + + buildPhase = '' + odin build . -o:aggressive -out:souls + ''; + + installPhase = '' + mkdir -p $out/bin + cp souls $out/bin/souls + WRAP_PATH=${ + lib.makeLibraryPath [ + pkgs.raylib + pkgs.zlib + pkgs.libuuid + ] + } + wrapProgram $out/bin/souls \ + --prefix LD_LIBRARY_PATH : "$WRAP_PATH" \ + --set-default DYLD_LIBRARY_PATH "$WRAP_PATH" + ''; + }; + + apps.default = { + type = "app"; + program = "${self.packages.${system}.default}/bin/souls"; + }; + + devShells.default = pkgs.mkShell { + packages = with pkgs; [ + odin + ols + raylib + lldb + ]; + }; + } + ); +} diff --git a/main.odin b/main.odin new file mode 100644 index 0000000..6a5d056 --- /dev/null +++ b/main.odin @@ -0,0 +1,482 @@ +package main + +import "core:fmt" +import "core:math" +import rl "vendor:raylib" + +SFX: []u8 : #load("sfx.mp3") +OTF: []u8 : #load("a.otf") + +clampf :: proc(v: f32, lo: f32, hi: f32) -> f32 { + if v < lo do return lo + if v > hi do return hi + return v +} + +lerp :: proc(a, b, t: f32) -> f32 { + return a + (b - a) * clampf(t, 0, 1) +} + +color_mul_rgb :: proc(a, b: rl.Color) -> rl.Color { + return { + u8((f32(a.r) * f32(b.r)) / 255), + u8((f32(a.g) * f32(b.g)) / 255), + u8((f32(a.b) * f32(b.b)) / 255), + 255, + } +} + +with_alpha :: proc(c: rl.Color, alpha: f32) -> rl.Color { + out := c + out.a = u8(clampf(alpha, 0, 1) * 255) + return out +} + +// easings +ease_out_cubic :: proc(t: f32) -> f32 { + t1 := clampf(t, 0, 1) + return 1 - (1 - t1) * (1 - t1) * (1 - t1) +} +ease_in_out_cubic :: proc(t: f32) -> f32 { + t1 := clampf(t, 0, 1) + if t1 < 0.5 { + return 4 * t1 * t1 * t1 + } + u := 2 * t1 - 2 + return 0.5 * u * u * u + 1 +} +ease_out_back :: proc(t: f32, s: f32 = 1.70158) -> f32 { + t1 := clampf(t, 0, 1) + return 1 + s * math.pow(t1 - 1, 3) + s * (t1 - 1) * math.pow(t1 - 1, 2) +} + +adsr :: proc(t, attack, decay, sustain: f32, r_start: f32, r_dur: f32) -> f32 { + if t <= 0 {return 0} + if t < attack { + return ease_out_cubic(t / attack) + } + if t < attack + decay { + return lerp(1, sustain, ease_in_out_cubic((t - attack) / decay)) + } + if t < r_start { + return sustain + } + return sustain * (1 - ease_out_cubic((t - r_start) / math.max(r_dur, 0.0001))) +} + +draw_noun_verbed_sheen :: proc( + font: rl.Font, + text: string, + center: rl.Vector2, + base_font_size: f32, + spacing: f32, + text_color: rl.Color, + text_opacity: f32, + sheen_tint: rl.Color, + blur_size_: f32, + blur_opacity: f32, +) { + blur_size := blur_size_ + screen_h := rl.GetScreenHeight() + s := f32(screen_h) / 1080.0 + + if blur_size <= 1.0001 { + blur_size = 1.0001 + } + + steps_f := 20.0 * blur_size * math.pow(s, 0.25) + zoomSteps := i32(math.floor(steps_f)) + if zoomSteps < 6 { + zoomSteps = 6 + } + + voff := s / (blur_size - 1.0) + + base_size := rl.MeasureTextEx(font, fmt.ctprintf("{}", text), base_font_size, spacing) + base_origin := rl.Vector2{base_size.x * 0.5, base_size.y * 0.5} + + glow_base := color_mul_rgb(text_color, sheen_tint) + + rl.BeginBlendMode(.ADDITIVE) + for i := zoomSteps; i >= 0; i -= 1 { + t := f32(i) / f32(zoomSteps) + scale_factor := f32(math.pow(blur_size, t)) + + denom := f32(math.log2_f32(blur_size) / math.log2_f32(2)) + if denom == 0 { + denom = 0.000001 + } + fat_product := f32(math.pow(scale_factor, 1.0 / denom)) + + alpha := blur_opacity / fat_product + if alpha <= 0.001 { + continue + } + + fs := base_font_size * scale_factor + sp := spacing * scale_factor + + size := rl.MeasureTextEx(font, fmt.ctprintf("{}", text), fs, sp) + origin := rl.Vector2{size.x * 0.5, size.y * 0.5} + + y_offset := voff * (scale_factor - 1.0) + + rl.DrawTextPro( + font, + fmt.ctprintf("{}", text), + rl.Vector2{center.x, center.y + y_offset}, + origin, + 0.0, + fs, + sp, + with_alpha(glow_base, alpha), + ) + } + rl.EndBlendMode() + + rl.DrawTextPro( + font, + fmt.ctprintf("{}", text), + center, + base_origin, + 0.0, + base_font_size, + spacing, + with_alpha(text_color, text_opacity), + ) +} + +draw_shadow_bar :: proc( + center_y: f32, + rel_size: f32, + opacity: f32, + offset: f32, + softness: f32, + tint: rl.Color, +) { + if rel_size <= 0.0 || opacity <= 0.0 { + return + } + + w := rl.GetScreenWidth() + hf := f32(rl.GetScreenHeight()) + + shadow_h := rel_size * 0.25 * hf + center := center_y + offset * hf + top := center - shadow_h * 0.5 + + c_tint := rl.Color{tint.r, tint.g, tint.b, u8(clampf(opacity, 0, 1) * 255)} + c_clear := rl.Color{tint.r, tint.g, tint.b, 0} + + y0 := i32(math.round(top)) + total_h := i32(math.round(shadow_h)) + if total_h <= 0 { + return + } + y3 := y0 + total_h + + fade_h := shadow_h * clampf(softness, 0, 1) * 0.5 + fade_i := i32(math.round(fade_h)) + if fade_i * 2 > total_h { + fade_i = total_h / 2 + } + + y1 := y0 + fade_i + y2 := y3 - fade_i + mid_h := y2 - y1 + + rl.BeginBlendMode(.ALPHA) + + if fade_i > 0 { + rl.DrawRectangleGradientV(0, y0, w, y1 - y0, c_clear, c_tint) + } + + if mid_h > 0 { + rl.DrawRectangle(0, y1, w, mid_h, c_tint) + } + + if fade_i > 0 { + rl.DrawRectangleGradientV(0, y2, w, y3 - y2, c_tint, c_clear) + } + + rl.EndBlendMode() +} + +draw_sheen_sweep :: proc( + font: rl.Font, + text: string, + center: rl.Vector2, + font_size: f32, + spacing: f32, + tint: rl.Color, + progress: f32, + core_frac: f32, + soft_frac: f32, + core_alpha: f32, + soft_alpha: f32, +) { + size := rl.MeasureTextEx(font, fmt.ctprintf("{}", text), font_size, spacing) + origin := rl.Vector2{size.x * 0.5, size.y * 0.5} + + left := center.x - origin.x + top := center.y - origin.y + width := size.x + height := size.y + + p := clampf(progress, 0, 1) + + x_center := left + width * p + core_w := width * core_frac + soft_w := width * soft_frac + + bright := rl.Color{255, 235, 210, 255} + + rl.BeginBlendMode(.ADDITIVE) + + rl.BeginScissorMode(i32(x_center - soft_w * 0.5), i32(top), i32(soft_w), i32(height)) + rl.DrawTextPro( + font, + fmt.ctprintf("{}", text), + center, + origin, + 0, + font_size, + spacing, + with_alpha(bright, soft_alpha), + ) + rl.EndScissorMode() + + rl.BeginScissorMode(i32(x_center - core_w * 0.5), i32(top), i32(core_w), i32(height)) + rl.DrawTextPro( + font, + fmt.ctprintf("{}", text), + center, + origin, + 0, + font_size, + spacing, + with_alpha(bright, core_alpha), + ) + rl.EndScissorMode() + + rl.EndBlendMode() +} + +main :: proc() { + mon := rl.GetCurrentMonitor() + w, h := rl.GetMonitorWidth(mon), rl.GetMonitorHeight(mon) + + rl.SetConfigFlags( + { + .WINDOW_TRANSPARENT, + .WINDOW_RESIZABLE, + .WINDOW_MOUSE_PASSTHROUGH, + .WINDOW_HIGHDPI, + .WINDOW_TOPMOST, + .WINDOW_UNFOCUSED, + .WINDOW_UNDECORATED, + .FULLSCREEN_MODE, + }, + ) + rl.InitWindow(w, h, "amongus") + + rl.InitAudioDevice() + music := rl.LoadMusicStreamFromMemory(".mp3", raw_data(SFX), i32(len(SFX))) + rl.PlayMusicStream(music) + + rt := rl.LoadRenderTexture(rl.GetScreenWidth(), rl.GetScreenHeight()) + rl.SetTextureFilter(rt.texture, .BILINEAR) + + font := rl.LoadFontFromMemory(".otf", raw_data(OTF), i32(len(OTF)), 64, nil, 0) + + text := "NIXOS REBUILT" + base_font_size0 := f32(128) + spacing := f32(2) + text_color := rl.Color{235, 200, 120, 255} + sheen_tint := rl.Color{255, 178, 153, 255} + + // base durations + fade_in_dur := f32(0.55) + scale_dur := f32(1.10) + sweep_delay := f32(0.20) + sweep_dur := f32(1.20) + glow_rise_dur := f32(0.70) + + post_fade_in_dur := f32(0.25) + post_hold_after_sweep := f32(0.35) + post_fade_out_dur := f32(0.60) + + DUR_SCALE :: f32(2.0) + + blur_base := f32(1.0) + blur_opacity_base := f32(0.08) + + // overlay appears 7s into the mp3 + OVERLAY_DELAY :: f32(5.0) + + SHEEN_GAIN :: 0.3 // overall intensity (0..1) + SHEEN_W_SCALE :: 0.5 // width scaler (<1 = thinner) + SHEEN_SOFT_ALPHA_CAP :: 0.10 // upper cap for soft halo + SHEEN_CORE_ALPHA_CAP :: 0.28 // upper cap for core + + prev_mt := f32(0) + ended := false + len_s := f32(rl.GetMusicTimeLength(music)) + + running := true + for !rl.WindowShouldClose() && running { + free_all(context.temp_allocator) + + if rl.IsWindowResized() { + rl.UnloadRenderTexture(rt) + rt = rl.LoadRenderTexture(rl.GetScreenWidth(), rl.GetScreenHeight()) + rl.SetTextureFilter(rt.texture, .BILINEAR) + } + + rl.UpdateMusicStream(music) + mt_raw := f32(rl.GetMusicTimePlayed(music)) + if !ended { + if mt_raw < prev_mt - 0.02 || + mt_raw >= len_s - 0.01 || + (!rl.IsMusicStreamPlaying(music) && mt_raw > 0) { + ended = true + rl.StopMusicStream(music) + } + } + mt := mt_raw + if ended { + mt = len_s + running = false + } + prev_mt = mt_raw + + visible := mt >= OVERLAY_DELAY + t := mt - OVERLAY_DELAY + if t < 0 {t = 0} + + fade_in := fade_in_dur * DUR_SCALE + scale_len := scale_dur * DUR_SCALE + sweep_dly := sweep_delay * DUR_SCALE + sweep_len := sweep_dur * DUR_SCALE + glow_len := glow_rise_dur * DUR_SCALE + post_in := post_fade_in_dur * DUR_SCALE + post_hold := post_hold_after_sweep * DUR_SCALE + post_out := post_fade_out_dur * DUR_SCALE + + alpha := ease_out_cubic(t / fade_in) + scale_bump := 0.06 * ease_out_back(t / scale_len, 1.3) + font_size := base_font_size0 * (1.0 + scale_bump) + + glow_in := ease_out_cubic(t / glow_len) + glow_pulse := 0.5 + 0.5 * f32(math.sin(t * 2.0)) + + sweep_end_time := sweep_dly + sweep_len + fade_out_start := sweep_end_time + post_hold + + blur_env := adsr( + t, + 0.18 * DUR_SCALE, // attack + 0.35 * DUR_SCALE, // decay + 0.70, // sustain level + fade_out_start, // release start + post_out, // release dur + ) + blur_size := blur_base * lerp(1.00, 1.30, ease_in_out_cubic(blur_env)) + blur_opacity := blur_opacity_base + blur_opacity *= (0.60 + 0.40 * blur_env) // grow with env + blur_opacity *= clampf(glow_in * (0.8 + 0.2 * glow_pulse), 0.0, 1.0) + + sheen_t := t - sweep_dly + + raw_env := adsr( + sheen_t, + 0.10 * DUR_SCALE, + 0.22 * DUR_SCALE, + 0.65, + sweep_len, + 0.28 * DUR_SCALE, + ) + sheen_env := math.pow(clampf(raw_env, 0, 1), 0.75) * SHEEN_GAIN + + core_frac := lerp(0.12, 0.18, sheen_env) * SHEEN_W_SCALE + soft_frac := lerp(0.24, 0.34, sheen_env) * SHEEN_W_SCALE + + core_alpha_dyn := math.min(lerp(0.10, 0.26, sheen_env), SHEEN_CORE_ALPHA_CAP) + soft_alpha_dyn := math.min(lerp(0.03, 0.09, sheen_env), SHEEN_SOFT_ALPHA_CAP) + + center := rl.Vector2{f32(rl.GetScreenWidth()) * 0.5, f32(rl.GetScreenHeight()) * 0.5} + + rl.BeginTextureMode(rt) + rl.ClearBackground(rl.BLANK) + + if visible { + draw_shadow_bar(center.y, 1.4, 0.95, -0.002, 0.5, rl.BLACK) + + draw_noun_verbed_sheen( + font, + text, + center, + font_size, + spacing, + text_color, + alpha, + sheen_tint, + blur_size, + blur_opacity, + ) + + sweep_p := clampf((t - sweep_dly) / sweep_len, 0, 1) + if sweep_p > 0 && sweep_p <= 1 { + draw_sheen_sweep( + font, + text, + center, + font_size, + spacing, + sheen_tint, + sweep_p, + core_frac, + soft_frac, + core_alpha_dyn, + soft_alpha_dyn, + ) + } + } + + rl.EndTextureMode() + + post_alpha := f32(1.0) + if !visible { + post_alpha = 0 + } else { + if t < post_in { + post_alpha = ease_out_cubic(t / post_in) + } else if t >= fade_out_start { + post_alpha = 1.0 - ease_out_cubic((t - fade_out_start) / post_out) + } + post_alpha = clampf(post_alpha, 0, 1) + } + + rl.BeginDrawing() + rl.ClearBackground(rl.BLANK) + + src := rl.Rectangle{0, 0, f32(rt.texture.width), -f32(rt.texture.height)} + dst := rl.Rectangle{0, 0, f32(rl.GetScreenWidth()), f32(rl.GetScreenHeight())} + rl.DrawTexturePro( + rt.texture, + src, + dst, + rl.Vector2{0, 0}, + 0, + with_alpha(rl.WHITE, post_alpha), + ) + + rl.EndDrawing() + } + + rl.UnloadRenderTexture(rt) + rl.UnloadFont(font) + rl.UnloadMusicStream(music) + rl.CloseAudioDevice() + rl.CloseWindow() +} diff --git a/sfx.mp3 b/sfx.mp3 new file mode 100644 index 0000000..62084ba Binary files /dev/null and b/sfx.mp3 differ