243 lines
6.2 KiB
Odin
243 lines
6.2 KiB
Odin
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,
|
|
background: rl.Texture,
|
|
plane: rl.Model,
|
|
bullets: [dynamic]Bullet,
|
|
snake_max_health: int,
|
|
snake_health: int,
|
|
music: rl.Music,
|
|
tutorial_enabled: bool,
|
|
tutorial_finished: bool,
|
|
tutorial_timer: f32,
|
|
tutorial_step: TutorSteps
|
|
}
|
|
|
|
TutorSteps :: enum {
|
|
THRUST,
|
|
LOOK,
|
|
SHOOT,
|
|
DODGE,
|
|
DONE
|
|
}
|
|
|
|
TutorTextsMouse := [TutorSteps]cstring{
|
|
.THRUST = "Используйте W/Ц для ускорения",
|
|
.LOOK = "Двигайте мышью для наведения",
|
|
.SHOOT = "Левая кнопка мыши: стрельба",
|
|
.DODGE = "Правая кнопка мыши: уклонение",
|
|
.DONE = "Удачи!"
|
|
}
|
|
|
|
TutorTextsKeyboard := [TutorSteps]cstring{
|
|
.THRUST = "Используйте стрелку \"Вверх\" для ускорения",
|
|
.LOOK = "Стрелки \"Влево\"/\"Вправо\" для наведения",
|
|
.SHOOT = "Пробел: стрельба",
|
|
.DODGE = "Shift: уклонение",
|
|
.DONE = "Удачи!"
|
|
}
|
|
|
|
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
|
|
rlgl.DisableBackfaceCulling()
|
|
|
|
img := rl.GenImageChecked(1024, 1024, 128, 128, rl.Color{60, 255, 255, 255}, rl.Color{30, 220, 220, 255})
|
|
state.background = rl.LoadTextureFromImage(img)
|
|
|
|
state.plane = rl.LoadModel(".\\assets\\models\\background.obj")
|
|
rl.UnloadImage(img)
|
|
|
|
game_setup(state)
|
|
return state
|
|
}
|
|
|
|
game_setup :: proc(game: ^Game) {
|
|
clear(&game.bullets)
|
|
clear(&Segments)
|
|
clear(&trails)
|
|
clear(&explosions)
|
|
explosions_init()
|
|
trail_init()
|
|
game.player = player_spawn({GameField.x / 2 + 70, 20, 0})
|
|
// rl.StopMusicStream(game.music)
|
|
|
|
game.health = 100
|
|
game.snake_max_health = 0
|
|
|
|
snake_spawn({0, -10, 0}, math.PI * 3 / 2, 100)
|
|
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
|
|
}
|
|
|
|
|
|
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
|
|
}
|
|
}
|
|
|
|
|
|
// 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)
|
|
}
|
|
|
|
|
|
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
|
|
trail_update(delta)
|
|
}
|
|
|
|
game_draw :: proc(state: ^GameState) {
|
|
rlgl.SetLineWidth(4)
|
|
game := transmute(^Game)state
|
|
using game
|
|
|
|
rl.BeginMode3D(camera)
|
|
rl.DrawModel(game.plane, {0, 0, 500}, 1000, rl.WHITE)
|
|
|
|
yy : i32 = 0
|
|
|
|
snake_draw(game)
|
|
player_draw(&player)
|
|
|
|
for &bullet, i in bullets {
|
|
bullet_draw(&bullet, game)
|
|
if i != 0 {
|
|
// rl.DrawLine3D(bullets[i].pos, bullets[i-1].pos, rl.WHITE)
|
|
}
|
|
}
|
|
trail_draw()
|
|
explosions_draw()
|
|
rl.EndMode3D()
|
|
|
|
|
|
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"
|
|
}
|
|
// 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)
|
|
}
|
|
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)
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
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)
|
|
}
|
|
|
|
|
|
|
|
game_free :: proc(state: ^GameState) {
|
|
game := transmute(^Game)state
|
|
rl.UnloadTexture(game.background)
|
|
free(state)
|
|
}
|
|
|
|
|