Ragnarokkr/game.odin

235 lines
5.9 KiB
Odin
Raw Normal View History

2024-10-04 21:16:58 +03:00
package main
import rl "vendor:raylib"
import "vendor:raylib/rlgl"
import "core:fmt"
import "core:math"
import "core:math/ease"
import "core:math/rand"
import "core:strings"
import "core:strconv"
// Virtual game field dimensions
GameField := vec2{400, 200}
Game :: struct {
using state: GameState,
health: u8,
player: Player,
camera: rl.Camera3D,
camera_offset: vec3,
score: u32,
bullets: [dynamic]Bullet,
snake_max_health: int,
2024-10-05 20:44:32 +03:00
snake_health: int,
music: rl.Music,
tutorial_enabled: bool,
tutorial_finished: bool,
tutorial_timer: f32,
tutorial_step: TutorSteps
2024-10-04 21:16:58 +03:00
}
TutorSteps :: enum {
THRUST,
LOOK,
SHOOT,
DODGE,
DONE
}
TutorTextsMouse := [TutorSteps]cstring{
.THRUST = "Используйте W/Ц для ускорения",
.LOOK = "Двигайте мышью для наведения",
.SHOOT = "Левая кнопка мыши: стрельба",
.DODGE = "Правая кнопка мыши: уклонение",
.DONE = "Удачи!"
}
2024-10-04 21:16:58 +03:00
TutorTextsKeyboard := [TutorSteps]cstring{
.THRUST = "Используйте стрелку \"Вверх\" для ускорения",
.LOOK = "Стрелки \"Влево\"/\"Вправо\" для наведения",
.SHOOT = "Пробел: стрельба",
.DODGE = "Shift: уклонение",
.DONE = "Удачи!"
}
2024-10-04 21:16:58 +03:00
game_init :: proc(prev: ^GameState = nil) -> ^GameState {
state := new(Game)
state.previous = prev
state.variant = state
state.draw = game_draw
state.update = game_update
state.free = game_free
2024-10-12 18:26:14 +03:00
// rlgl.DisableBackfaceCulling()
2024-10-04 21:16:58 +03:00
game_setup(state)
return state
}
game_setup :: proc(game: ^Game) {
clear(&game.bullets)
2024-10-05 20:44:32 +03:00
clear(&Segments)
clear(&trails)
clear(&explosions)
explosions_init()
trail_init()
game.player = player_spawn({GameField.x / 2 + 70, 20, 0})
// rl.StopMusicStream(game.music)
2024-10-04 21:16:58 +03:00
game.health = 100
2024-10-05 20:44:32 +03:00
game.snake_max_health = 0
2024-10-04 21:16:58 +03:00
snake_spawn({0, -10, 0}, math.PI * 3 / 2, 100)
2024-10-04 21:16:58 +03:00
for segment in Segments {
game.snake_max_health += int(segment.health)
}
game.camera = rl.Camera3D{
target = game.player.pos,
position = game.player.pos + vec3backward * 50,
fovy = 60,
//offset = WSize/2,
projection = rl.CameraProjection.PERSPECTIVE,
up = vec3up
}
game.camera.target.x = clamp(game.camera.target.x, -GameField.x/2, GameField.x/2)
game.camera.target.y = clamp(game.camera.target.y, 0, GameField.y)
game.camera.position = game.camera.target + vec3backward * 50
game.tutorial_timer = 3
2024-10-04 21:16:58 +03:00
}
game_gen_level :: proc(game: ^Game) {
}
game_update :: proc(state: ^GameState, delta: f32) {
game := transmute(^Game)state
using game
tutorial_enabled = NeedTutorial && !tutorial_finished && player.intro_timer <= 0
if tutorial_enabled {
tutorial_timer -= delta
if tutorial_timer <= 0 {
tutorial_timer = 3
switch tutorial_step {
case .THRUST: tutorial_step = .LOOK
case .LOOK: tutorial_step = .SHOOT
case .SHOOT: tutorial_step = .DODGE
case .DODGE: tutorial_step = .DONE; tutorial_timer = 2
case .DONE:
tutorial_finished = true
NeedTutorial = false
SnakeActive = true
rl.PlaySound(Res.Sfx.SnakeRoarBlast)
}
}
} else {
if player.intro_timer <= 0 && !SnakeActive {
SnakeActive = true
}
}
2024-10-05 20:44:32 +03:00
// if rl.IsMusicStreamPlaying(game.music) {
rl.UpdateMusicStream(game.music)
// }
explosions_process(delta)
if rl.IsKeyPressed(rl.KeyboardKey.ESCAPE) {
pause := pause_init(game)
stack_push(pause)
}
2024-10-04 21:16:58 +03:00
player_update(&player, game, delta)
#reverse for &bullet, i in bullets {
bullet_process(&bullet, game, delta)
if !bullet.alive {
unordered_remove(&bullets, i)
}
}
snake_process(game, delta)
target_offset := player.vel / 5
camera_offset = rl.Vector3MoveTowards(camera_offset, target_offset, rl.Vector3Length(target_offset - camera_offset) * 10 * delta)
camera.target = player.pos + camera_offset
camera.target.x = clamp(camera.target.x, -GameField.x/2, GameField.x/2)
camera.target.y = clamp(camera.target.y, 0, GameField.y)
camera.position = camera.target + vec3backward * 50
2024-10-05 20:44:32 +03:00
trail_update(delta)
2024-10-04 21:16:58 +03:00
}
game_draw :: proc(state: ^GameState) {
2024-10-05 20:44:32 +03:00
rlgl.SetLineWidth(4)
2024-10-04 21:16:58 +03:00
game := transmute(^Game)state
using game
rl.BeginMode3D(camera)
2024-10-12 18:26:14 +03:00
rl.DrawModel(Res.Models.Background, {0, 0, 500}, 1000, rl.WHITE)
2024-10-04 21:16:58 +03:00
yy : i32 = 0
snake_draw(game)
player_draw(&player)
2024-10-05 20:44:32 +03:00
for &bullet, i in bullets {
bullet_draw(&bullet, game)
if i != 0 {
// rl.DrawLine3D(bullets[i].pos, bullets[i-1].pos, rl.WHITE)
}
2024-10-04 21:16:58 +03:00
}
2024-10-05 20:44:32 +03:00
trail_draw()
explosions_draw()
2024-10-04 21:16:58 +03:00
rl.EndMode3D()
2024-10-04 21:29:39 +03:00
if stack_top() == game {
hb_text : cstring = "Jörmungandr"
height := 30 * (WSize.y / 480)
hb_health : f32 = f32(snake_health) / f32(snake_max_health)
if snake_health == 0 {
hb_health = f32(Head.health) / f32(Head.max_health)
hb_text = "Jörmungandr's head"
}
2024-10-05 20:44:32 +03:00
// rl.DrawRectangleV({0, WSize.y - height - 10}, {WSize.x, height + 10}, rl.WHITE)
// hb_width := hb_health * WSize.x
// rl.DrawRectangleV({WSize.x / 2 - hb_width / 2, WSize.y - height - 7}, {hb_width, height + 4}, rl.RED)
// draw_text_centered(Res.Fonts.Title, hb_text, {WSize.x / 2, WSize.y - height / 2}, height)
if SnakeActive {
draw_hbar(hb_text, hb_health, {0, WSize.y - height}, {WSize.x, height}, rl.RED)
}
2024-10-05 20:44:32 +03:00
draw_hbar("Þórr", f32(health) / 100.0, {0, 0}, {WSize.x, height}, rl.GREEN)
reload_color := rl.SKYBLUE
if player.reloading {
reload_color = rl.RED
}
draw_hbar("", f32(player.power) / 100.0, {0, height}, {WSize.x, 10}, reload_color)
if tutorial_enabled {
texts := TutorTextsMouse
if KeyboardOnly {
texts = TutorTextsKeyboard
}
draw_text_centered(Res.Fonts.UI, texts[tutorial_step], WSize / 2 + vec2{0, 100}, 48, 1, rl.WHITE)
}
2024-10-04 21:16:58 +03:00
}
}
2024-10-05 20:44:32 +03:00
draw_hbar :: proc(text: cstring, value: f32, pos, size: vec2, color: rl.Color) {
rl.DrawRectangleV(pos, size, rl.WHITE)
hb_width := value * size.x
rl.DrawRectangleV(pos + {size.x / 2 - hb_width / 2, 2}, {hb_width, size.y - 4}, color)
draw_text_centered(Res.Fonts.Title, text, pos + size / 2, size.y / 1.3)
}
2024-10-04 21:16:58 +03:00
game_free :: proc(state: ^GameState) {
game := transmute(^Game)state
free(state)
}