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 !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) }