diff --git a/Ragnarokkr b/Ragnarokkr index 4a0095e..0e37e3b 100755 Binary files a/Ragnarokkr and b/Ragnarokkr differ diff --git a/gameover.odin b/gameover.odin index ce174d6..7470755 100644 --- a/gameover.odin +++ b/gameover.odin @@ -1,14 +1,15 @@ package main -import rl "vendor:raylib" -import "core:math/ease" import "core:fmt" +import "core:math/ease" +import "ntween" +import rl "vendor:raylib" GameOver :: struct { using state: GameState, - position: vec2, - size: vec2, + position: vec2, + size: vec2, ready_to_go: bool, } @@ -22,8 +23,16 @@ gameover_init :: proc(prev: ^GameState = nil) -> ^GameState { state.free = gameover_free state.previous = prev - tween_to(&state.position.y, WSize.y / 2, 1, ease.Ease.Back_Out, state, gameover_ready) - + ntween.animate( + &vec2_tweens, + &state.position, + WSize / 2, + 1, + ease.Ease.Back_Out, + state, + gameover_ready, + ) + return state } @@ -33,16 +42,24 @@ gameover_update :: proc(state: ^GameState, delta: f32) { if rl.IsKeyPressed(rl.KeyboardKey.ESCAPE) { gameover.ready_to_go = false rl.StopMusicStream(current_music) - tween_to(&Overlay_Opacity, 1.0, 0.5, ease.Ease.Cubic_Out, state, proc(data: rawptr) { - state := transmute(^GameState)data - stack_pop() - game := transmute(^Game)state.previous - game_setup(game) - menu := menu_init(game) - stack_push(menu) - free(state) - tween_to(&Overlay_Opacity, 0, 0.5, ease.Ease.Cubic_Out) - }) + ntween.animate( + &f32_tweens, + &Overlay_Opacity, + 1.0, + 0.5, + ease.Ease.Cubic_Out, + state, + proc(data: rawptr) { + state := transmute(^GameState)data + stack_pop() + game := transmute(^Game)state.previous + game_setup(game) + menu := menu_init(game) + stack_push(menu) + free(state) + ntween.animate(&f32_tweens, &Overlay_Opacity, 0, 0.5, ease.Ease.Cubic_Out) + }, + ) } } } @@ -50,7 +67,7 @@ gameover_update :: proc(state: ^GameState, delta: f32) { gameover_draw :: proc(state: ^GameState) { gameover := transmute(^GameOver)state - + if state.previous != nil { state.previous->draw() } @@ -63,12 +80,26 @@ gameover_draw :: proc(state: ^GameState) { SubtitleText := [?]cstring{"Нажмите Escape чтобы выйти"} SubtitleFontSize :: 48 - + // rl.DrawRectangleV(gameover.position - gameover.size / 2, gameover.size, rl.Color{90, 30, 150, 10}) - draw_text_centered(Res.Fonts.Title, TitleText, gameover.position - {0, 100}, TitleFontSize, 1, rl.WHITE) + draw_text_centered( + Res.Fonts.Title, + TitleText, + gameover.position - {0, 100}, + TitleFontSize, + 1, + rl.WHITE, + ) for c, i in SubtitleText { - draw_text_centered(Res.Fonts.UI, c, gameover.position - {0, f32(10 - i * 50)}, SubtitleFontSize, 1, rl.WHITE) + draw_text_centered( + Res.Fonts.UI, + c, + gameover.position - {0, f32(10 - i * 50)}, + SubtitleFontSize, + 1, + rl.WHITE, + ) // rl.DrawTextPro(Res.Fonts.UI, c, winning.position - {0, f32(10 - i * 50)}, SubtitleSizes[i] / 2, 0, SubtitleFontSize, SubtitleSpacing, rl.WHITE) } diff --git a/main.odin b/main.odin index 5602353..e359629 100644 --- a/main.odin +++ b/main.odin @@ -1,9 +1,10 @@ package main -import rl "vendor:raylib" +import "core:fmt" import "core:path/filepath" import "core:strings" -import "core:fmt" +import "ntween" +import rl "vendor:raylib" vec3 :: [3]f32 vec3i :: [3]i32 @@ -28,49 +29,49 @@ NeedTutorial := true KeyboardOnly := false -Overlay_Opacity : f32 = 0 +Overlay_Opacity: f32 = 0 Resources :: struct { - Fonts: struct { - UI: rl.Font, - Title: rl.Font, - }, - Models: struct { - PlayerModel: rl.Model, - SnakeHeadTop: rl.Model, - SnakeHeadJaw: rl.Model, - SnakeBody: rl.Model, - Background: rl.Model, - }, - Sfx: struct { - Drums: rl.Sound, - Lightning: rl.Sound, - LightningHit: rl.Sound, - Rocket: rl.Sound, - PlayerHit: rl.Sound, - PlayerSwoosh: rl.Sound, - PlayerDead: rl.Sound, - SnakeGrowl: rl.Sound, - SnakeRoarBlast: rl.Sound, - SnakeEarthHit: rl.Sound, - SnakeSegmentExplode: rl.Sound, - SnakeBeam: rl.Sound, - }, - Music: struct { - First: rl.Music, - Second: rl.Music, - } + Fonts: struct { + UI: rl.Font, + Title: rl.Font, + }, + Models: struct { + PlayerModel: rl.Model, + SnakeHeadTop: rl.Model, + SnakeHeadJaw: rl.Model, + SnakeBody: rl.Model, + Background: rl.Model, + }, + Sfx: struct { + Drums: rl.Sound, + Lightning: rl.Sound, + LightningHit: rl.Sound, + Rocket: rl.Sound, + PlayerHit: rl.Sound, + PlayerSwoosh: rl.Sound, + PlayerDead: rl.Sound, + SnakeGrowl: rl.Sound, + SnakeRoarBlast: rl.Sound, + SnakeEarthHit: rl.Sound, + SnakeSegmentExplode: rl.Sound, + SnakeBeam: rl.Sound, + }, + Music: struct { + First: rl.Music, + Second: rl.Music, + }, } -Res : Resources +Res: Resources -res_paths := map[typeid]string{ +res_paths := map[typeid]string { rl.Music = "music", rl.Sound = "sfx", rl.Model = "models", } -get_path :: proc(name: string, $T: typeid) -> cstring{ +get_path :: proc(name: string, $T: typeid) -> cstring { p := filepath.join([]string{"./assets", res_paths[T], name}) cstr := strings.clone_to_cstring(p) return cstr @@ -84,101 +85,110 @@ load_model :: proc(name: string) -> rl.Model { load_sfx :: proc(name: string, volume: f32 = 1) -> rl.Sound { p := get_path(name, rl.Sound) - snd := rl.LoadSound(p) - rl.SetSoundVolume(snd, volume) - return snd + snd := rl.LoadSound(p) + rl.SetSoundVolume(snd, volume) + return snd } load_music :: proc(name: string, volume: f32 = 1) -> rl.Music { p := get_path(name, rl.Music) - snd := rl.LoadMusicStream(p) - rl.SetMusicVolume(snd, volume) - return snd + snd := rl.LoadMusicStream(p) + rl.SetMusicVolume(snd, volume) + return snd } -current_music : rl.Music +current_music: rl.Music change_track :: proc(music: rl.Music) { - rl.StopMusicStream(current_music) - current_music = music - rl.PlayMusicStream(current_music) + rl.StopMusicStream(current_music) + current_music = music + rl.PlayMusicStream(current_music) } -Cursor : rl.Texture +Cursor: rl.Texture load_resources :: proc() { - Res.Fonts.Title = rl.LoadFontEx("./assets/fonts/norse.otf", 96*2, nil, 2048) - Res.Fonts.UI = rl.LoadFontEx("./assets/fonts/PTSerif-Regular.ttf", 96, nil, 2048) + Res.Fonts.Title = rl.LoadFontEx("./assets/fonts/norse.otf", 96 * 2, nil, 2048) + Res.Fonts.UI = rl.LoadFontEx("./assets/fonts/PTSerif-Regular.ttf", 96, nil, 2048) Res.Models = { - PlayerModel = load_model("chariot.glb"), - SnakeHeadTop = load_model("snake_head_top.obj"), - SnakeHeadJaw = load_model("snake_jaw.obj"), - SnakeBody = load_model("snake_body.obj"), - Background = load_model("background.obj"), + PlayerModel = load_model("chariot.glb"), + SnakeHeadTop = load_model("snake_head_top.obj"), + SnakeHeadJaw = load_model("snake_jaw.obj"), + SnakeBody = load_model("snake_body.obj"), + Background = load_model("background.obj"), } - Res.Sfx = { - Drums = load_sfx("drums.ogg"), - Lightning = load_sfx("lightning.ogg", 0.5), - LightningHit = load_sfx("lightning-hit.ogg", 0.3), - Rocket = load_sfx("rocket.ogg", 0.5), - PlayerHit = load_sfx("player-hit.ogg", 0.5), - PlayerSwoosh = load_sfx("player-swoosh.ogg"), - PlayerDead = load_sfx("player-dead.ogg"), - SnakeGrowl = load_sfx("snake-growl.ogg", 0.7), - SnakeRoarBlast = load_sfx("snake-roar-blast.ogg", 0.8), - SnakeBeam = load_sfx("snake-beam.ogg"), - SnakeSegmentExplode = load_sfx("snake-segment-explode.ogg", 0.8), - SnakeEarthHit = load_sfx("snake-earth-hit.ogg", 0.6), + Res.Sfx = { + Drums = load_sfx("drums.ogg"), + Lightning = load_sfx("lightning.ogg", 0.5), + LightningHit = load_sfx("lightning-hit.ogg", 0.3), + Rocket = load_sfx("rocket.ogg", 0.5), + PlayerHit = load_sfx("player-hit.ogg", 0.5), + PlayerSwoosh = load_sfx("player-swoosh.ogg"), + PlayerDead = load_sfx("player-dead.ogg"), + SnakeGrowl = load_sfx("snake-growl.ogg", 0.7), + SnakeRoarBlast = load_sfx("snake-roar-blast.ogg", 0.8), + SnakeBeam = load_sfx("snake-beam.ogg"), + SnakeSegmentExplode = load_sfx("snake-segment-explode.ogg", 0.8), + SnakeEarthHit = load_sfx("snake-earth-hit.ogg", 0.6), } - Res.Music.First = load_music("alexander-nakarada-mjolnir.mp3", 0.7) - Res.Music.Second = load_music("alexander-nakarada-the-northern-path.mp3", 0.7) + Res.Music.First = load_music("alexander-nakarada-mjolnir.mp3", 0.7) + Res.Music.Second = load_music("alexander-nakarada-the-northern-path.mp3", 0.7) } +f32_tweens: ntween.Tween_Map(f32) +vec2_tweens: ntween.Tween_Map(vec2) +vec3_tweens: ntween.Tween_Map(vec3) + main :: proc() { -// rl.SetConfigFlags(rl.ConfigFlags{.MSAA_4X_HINT, .FULLSCREEN_MODE, .VSYNC_HINT, .WINDOW_RESIZABLE}) - rl.SetConfigFlags(rl.ConfigFlags{.VSYNC_HINT, .WINDOW_RESIZABLE}) - - rl.InitWindow(800, 600, "Ragnarøkkr") - rl.InitAudioDevice() - rl.SetWindowMinSize(800, 600) + f32_tweens = ntween.init(f32) + vec2_tweens = ntween.init(vec2) + vec3_tweens = ntween.init(vec3) + // rl.SetConfigFlags(rl.ConfigFlags{.MSAA_4X_HINT, .FULLSCREEN_MODE, .VSYNC_HINT, .WINDOW_RESIZABLE}) + rl.SetConfigFlags(rl.ConfigFlags{.VSYNC_HINT, .WINDOW_RESIZABLE}) - rl.HideCursor() - Cursor = rl.LoadTexture("./assets/gfx/crosshair.png") - load_resources() + rl.InitWindow(800, 600, "Ragnarøkkr") + rl.InitAudioDevice() + rl.SetWindowMinSize(800, 600) - WSizei = {rl.GetScreenWidth(), rl.GetScreenHeight()} - WSize = {f32(WSizei.x), f32(WSizei.y)} + rl.HideCursor() + Cursor = rl.LoadTexture("./assets/gfx/crosshair.png") + load_resources() - game := game_init() - stack_push(game) - menu := menu_init(game) - stack_push(menu) + WSizei = {rl.GetScreenWidth(), rl.GetScreenHeight()} + WSize = {f32(WSizei.x), f32(WSizei.y)} - for !WindowShouldExit { - if rl.IsWindowResized() { - WSizei = {rl.GetScreenWidth(), rl.GetScreenHeight()} - WSize = {f32(WSizei.x), f32(WSizei.y)} - } + game := game_init() + stack_push(game) + menu := menu_init(game) + stack_push(menu) - rl.UpdateMusicStream(current_music) + for !WindowShouldExit { + if rl.IsWindowResized() { + WSizei = {rl.GetScreenWidth(), rl.GetScreenHeight()} + WSize = {f32(WSizei.x), f32(WSizei.y)} + } - state := stack_top() - delta := rl.GetFrameTime() - timers_process(delta) - tweens_process(delta) - state->update(delta) + rl.UpdateMusicStream(current_music) - rl.BeginDrawing() - rl.ClearBackground(rl.SKYBLUE) - state->draw() + state := stack_top() + delta := rl.GetFrameTime() + timers_process(delta) + ntween.process(&f32_tweens, delta) + ntween.process(&vec2_tweens, delta) + ntween.process(&vec3_tweens, delta) + state->update(delta) - rl.DrawRectangleV({}, WSize, rl.Color{0, 0, 0, u8(Overlay_Opacity * 255)}) - pos := rl.GetMousePosition() - rl.DrawTextureEx(Cursor, pos - {16, 16} * 3, 0, 3, rl.WHITE) - rl.EndDrawing() - } + rl.BeginDrawing() + rl.ClearBackground(rl.SKYBLUE) + state->draw() + + rl.DrawRectangleV({}, WSize, rl.Color{0, 0, 0, u8(Overlay_Opacity * 255)}) + pos := rl.GetMousePosition() + rl.DrawTextureEx(Cursor, pos - {16, 16} * 3, 0, 3, rl.WHITE) + rl.EndDrawing() + } } Fullscreen := false @@ -186,10 +196,10 @@ Fullscreen := false toggle_fullscreen :: proc() { monitor := rl.GetCurrentMonitor() rl.ToggleBorderlessWindowed() - WSizei = {rl.GetScreenWidth(), rl.GetScreenHeight()} - WSize = {f32(WSizei.x), f32(WSizei.y)} + WSizei = {rl.GetScreenWidth(), rl.GetScreenHeight()} + WSize = {f32(WSizei.x), f32(WSizei.y)} Fullscreen = !Fullscreen - -// rl.ToggleFullscreen() + + // rl.ToggleFullscreen() } diff --git a/menu_list.odin b/menu_list.odin index 31f37d8..fedc2f3 100644 --- a/menu_list.odin +++ b/menu_list.odin @@ -1,8 +1,9 @@ package main -import rl "vendor:raylib" -import "core:math/ease" import "core:fmt" +import "core:math/ease" +import "ntween" +import rl "vendor:raylib" MenuItemType :: enum { NONE, @@ -11,28 +12,28 @@ MenuItemType :: enum { } BoolStrings := map[bool]cstring { - true = "вкл", - false = "выкл" + true = "вкл", + false = "выкл", } -MenuItem :: struct{ - text: cstring, +MenuItem :: struct { + text: cstring, param: rawptr, - type: MenuItemType, + type: MenuItemType, } MenuList :: struct($T: typeid) { - state: ^GameState, - position: vec2, - line_size: f32, - font_size: f32, + state: ^GameState, + position: vec2, + line_size: f32, + font_size: f32, active_element: T, - active_marker: vec2, - tween: ^Tween, - elements: ^[T]MenuItem, - menu_pressed: proc(state: ^GameState, element: T), - background: rl.Color, - mouse_pos: vec2, + active_marker: vec2, + tween: ^ntween.Tween(f32), + elements: ^[T]MenuItem, + menu_pressed: proc(state: ^GameState, element: T), + background: rl.Color, + mouse_pos: vec2, } @@ -46,14 +47,12 @@ menu_list_update :: proc(list: ^MenuList($T)) { list.mouse_pos = rl.GetMousePosition() size := menu_list_get_size(list) - if rl.CheckCollisionPointRec(list.mouse_pos, rl.Rectangle{ - x = list.position.x, - y = list.position.y, - width = size.x, - height = size.y, - }) { + if rl.CheckCollisionPointRec( + list.mouse_pos, + rl.Rectangle{x = list.position.x, y = list.position.y, width = size.x, height = size.y}, + ) { if last_mouse_pos != list.mouse_pos { - mouse_relative := list.mouse_pos - list.position + mouse_relative := list.mouse_pos - list.position cur_element = i8(mouse_relative.y / list.line_size) } if rl.IsMouseButtonPressed(rl.MouseButton.LEFT) { @@ -63,7 +62,6 @@ menu_list_update :: proc(list: ^MenuList($T)) { last_mouse_pos = list.mouse_pos - if rl.IsKeyPressed(rl.KeyboardKey.DOWN) { cur_element += 1 } @@ -71,13 +69,14 @@ menu_list_update :: proc(list: ^MenuList($T)) { cur_element -= 1 } if prev_element != cur_element { - if cur_element < 0 { cur_element = len(T) -1 } - if cur_element == len(T) { cur_element = 0 } + if cur_element < 0 {cur_element = len(T) - 1} + if cur_element == len(T) {cur_element = 0} list.active_element = cast(T)cur_element if list.tween != nil { - tween_cancel(list.tween) + ntween.cancel(list.tween) } - list.tween = tween_to( + list.tween = ntween.animate( + &f32_tweens, &list.active_marker.y, f32(list.active_element) * list.line_size, 0.25, @@ -94,10 +93,17 @@ menu_list_draw :: proc(list: ^MenuList($T)) { size := menu_list_get_size(list) rl.DrawRectangleV(list.position - {40, 40}, size + {80, 80}, list.background) } - rl.DrawTextEx(Res.Fonts.UI, ">", list.position + list.active_marker + {-30, 0}, 48, 2, rl.WHITE) + rl.DrawTextEx( + Res.Fonts.UI, + ">", + list.position + list.active_marker + {-30, 0}, + 48, + 2, + rl.WHITE, + ) for el, i in list.elements { pos := list.position + {0, f32(i) * list.line_size} - text := el.text + text := el.text if el.type == .BOOL { param := transmute(^bool)el.param value := param^ diff --git a/ntween b/ntween new file mode 160000 index 0000000..7136c68 --- /dev/null +++ b/ntween @@ -0,0 +1 @@ +Subproject commit 7136c68f3e63470b9b1c80109dd0904151e08070 diff --git a/pause.odin b/pause.odin index c0fb876..b04f027 100644 --- a/pause.odin +++ b/pause.odin @@ -1,51 +1,50 @@ package main -import rl "vendor:raylib" import "core:math/ease" +import "ntween" +import rl "vendor:raylib" Pause_Buttons :: enum { CONTINUE, - EXIT + EXIT, } pause_strings := [Pause_Buttons]cstring { .CONTINUE = "Продолжить", - .EXIT = "Прервать игру" + .EXIT = "Прервать игру", } pause_items := [Pause_Buttons]MenuItem { .CONTINUE = {text = pause_strings[.CONTINUE]}, - .EXIT = {text = pause_strings[.EXIT]} + .EXIT = {text = pause_strings[.EXIT]}, } Pause :: struct { using state: GameState, - - active: bool, - list: MenuList(Pause_Buttons), - + active: bool, + list: MenuList(Pause_Buttons), } pause_init :: proc(prev: ^GameState = nil) -> ^GameState { state := new(Pause) state.variant = state - state.list = MenuList(Pause_Buttons){ - state = state, - position = {-300, WSize.y / 2}, - line_size = 60, - font_size = 48, - elements = &pause_items, + state.list = MenuList(Pause_Buttons) { + state = state, + position = {-300, WSize.y / 2}, + line_size = 60, + font_size = 48, + elements = &pause_items, menu_pressed = pause_button_pressed, - background = rl.Color{50, 10, 110, 0} + background = rl.Color{50, 10, 110, 0}, } state.update = pause_update - state.draw = pause_draw + state.draw = pause_draw state.free = pause_free state.previous = prev - state.active = true + state.active = true - tween_to(&state.list.position.x, 100, 0.5, ease.Ease.Back_Out) + ntween.animate(&f32_tweens, &state.list.position.x, 100, 0.5, ease.Ease.Back_Out) return state } @@ -53,20 +52,27 @@ pause_init :: proc(prev: ^GameState = nil) -> ^GameState { pause_update :: proc(state: ^GameState, delta: f32) { pause := transmute(^Pause)state pause.list.position.y = WSize.y / 2 - + menu_list_update(&pause.list) } pause_button_pressed :: proc(state: ^GameState, el: Pause_Buttons) { pause := transmute(^Pause)state - if !pause.active { return } - switch el { - case .CONTINUE: - stack_pop() - case .EXIT: - pause.active = false - rl.StopMusicStream(current_music) - tween_to(&Overlay_Opacity, 1.0, 0.5, ease.Ease.Cubic_Out, state, proc(data: rawptr) { + if !pause.active {return} + switch el { + case .CONTINUE: + stack_pop() + case .EXIT: + pause.active = false + rl.StopMusicStream(current_music) + ntween.animate( + &f32_tweens, + &Overlay_Opacity, + 1.0, + 0.5, + ease.Ease.Cubic_Out, + state, + proc(data: rawptr) { state := transmute(^GameState)data stack_pop() game := transmute(^Game)stack_top() @@ -74,21 +80,31 @@ pause_button_pressed :: proc(state: ^GameState, el: Pause_Buttons) { menu := menu_init(game) stack_push(menu) //free(state) - tween_to(&Overlay_Opacity, 0, 0.5, ease.Ease.Cubic_Out) - }) + ntween.animate(&f32_tweens, &Overlay_Opacity, 0, 0.5, ease.Ease.Cubic_Out) + }, + ) } } pause_draw :: proc(state: ^GameState) { pause := transmute(^Pause)state - pause.previous.draw(pause.previous) + pause.previous.draw(pause.previous) TitleFontSize :: 96 TitleSpacing :: 3 TitleText :: "Ragnarøkkr" TitleSize := rl.MeasureTextEx(Res.Fonts.Title, TitleText, TitleFontSize, TitleSpacing) - rl.DrawTextPro(Res.Fonts.Title, TitleText, {WSize.x - 50, 50}, {TitleSize.x, 0}, 0, 96, 3, rl.WHITE) + rl.DrawTextPro( + Res.Fonts.Title, + TitleText, + {WSize.x - 50, 50}, + {TitleSize.x, 0}, + 0, + 96, + 3, + rl.WHITE, + ) menu_list_draw(&pause.list) diff --git a/player.odin b/player.odin index f3efb6a..5f53b77 100644 --- a/player.odin +++ b/player.odin @@ -1,270 +1,280 @@ package main +import "core:fmt" +import "core:math" +import "core:math/ease" +import "core:math/rand" +import "core:slice" +import "core:strings" +import "ntween" import rl "vendor:raylib" import "vendor:raylib/rlgl" -import "core:math" -import "core:strings" -import "core:math/rand" -import "core:math/ease" -import "core:fmt" -import "core:slice" - // PlayerAnims : [^]rl.ModelAnimation // PlayerAnimsCount : i32 Player :: struct { - pos: vec3, - vel: vec3, - dir: f32, - radius: f32, - thrust: f32, - max_speed: f32, - rolling: f32, - charge: f32, - is_dodging: bool, - can_dodge: bool, - can_shoot: bool, - is_invulnerable: bool, - is_dead: bool, - intro_timer: f32, - power: f32, - reload_timer: f32, - reloading: bool, - // animation: rl.ModelAnimation, - // animTime: f32, - // animFrame: i32, + pos: vec3, + vel: vec3, + dir: f32, + radius: f32, + thrust: f32, + max_speed: f32, + rolling: f32, + charge: f32, + is_dodging: bool, + can_dodge: bool, + can_shoot: bool, + is_invulnerable: bool, + is_dead: bool, + intro_timer: f32, + power: f32, + reload_timer: f32, + reloading: bool, + // animation: rl.ModelAnimation, + // animTime: f32, + // animFrame: i32, } player_spawn :: proc(position: vec3) -> Player { - // PlayerAnims = rl.LoadModelAnimations(PlayerModelPath, &PlayerAnimsCount) - return Player{ - pos = position, - radius = 1, - max_speed = 40, - dir = 0, - vel = {-80, 0, 0}, - can_dodge = true, - can_shoot = true, - intro_timer = 2, - power = 100, - } + // PlayerAnims = rl.LoadModelAnimations(PlayerModelPath, &PlayerAnimsCount) + return Player { + pos = position, + radius = 1, + max_speed = 40, + dir = 0, + vel = {-80, 0, 0}, + can_dodge = true, + can_shoot = true, + intro_timer = 2, + power = 100, + } } player_update :: proc(player: ^Player, game: ^Game, delta: f32) { - using player + using player - if intro_timer > 0 { - intro_timer -= delta - } + if intro_timer > 0 { + intro_timer -= delta + } - - mouse_ray := rl.GetMouseRay(rl.GetMousePosition(), game.camera) - mouse_pos : vec3 - hit := rl.GetRayCollisionQuad(mouse_ray, - {-1000, -1000, 0}, - {-1000, 1000, 0}, - {1000, 1000, 0}, - {1000, -1000, 0} - ) - if hit.hit { - mouse_pos = hit.point - } - - mouse_diff := mouse_pos - pos - mouse_angle := math.atan2(-mouse_diff.y, mouse_diff.x) - if !is_dead { - pos += vel * delta - if pos.y < radius { - pos.y = radius - vel.y = - vel.y - } - if !is_dodging { - dir_vector : vec3 - if intro_timer <= 0 { - thrust_key := rl.KeyboardKey.W - if !KeyboardOnly { - dir = angle_rotate(dir, mouse_angle, math.PI * 2 * delta) - dir_vector = get_vec_from_angle(dir) - } else { - thrust_key = rl.KeyboardKey.UP - if rl.IsKeyDown(rl.KeyboardKey.LEFT) { - dir += math.PI * 2 * delta - } - if rl.IsKeyDown(rl.KeyboardKey.RIGHT) { - dir -= math.PI * 2 * delta - } - dir_vector = get_vec_from_angle(dir) - } - thrust = 0 - if rl.IsKeyDown(thrust_key) { - thrust = 110 - } - } else { - thrust = 110 - dir_vector = vec3left - dir = math.atan2(-dir_vector.y, dir_vector.x) - } - if thrust > 0 { - roll := -math.PI / 2 + math.cos(dir) * math.PI / 2 - vel = rl.Vector3MoveTowards(vel, dir_vector * max_speed, thrust * delta) - offset := rl.Vector3RotateByAxisAngle(vec3backward, vec3right, roll) * 2.6 - offset = rl.Vector3RotateByAxisAngle(offset, vec3backward, dir) - pl := pos + offset - pr := pos - offset - trail(pl, 1, rand.float32_range(1.7, 3.5)) - trail(pr, 1, rand.float32_range(1.7, 3.5)) - } - } - if thrust == 0 { - vel = rl.Vector3MoveTowards(vel, {0, -30, 0}, 20 * delta) - rl.StopSound(Res.Sfx.Rocket) - } else { - if !rl.IsSoundPlaying(Res.Sfx.Rocket) && !is_dodging { - rl.PlaySound(Res.Sfx.Rocket) - } - } + mouse_ray := rl.GetMouseRay(rl.GetMousePosition(), game.camera) + mouse_pos: vec3 + hit := rl.GetRayCollisionQuad( + mouse_ray, + {-1000, -1000, 0}, + {-1000, 1000, 0}, + {1000, 1000, 0}, + {1000, -1000, 0}, + ) + if hit.hit { + mouse_pos = hit.point + } - dodge := false - shoot := false - if KeyboardOnly { - dodge = rl.IsKeyPressed(rl.KeyboardKey.LEFT_SHIFT) - shoot = rl.IsKeyDown(rl.KeyboardKey.SPACE) - } else { - dodge = rl.IsMouseButtonPressed(rl.MouseButton.RIGHT) - shoot = rl.IsMouseButtonDown(rl.MouseButton.LEFT) - } + mouse_diff := mouse_pos - pos + mouse_angle := math.atan2(-mouse_diff.y, mouse_diff.x) - if dodge && can_dodge && intro_timer <= 0 { - is_dodging = true - can_dodge = false - rl.StopSound(Res.Sfx.Rocket) - rl.PlaySound(Res.Sfx.PlayerSwoosh) - tween_to(&player.rolling, math.PI*2, 0.42, ease.Ease.Quadratic_Out) - timer_start(0.45, player, proc(data: rawptr) { - player := transmute(^Player)data - player.is_dodging = false - player.rolling = 0 - }) - timer_start(0.55, player, proc(data: rawptr) { - player := transmute(^Player)data - player.can_dodge = true - }) - } + if !is_dead { + pos += vel * delta + if pos.y < radius { + pos.y = radius + vel.y = -vel.y + } + if !is_dodging { + dir_vector: vec3 + if intro_timer <= 0 { + thrust_key := rl.KeyboardKey.W + if !KeyboardOnly { + dir = angle_rotate(dir, mouse_angle, math.PI * 2 * delta) + dir_vector = get_vec_from_angle(dir) + } else { + thrust_key = rl.KeyboardKey.UP + if rl.IsKeyDown(rl.KeyboardKey.LEFT) { + dir += math.PI * 2 * delta + } + if rl.IsKeyDown(rl.KeyboardKey.RIGHT) { + dir -= math.PI * 2 * delta + } + dir_vector = get_vec_from_angle(dir) + } + thrust = 0 + if rl.IsKeyDown(thrust_key) { + thrust = 110 + } + } else { + thrust = 110 + dir_vector = vec3left + dir = math.atan2(-dir_vector.y, dir_vector.x) + } + if thrust > 0 { + roll := -math.PI / 2 + math.cos(dir) * math.PI / 2 + vel = rl.Vector3MoveTowards(vel, dir_vector * max_speed, thrust * delta) + offset := rl.Vector3RotateByAxisAngle(vec3backward, vec3right, roll) * 2.6 + offset = rl.Vector3RotateByAxisAngle(offset, vec3backward, dir) + pl := pos + offset + pr := pos - offset + trail(pl, 1, rand.float32_range(1.7, 3.5)) + trail(pr, 1, rand.float32_range(1.7, 3.5)) + } + } + if thrust == 0 { + vel = rl.Vector3MoveTowards(vel, {0, -30, 0}, 20 * delta) + rl.StopSound(Res.Sfx.Rocket) + } else { + if !rl.IsSoundPlaying(Res.Sfx.Rocket) && !is_dodging { + rl.PlaySound(Res.Sfx.Rocket) + } + } - // player.animation = PlayerAnims[1] - // player.animTime += delta - // player.animFrame = i32(player.animTime * 60) % player.animation.frameCount - // rl.UpdateModelAnimation(PlayerModel, player.animation, player.animFrame) - shooting := shoot && !is_dodging && intro_timer <= 0 && !reloading - if shooting { - if !rl.IsSoundPlaying(Res.Sfx.Lightning) { - rl.PlaySound(Res.Sfx.Lightning) - } - if can_shoot { - roll := -math.PI / 2 + math.cos(dir) * math.PI / 2 - b := bullet_spawn(pos + get_vec_from_angle(dir) * 3 + get_vec_from_angle(dir+math.PI/2)*.3, dir) - append(&game.bullets, b) - can_shoot = false - timer_start(0.07, player, proc(data: rawptr) { - player := transmute(^Player)data - player.can_shoot = true - }) - player.power -= 2 - player.reload_timer = 1 - if player.power <= 0 { - player.reloading = true - player.power = 0 - } - } - } else { - rl.StopSound(Res.Sfx.Lightning) - } - if !shooting && can_shoot { - reload_timer -= delta - if reload_timer <= 0 { - player.power += 20 * delta - if player.power > 100 { - player.power = 100 - player.reloading = false - } - } - } - } + dodge := false + shoot := false + if KeyboardOnly { + dodge = rl.IsKeyPressed(rl.KeyboardKey.LEFT_SHIFT) + shoot = rl.IsKeyDown(rl.KeyboardKey.SPACE) + } else { + dodge = rl.IsMouseButtonPressed(rl.MouseButton.RIGHT) + shoot = rl.IsMouseButtonDown(rl.MouseButton.LEFT) + } - got_hit := false + if dodge && can_dodge && intro_timer <= 0 { + is_dodging = true + can_dodge = false + rl.StopSound(Res.Sfx.Rocket) + rl.PlaySound(Res.Sfx.PlayerSwoosh) + ntween.animate( + &f32_tweens, + &player.rolling, + math.PI * 2, + 0.42, + ease.Ease.Quadratic_Out, + ) + timer_start(0.45, player, proc(data: rawptr) { + player := transmute(^Player)data + player.is_dodging = false + player.rolling = 0 + }) + timer_start(0.55, player, proc(data: rawptr) { + player := transmute(^Player)data + player.can_dodge = true + }) + } - hit: if !is_invulnerable && !is_dodging && !is_dead { - if rl.CheckCollisionCircles(pos.xy, radius, Head.pos.xy, Head.radius) { - got_hit = true - break hit - } - ray := rl.Ray{ - position = Head.pos, - direction = get_vec_from_angle(Head.dir) - } - if Head.is_shooting && rl.GetRayCollisionSphere(ray, pos, radius + 3).hit { - got_hit = true - break hit - } - for segment in Segments { - if rl.CheckCollisionCircles(pos.xy, radius, segment.pos.xy, radius) { - got_hit = true - break - } - } - } - if rl.IsKeyPressed(rl.KeyboardKey.M) { - got_hit = true - } + // player.animation = PlayerAnims[1] + // player.animTime += delta + // player.animFrame = i32(player.animTime * 60) % player.animation.frameCount + // rl.UpdateModelAnimation(PlayerModel, player.animation, player.animFrame) + shooting := shoot && !is_dodging && intro_timer <= 0 && !reloading + if shooting { + if !rl.IsSoundPlaying(Res.Sfx.Lightning) { + rl.PlaySound(Res.Sfx.Lightning) + } + if can_shoot { + roll := -math.PI / 2 + math.cos(dir) * math.PI / 2 + b := bullet_spawn( + pos + get_vec_from_angle(dir) * 3 + get_vec_from_angle(dir + math.PI / 2) * .3, + dir, + ) + append(&game.bullets, b) + can_shoot = false + timer_start(0.07, player, proc(data: rawptr) { + player := transmute(^Player)data + player.can_shoot = true + }) + player.power -= 2 + player.reload_timer = 1 + if player.power <= 0 { + player.reloading = true + player.power = 0 + } + } + } else { + rl.StopSound(Res.Sfx.Lightning) + } + if !shooting && can_shoot { + reload_timer -= delta + if reload_timer <= 0 { + player.power += 20 * delta + if player.power > 100 { + player.power = 100 + player.reloading = false + } + } + } + } + + got_hit := false + + hit: if !is_invulnerable && !is_dodging && !is_dead { + if rl.CheckCollisionCircles(pos.xy, radius, Head.pos.xy, Head.radius) { + got_hit = true + break hit + } + ray := rl.Ray { + position = Head.pos, + direction = get_vec_from_angle(Head.dir), + } + if Head.is_shooting && rl.GetRayCollisionSphere(ray, pos, radius + 3).hit { + got_hit = true + break hit + } + for segment in Segments { + if rl.CheckCollisionCircles(pos.xy, radius, segment.pos.xy, radius) { + got_hit = true + break + } + } + } + if rl.IsKeyPressed(rl.KeyboardKey.M) { + got_hit = true + } + + if got_hit { + game.health -= 10 + rl.PlaySound(Res.Sfx.PlayerHit) + is_invulnerable = true + timer_start(1, player, proc(data: rawptr) { + plr := transmute(^Player)data + plr.is_invulnerable = false + }) + if game.health <= 0 && !is_dead { + is_dead = true + explode(pos, 10, 0.8, rl.WHITE) + rl.StopMusicStream(current_music) + rl.PlaySound(Res.Sfx.PlayerDead) + timer_start(3, game, proc(data: rawptr) { + state := transmute(^Game)data + screen := gameover_init(state) + stack_push(screen) + }) + } + } - if got_hit { - game.health -= 10 - rl.PlaySound(Res.Sfx.PlayerHit) - is_invulnerable = true - timer_start(1, player, proc(data: rawptr) { - plr := transmute(^Player)data - plr.is_invulnerable = false - }) - if game.health <= 0 && !is_dead { - is_dead = true - explode(pos, 10, 0.8, rl.WHITE) - rl.StopMusicStream(current_music) - rl.PlaySound(Res.Sfx.PlayerDead) - timer_start(3, game, proc(data: rawptr) { - state := transmute(^Game)data - screen := gameover_init(state) - stack_push(screen) - }) - } - } - } player_draw :: proc(player: ^Player) { - using player - if player.is_dead { return } - dir_vector := get_vec_from_angle(dir) - color := rl.WHITE - if is_invulnerable { - color = rl.Color{255, 170, 170, 255} - } + using player + if player.is_dead {return} + dir_vector := get_vec_from_angle(dir) + color := rl.WHITE + if is_invulnerable { + color = rl.Color{255, 170, 170, 255} + } - roll := -math.PI / 2 + math.cos(dir) * math.PI / 2 - rlgl.PushMatrix() - rlgl.Translatef(pos.x, pos.y, pos.z) - rlgl.Rotatef(math.to_degrees(dir), 0, 0, -1) - rlgl.Rotatef(math.to_degrees(roll + player.rolling), 1, 0, 0) - // rl.DrawCircle3D({}, radius, vec3up, 0, color) - // rl.DrawLine3D({}, {-4, 0, 0}, rl.GREEN) - rl.DrawModel(Res.Models.PlayerModel, {}, 6, color) - rlgl.PopMatrix() - // rl.DrawLine3D(pos, pos + dir_vector * radius, rl.BLACK) - // rl.DrawLine3D(pos, pos + vel, rl.RED) + roll := -math.PI / 2 + math.cos(dir) * math.PI / 2 + rlgl.PushMatrix() + rlgl.Translatef(pos.x, pos.y, pos.z) + rlgl.Rotatef(math.to_degrees(dir), 0, 0, -1) + rlgl.Rotatef(math.to_degrees(roll + player.rolling), 1, 0, 0) + // rl.DrawCircle3D({}, radius, vec3up, 0, color) + // rl.DrawLine3D({}, {-4, 0, 0}, rl.GREEN) + rl.DrawModel(Res.Models.PlayerModel, {}, 6, color) + rlgl.PopMatrix() + // rl.DrawLine3D(pos, pos + dir_vector * radius, rl.BLACK) + // rl.DrawLine3D(pos, pos + vel, rl.RED) } diff --git a/tween.odin b/tween.odin deleted file mode 100644 index 08f42d3..0000000 --- a/tween.odin +++ /dev/null @@ -1,80 +0,0 @@ -package main - -import "core:math" -import "core:math/ease" -import "core:math/linalg" -import "core:slice" - - -Tween :: struct { - ptr: ^f32, - from: f32, - to: f32, - time: f32, - duration: f32, - ease_type: ease.Ease, - active: bool, - finished: proc(data: rawptr), - data: rawptr -} - - -tweens : [dynamic]^Tween - - -tween_clean :: proc() { - for tween, i in tweens { - free(tween) - } -} - -tween_to :: proc( - value: ^f32, to: f32, duration: f32, - ease: ease.Ease = ease.Ease.Quartic_In_Out, - data: rawptr = nil, - callback: proc(data: rawptr) = nil - ) -> ^Tween { - - tween := new(Tween) - tween.ptr = value - tween.from = value^ - tween.to = to - tween.duration = duration - tween.ease_type = ease - tween.active = true - tween.data = data - tween.finished = callback - - append(&tweens, tween) - return tween -} - -tween_cancel :: proc(t: ^Tween) { - t.active = false -} - -tweens_process :: proc(delta: f32) { - #reverse for tween, i in tweens { - - tween.time += delta - p := clamp(tween.time / tween.duration, 0, 1) - - val := ease.ease(tween.ease_type, p) - if tween.ptr != nil { - tween.ptr^ = math.lerp(tween.from, tween.to, val) - } else { - tween.active = false - } - - if tween.time >= tween.duration { - tween.active = false - if tween.finished != nil { - tween.finished(tween.data) - } - } - if !tween.active { - free(tween) - unordered_remove(&tweens, i) - } - } -} diff --git a/winning.odin b/winning.odin index cb74178..c9d961f 100644 --- a/winning.odin +++ b/winning.odin @@ -1,14 +1,15 @@ package main -import rl "vendor:raylib" -import "core:math/ease" import "core:fmt" +import "core:math/ease" +import "ntween" +import rl "vendor:raylib" Winning :: struct { using state: GameState, - position: vec2, - size: vec2, + position: vec2, + size: vec2, ready_to_go: bool, } @@ -22,8 +23,16 @@ winning_init :: proc(prev: ^GameState = nil) -> ^GameState { state.free = winning_free state.previous = prev - tween_to(&state.position.y, WSize.y / 2, 1, ease.Ease.Back_Out, state, winning_ready) - + ntween.animate( + &vec2_tweens, + &state.position, + WSize / 2, + 1, + ease.Ease.Back_Out, + state, + winning_ready, + ) + return state } @@ -33,16 +42,24 @@ winning_update :: proc(state: ^GameState, delta: f32) { if rl.IsKeyPressed(rl.KeyboardKey.ESCAPE) { winning.ready_to_go = false rl.StopMusicStream(current_music) - tween_to(&Overlay_Opacity, 1.0, 0.5, ease.Ease.Cubic_Out, nil, proc(data: rawptr) { - state := transmute(^GameState)data - stack_pop() - game := transmute(^Game)state.previous - game_setup(game) - menu := menu_init(game) - stack_push(menu) - free(state) - tween_to(&Overlay_Opacity, 0, 0.5, ease.Ease.Cubic_Out) - }) + ntween.animate( + &f32_tweens, + &Overlay_Opacity, + 1.0, + 0.5, + ease.Ease.Cubic_Out, + nil, + proc(data: rawptr) { + state := transmute(^GameState)data + stack_pop() + game := transmute(^Game)state.previous + game_setup(game) + menu := menu_init(game) + stack_push(menu) + free(state) + ntween.animate(&f32_tweens, &Overlay_Opacity, 0, 0.5, ease.Ease.Cubic_Out) + }, + ) } } } @@ -50,7 +67,7 @@ winning_update :: proc(state: ^GameState, delta: f32) { winning_draw :: proc(state: ^GameState) { winning := transmute(^Winning)state - + if state.previous != nil { state.previous->draw() } @@ -59,15 +76,33 @@ winning_draw :: proc(state: ^GameState) { TitleSpacing :: 3 TitleText :: "GAME OVER" - SubtitleText := [?]cstring{"Тор смог спасти Асгард", "от Рагнарёка!", "Нажмите Escape чтобы выйти"} + SubtitleText := [?]cstring { + "Тор смог спасти Асгард", + "от Рагнарёка!", + "Нажмите Escape чтобы выйти", + } SubtitleFontSize :: 48 - + rl.DrawRectangleV(winning.position - winning.size / 2, winning.size, rl.Color{90, 30, 150, 10}) - draw_text_centered(Res.Fonts.Title, TitleText, winning.position - {0, 100}, TitleFontSize, 1, rl.WHITE) + draw_text_centered( + Res.Fonts.Title, + TitleText, + winning.position - {0, 100}, + TitleFontSize, + 1, + rl.WHITE, + ) for c, i in SubtitleText { - draw_text_centered(Res.Fonts.UI, c, winning.position - {0, f32(10 - i * 50)}, SubtitleFontSize, 1, rl.WHITE) + draw_text_centered( + Res.Fonts.UI, + c, + winning.position - {0, f32(10 - i * 50)}, + SubtitleFontSize, + 1, + rl.WHITE, + ) // rl.DrawTextPro(Res.Fonts.UI, c, winning.position - {0, f32(10 - i * 50)}, SubtitleSizes[i] / 2, 0, SubtitleFontSize, SubtitleSpacing, rl.WHITE) }