231 lines
6.8 KiB
Odin
231 lines
6.8 KiB
Odin
package main
|
|
|
|
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,
|
|
// 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,
|
|
}
|
|
}
|
|
|
|
player_update :: proc(player: ^Player, game: ^Game, delta: f32) {
|
|
using player
|
|
|
|
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 {
|
|
dir = angle_rotate(dir, mouse_angle, math.PI * 2 * delta)
|
|
dir_vector = get_vec_from_angle(dir)
|
|
|
|
thrust = 0
|
|
|
|
if rl.IsKeyDown(rl.KeyboardKey.W) {
|
|
thrust = 70
|
|
}
|
|
} else {
|
|
thrust = 70
|
|
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)
|
|
}
|
|
}
|
|
|
|
if rl.IsMouseButtonPressed(rl.MouseButton.RIGHT) && 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
|
|
})
|
|
}
|
|
|
|
// player.animation = PlayerAnims[1]
|
|
// player.animTime += delta
|
|
// player.animFrame = i32(player.animTime * 60) % player.animation.frameCount
|
|
// rl.UpdateModelAnimation(PlayerModel, player.animation, player.animFrame)
|
|
shooting := rl.IsMouseButtonDown(rl.MouseButton.LEFT) && !is_dodging && intro_timer <= 0
|
|
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
|
|
})
|
|
}
|
|
} else {
|
|
rl.StopSound(Res.Sfx.Lightning)
|
|
}
|
|
}
|
|
|
|
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)
|
|
})
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
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}
|
|
}
|
|
|
|
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)
|
|
|
|
}
|