Trying to compile a vibe coded odin project I get this output:
/a/proj/zarch ❯ odin run zarch
/usr/lib/odin/core/math/math.odin(391:85) Error: Mismatched types in binary expression 'a * (1 - t)' : 'f32' vs 'f64'
... ontextless" (a, b: $T, t: $E) -> (x: T) { return a*(1-t) + b*t }
^
/usr/lib/odin/core/math/math.odin(391:95) Error: Mismatched types in binary expression 'b * t' : 'f32' vs 'f64'
... ontextless" (a, b: $T, t: $E) -> (x: T) { return a*(1-t) + b*t }
^
How am I supposed to know where I (well, eh gemini) did wrong? It points to somewhere in the stdlib but no indication where in my code there is a problem?
Sorry for being a noob. ![]()
– Henrik
zarch.odin in zarch folder (warning gemini 3 pro output):
package zarch
import "core:fmt"
import "core:math"
import "core:math/linalg"
import "core:math/rand"
import rl "vendor:raylib"
// --- Constants ---
SCREEN_WIDTH :: 800
SCREEN_HEIGHT :: 600
MAP_SIZE :: 64
TILE_SIZE :: 2.0
GRAVITY :: 9.8
THRUST_POWER :: 25.0
DRAG_FACTOR :: 0.5
MAX_PARTICLES :: 2000
MAX_ENEMIES :: 10
// --- Structs ---
Particle :: struct {
active: bool,
position: rl.Vector3,
velocity: rl.Vector3,
color: rl.Color,
life: f32, // Remaining time
size: f32,
}
Enemy :: struct {
active: bool,
position: rl.Vector3,
velocity: rl.Vector3,
offset_t: f32, // Time offset for hovering animation
}
Player :: struct {
position: rl.Vector3,
velocity: rl.Vector3,
yaw: f32,
pitch: f32,
is_thrusting: bool,
}
Game_State :: struct {
player: Player,
camera: rl.Camera3D,
height_map: [MAP_SIZE][MAP_SIZE]f32,
// Systems
particles: [MAX_PARTICLES]Particle,
p_index: int, // Ring buffer index
enemies: [MAX_ENEMIES]Enemy,
}
// --- Math Helpers ---
get_forward_vector :: proc(yaw, pitch: f32) -> rl.Vector3 {
cx := math.cos(pitch)
sx := math.sin(pitch)
cy := math.cos(yaw)
sy := math.sin(yaw)
return rl.Vector3{sx * sy, cx, sx * cy}
}
get_terrain_height :: proc(state: ^Game_State, x, z: f32) -> f32 {
map_offset := f32(MAP_SIZE * TILE_SIZE) / 2.0
gx := int((x + map_offset) / TILE_SIZE)
gz := int((z + map_offset) / TILE_SIZE)
if gx >= 0 && gx < MAP_SIZE && gz >= 0 && gz < MAP_SIZE {
return state.height_map[gx][gz]
}
return -50.0 // Abyss
}
// --- Particle System ---
spawn_particle :: proc(state: ^Game_State, pos, vel: rl.Vector3, col: rl.Color, size, life: f32) {
idx := state.p_index
state.particles[idx] = Particle{
active = true,
position = pos,
velocity = vel,
color = col,
life = life,
size = size,
}
state.p_index = (state.p_index + 1) % MAX_PARTICLES
}
update_particles :: proc(state: ^Game_State, dt: f32) {
for i in 0..<MAX_PARTICLES {
p := &state.particles[i]
if !p.active do continue
p.life -= dt
if p.life <= 0 {
p.active = false
continue
}
p.position += p.velocity * dt
// Simple floor collision for particles
h := get_terrain_height(state, p.position.x, p.position.z)
if p.position.y < h {
p.position.y = h
p.velocity.y = 0
p.velocity.x *= 0.5 // Friction
p.velocity.z *= 0.5
}
}
}
// --- Enemy System ---
init_enemies :: proc(state: ^Game_State) {
for i in 0..<MAX_ENEMIES {
// Random spawn
rx := (rand.float32() * 80.0) - 40.0
rz := (rand.float32() * 80.0) - 40.0
state.enemies[i] = Enemy{
active = true,
position = rl.Vector3{rx, 30.0, rz},
offset_t = rand.float32() * 10.0,
}
}
}
update_enemies :: proc(state: ^Game_State, dt: f32) {
for i in 0..<MAX_ENEMIES {
e := &state.enemies[i]
if !e.active do continue
// 1. Behavior: Hover and slowly chase player logic
target := state.player.position
diff := target - e.position
dist := math.sqrt(diff.x*diff.x + diff.z*diff.z)
// Move towards player slowly
dir := rl.Vector3Normalize(diff)
speed : f32 = 4.0
// Add some sine wave bobbing
e.offset_t += dt
bob := math.sin(e.offset_t * 2.0) * 0.1
e.velocity = rl.Vector3{dir.x * speed, bob, dir.z * speed}
e.position += e.velocity * dt
// 2. Terrain Constraint (Keep them above ground)
h := get_terrain_height(state, e.position.x, e.position.z)
desired_alt := h + 10.0 + (math.sin(e.offset_t)*2.0)
// Smoothly adjust altitude
e.position.y = math.lerp(e.position.y, desired_alt, 0.05)
// 3. Emit "Virus" Particles (Green trail)
if rand.float32() > 0.8 {
spawn_particle(state,
e.position,
rl.Vector3{(rand.float32()-0.5), (rand.float32()-0.5), (rand.float32()-0.5)},
rl.LIME, 0.4, 1.0)
}
}
}
// --- Main Logic ---
init_terrain :: proc(state: ^Game_State) {
for x in 0..<MAP_SIZE {
for z in 0..<MAP_SIZE {
fx := f32(x) * 0.15
fz := f32(z) * 0.15
height := math.sin(fx) * 3.0 + math.cos(fz) * 3.0 + math.sin(fx + fz) * 1.5
state.height_map[x][z] = height - 10.0
}
}
}
update_player :: proc(state: ^Game_State, dt: f32) {
p := &state.player
// Input
mouse_delta := rl.GetMouseDelta()
p.yaw -= mouse_delta.x * 0.005
p.pitch -= mouse_delta.y * 0.005
p.pitch = math.clamp(p.pitch, -math.PI + 0.1, -0.1)
p.is_thrusting = rl.IsMouseButtonDown(.LEFT) || rl.IsKeyDown(.W) || rl.IsKeyDown(.SPACE)
forward := get_forward_vector(p.yaw, p.pitch)
// Physics
accel := rl.Vector3{0, -GRAVITY, 0}
if p.is_thrusting {
accel += forward * THRUST_POWER
// PARTICLES: Engine Exhaust
// Spawn particle behind the ship
exhaust_pos := p.position - (forward * 0.5)
// Add some randomness to velocity
rnd_vel := rl.Vector3{
(rand.float32() - 0.5) * 5.0,
(rand.float32() - 0.5) * 5.0,
(rand.float32() - 0.5) * 5.0,
}
spawn_particle(state, exhaust_pos, (-forward * 10.0) + rnd_vel, rl.ORANGE, 0.5, 0.5)
}
accel -= p.velocity * DRAG_FACTOR
p.velocity += accel * dt
p.position += p.velocity * dt
// Terrain Collision & Dust
h := get_terrain_height(state, p.position.x, p.position.z)
// Dust effect when close to ground
dist_to_ground := p.position.y - h
if dist_to_ground < 8.0 && dist_to_ground > 0.0 {
// More dust the closer we are
chance := 1.0 - (dist_to_ground / 8.0)
if rand.float32() < chance {
// Spawn dust at ground level
dust_pos := rl.Vector3{
p.position.x + (rand.float32()*4.0 - 2.0),
h + 0.5,
p.position.z + (rand.float32()*4.0 - 2.0),
}
spawn_particle(state, dust_pos, rl.Vector3{0, 1.0, 0}, rl.BROWN, 0.8, 1.5)
}
}
if p.position.y < h + 0.5 {
p.position.y = h + 0.5
p.velocity.y *= -0.5
p.velocity.x *= 0.8
p.velocity.z *= 0.8
}
}
main :: proc() {
rl.InitWindow(SCREEN_WIDTH, SCREEN_HEIGHT, "Odin Zarch: Particles & Enemies")
defer rl.CloseWindow()
rl.SetTargetFPS(60)
rl.DisableCursor()
state := Game_State{}
state.player.position = rl.Vector3{0, 10, 0}
state.player.pitch = -math.PI / 2.0
state.camera.position = rl.Vector3{0, 20, 20}
state.camera.target = state.player.position
state.camera.up = rl.Vector3{0, 1, 0}
state.camera.fovy = 45.0
state.camera.projection = .PERSPECTIVE
init_terrain(&state)
init_enemies(&state)
for !rl.WindowShouldClose() {
dt := rl.GetFrameTime()
update_player(&state, dt)
update_enemies(&state, dt)
update_particles(&state, dt)
// Camera Follow
CAM_OFFSET_Y :: 30.0
CAM_OFFSET_Z :: 30.0
target_cam_pos := state.player.position + rl.Vector3{0, CAM_OFFSET_Y, CAM_OFFSET_Z}
state.camera.position = math.lerp(state.camera.position, target_cam_pos, 0.1)
state.camera.target = state.player.position
// --- Draw ---
rl.BeginDrawing()
rl.ClearBackground(rl.BLACK)
rl.BeginMode3D(state.camera)
// Draw Terrain
map_offset := f32(MAP_SIZE * TILE_SIZE) / 2.0
for x in 0..<MAP_SIZE {
for z in 0..<MAP_SIZE {
h := state.height_map[x][z]
wx := (f32(x) * TILE_SIZE) - map_offset
wz := (f32(z) * TILE_SIZE) - map_offset
pos := rl.Vector3{wx, h/2.0, wz}
color := rl.DARKGREEN
if h > 2.0 do color = rl.LIME
if h > 5.0 do color = rl.WHITE
if h < -5.0 do color = rl.BLUE
// Simple distance culling (fog-like optimization)
dist := rl.Vector3Distance(state.player.position, pos)
if dist < 60.0 {
rl.DrawCube(pos, TILE_SIZE, h + 20.0, TILE_SIZE, color)
rl.DrawCubeWires(pos, TILE_SIZE, h + 20.0, TILE_SIZE, rl.ColorAlpha(rl.BLACK, 0.3))
}
}
}
// Draw Particles
for p in state.particles {
if p.active {
// Fade out alpha based on life
alpha := p.life
if alpha > 1.0 do alpha = 1.0
col := rl.ColorAlpha(p.color, alpha)
rl.DrawCube(p.position, p.size, p.size, p.size, col)
}
}
// Draw Enemies
for e in state.enemies {
if e.active {
// Draw a pyramid shape for the Virus
rl.DrawCylinder(e.position, 0.0, 1.5, 3.0, 4, rl.GREEN)
// Enemy Shadow
shadow_y := get_terrain_height(&state, e.position.x, e.position.z) + 0.1
rl.DrawCube(rl.Vector3{e.position.x, shadow_y, e.position.z}, 1.0, 0.05, 1.0, rl.ColorAlpha(rl.BLACK, 0.5))
}
}
// Draw Player
rl.DrawSphere(state.player.position, 0.5, rl.RED)
forward := get_forward_vector(state.player.yaw, state.player.pitch)
rl.DrawLine3D(state.player.position, state.player.position + (forward * 2.0), rl.YELLOW)
// Player Shadow
shadow_y := get_terrain_height(&state, state.player.position.x, state.player.position.z) + 0.1
rl.DrawCube(rl.Vector3{state.player.position.x, shadow_y, state.player.position.z}, 0.8, 0.05, 0.8, rl.BLACK)
rl.EndMode3D()
// UI
rl.DrawText(rl.TextFormat("Particles: %d", state.p_index), 10, 10, 20, rl.WHITE)
rl.DrawText(rl.TextFormat("Enemies: %d", MAX_ENEMIES), 10, 30, 20, rl.GREEN)
rl.DrawFPS(SCREEN_WIDTH - 80, 10)
rl.EndDrawing()
}
}