diff --git a/compile.sh b/compile.sh index ec128e1..6794d74 100755 --- a/compile.sh +++ b/compile.sh @@ -46,7 +46,7 @@ fi # Get the C file name (should be next to the .sui file) C_FILE="${INPUT_SUI%.sui}.c" -OUTPUT_BINARY="${INPUT_SUI%.sui}.o" +OUTPUT_BINARY="${INPUT_SUI%.sui}" if [ -n "$2" ]; then OUTPUT_BINARY="$2" @@ -67,7 +67,7 @@ ODE_LIBS=$(pkg-config --libs ode 2>/dev/null || echo "-lode -lm") # Compile C to executable with libsuicmez, raylib, and ODE support echo "Compiling C code and linking with libsuicmez, raylib, and ODE..." -gcc $OPTIMIZE_FLAG -I. -Ilibfishsoup/include "$C_FILE" libsuicmez/libsuicmez.c libsuicmez/suicmez_gc.c libfishsoup/src/*.c $RAYLIB_CFLAGS $ODE_CFLAGS -o "$OUTPUT_BINARY" $RAYLIB_LIBS $ODE_LIBS -lz -lm || exit 1 +gcc $OPTIMIZE_FLAG -I. -Ilibfishsoup/include "$C_FILE" libsuicmez/libsuicmez.c libsuicmez/suicmez_gc.c libfishsoup/src/*.c game/inc/suic_math.c game/inc/suic_terrain.c game/inc/suic_net.c game/inc/suic_ui.c game/inc/suic_lighting.c $RAYLIB_CFLAGS $ODE_CFLAGS -o "$OUTPUT_BINARY" $RAYLIB_LIBS $ODE_LIBS -lz -lm || exit 1 echo "✓ Successfully created: $OUTPUT_BINARY" echo " Run with: ./$OUTPUT_BINARY" diff --git a/compiler_out.txt b/compiler_out.txt new file mode 100644 index 0000000..3391e11 --- /dev/null +++ b/compiler_out.txt @@ -0,0 +1,222 @@ +warning: unused variable: `size` + --> src/c_ir.rs:54:38 + | +54 | CType::FixedArray(inner, size) => format!("{}*", inner.to_string()), // For now, treat as pointer in type strings + | ^^^^ help: if this is intentional, prefix it with an underscore: `_size` + | + = note: `#[warn(unused_variables)]` (part of `#[warn(unused)]`) on by default + +warning: unused variable: `e` + --> src/codegen/transpiler.rs:284:38 + | +284 | TypedASTNodeKind::Extern(e) => { + | ^ help: if this is intentional, prefix it with an underscore: `_e` + +warning: unused variable: `typed_trait` + --> src/codegen/transpiler.rs:289:37 + | +289 | TypedASTNodeKind::Trait(typed_trait) => {} + | ^^^^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_typed_trait` + +warning: unreachable pattern + --> src/codegen/transpiler.rs:679:22 + | +667 | "enable_msaa_4x" => "suic_enable_msaa_4x", + | ---------------- matches all the relevant values +... +679 | "enable_msaa_4x" => "suic_enable_msaa_4x", + | ^^^^^^^^^^^^^^^^ no value can reach this + | + = note: `#[warn(unreachable_patterns)]` (part of `#[warn(unused)]`) on by default + +warning: unused variable: `c` + --> src/monomorphize.rs:107:41 + | +107 | TypedASTNodeKind::Const(c) => { + | ^ help: if this is intentional, prefix it with an underscore: `_c` + +warning: unused variable: `c` + --> src/monomorphize.rs:994:33 + | +994 | TypedASTNodeKind::Const(c) => { + | ^ help: if this is intentional, prefix it with an underscore: `_c` + +warning: unused variable: `path` + --> src/typechecker.rs:2638:30 + | +2638 | ASTNodeKind::Use(path) => Type::Unit, + | ^^^^ help: if this is intentional, prefix it with an underscore: `_path` + +warning: unused variable: `is_ptr` + --> src/typechecker.rs:3169:28 + | +3169 | let (name, is_ptr) = match &typed_obj.ty { + | ^^^^^^ help: if this is intentional, prefix it with an underscore: `_is_ptr` + +warning: field `type_map` is never read + --> src/c_lowerer/declaration_transpiler.rs:7:5 + | +6 | pub struct DeclarationTranspiler { + | --------------------- field in this struct +7 | type_map: HashMap, + | ^^^^^^^^ + | + = note: `#[warn(dead_code)]` (part of `#[warn(unused)]`) on by default + +warning: associated items `register_array_type`, `get_array_struct_name`, and `generate_heap_alloc` are never used + --> src/codegen/transpiler.rs:63:8 + | + 48 | impl Transpiler { + | --------------- associated items in this implementation +... + 63 | fn register_array_type(&mut self, elem_type: &CType) { + | ^^^^^^^^^^^^^^^^^^^ +... +332 | fn get_array_struct_name(elem_type: &CType) -> String { + | ^^^^^^^^^^^^^^^^^^^^^ +... +482 | fn generate_heap_alloc(&self, ty: &CType) -> String { + | ^^^^^^^^^^^^^^^^^^^ + +warning: field `type_params` is never read + --> src/typechecker.rs:198:5 + | +197 | struct FunctionType { + | ------------ field in this struct +198 | type_params: Vec, + | ^^^^^^^^^^^ + | + = note: `FunctionType` has derived impls for the traits `Debug` and `Clone`, but these are intentionally ignored during dead code analysis + +warning: fields `methods` and `parameters` are never read + --> src/typechecker.rs:205:5 + | +204 | struct TraitInfo { + | --------- fields in this struct +205 | methods: HashMap, + | ^^^^^^^ +206 | parameters: Vec, + | ^^^^^^^^^^ + | + = note: `TraitInfo` has derived impls for the traits `Debug` and `Clone`, but these are intentionally ignored during dead code analysis + +warning: field `trait_name` is never read + --> src/typechecker.rs:212:5 + | +210 | struct ImplInfo { + | -------- field in this struct +211 | target: String, +212 | trait_name: Option, + | ^^^^^^^^^^ + | + = note: `ImplInfo` has derived impls for the traits `Debug` and `Clone`, but these are intentionally ignored during dead code analysis + +warning: method `get_var` is never used + --> src/typechecker.rs:294:8 + | +216 | impl TypeEnv { + | ------------ method in this implementation +... +294 | fn get_var(&self, id: &crate::ast::BindingId) -> Option<&VarInfo> { + | ^^^^^^^ + +warning: `suicmez` (lib) generated 14 warnings + Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.05s + Running `target/debug/suicmez game/client.sui` +Type checking file: game/client.sui + +=== Import Resolution Phase === +Starting import resolution... + [parsing] game/client.sui +Import resolution complete! 41 total nodes loaded +Lambda lowering passed! 41 nodes after lowering. +Type checking passed! 41 nodes typechecked. + +Typed AST nodes before monomorphization: + [0] Struct(SuicVec3) with 0 params + [1] Struct(SuicColor) with 0 params + [2] Struct(SuicDirectionalLight) with 0 params + [3] Const(PISTOL_FIRE_RATE) + [4] Const(RIFLE_FIRE_RATE) + [5] Const(RECOIL_RETURN) + [6] Const(CROSS_RETURN) + [7] Const(PISTOL_KICK_PITCH) + [8] Const(PISTOL_KICK_YAW) + [9] Const(RIFLE_KICK_PITCH) + [10] Const(RIFLE_KICK_YAW) + [11] Const(PISTOL_CROSS_KICK) + [12] Const(RIFLE_CROSS_KICK) + [13] Const(PISTOL_SPRAY_GROW) + [14] Const(RIFLE_SPRAY_GROW) + [15] Const(BURST_RESET_TIME) + [16] Const(SUIC_BTN_RELOAD) + [17] Const(SUIC_BTN_SWITCH_PISTOL) + [18] Const(SUIC_BTN_SWITCH_RIFLE) + [19] Const(SUIC_BTN_PICK) + [20] Const(SUIC_BTN_USE_MEDKIT) + [21] Const(SUIC_BTN_JUMP) + [22] Const(SUIC_WEAPON_PISTOL) + [23] Const(SUIC_WEAPON_RIFLE) + [24] Const(SUIC_ITEM_MEDKIT) + [25] Const(SUIC_ITEM_AMMO_PISTOL) + [26] Const(SUIC_ITEM_AMMO_RIFLE) + [27] Const(SUIC_TERRAIN_SIZE) + [28] Const(SUIC_TERRAIN_SCALE) + [29] Const(SUIC_TERRAIN_MIN) + [30] Const(SUIC_TERRAIN_MAX) + [31] Const(SUIC_NET_MAX_PLAYERS) + [32] Const(SUIC_NET_USERNAME_MAX) + [33] Const(SUIC_NET_MAX_ITEMS) + [34] Struct(GameState) with 0 params + [35] Function(suic_game_init) + [36] Function(suic_game_shutdown) + [37] Function(suic_game_handle_input) + [38] Function(suic_game_update) + [39] Function(suic_game_render) + [40] Function(main) +Monomorphization passed! 41 nodes after specialization. + +Monomorphized AST nodes: + [0] Struct(SuicVec3) with 0 params + [1] Struct(SuicColor) with 0 params + [2] Struct(SuicDirectionalLight) with 0 params + [3] Const(PISTOL_FIRE_RATE) + [4] Const(RIFLE_FIRE_RATE) + [5] Const(RECOIL_RETURN) + [6] Const(CROSS_RETURN) + [7] Const(PISTOL_KICK_PITCH) + [8] Const(PISTOL_KICK_YAW) + [9] Const(RIFLE_KICK_PITCH) + [10] Const(RIFLE_KICK_YAW) + [11] Const(PISTOL_CROSS_KICK) + [12] Const(RIFLE_CROSS_KICK) + [13] Const(PISTOL_SPRAY_GROW) + [14] Const(RIFLE_SPRAY_GROW) + [15] Const(BURST_RESET_TIME) + [16] Const(SUIC_BTN_RELOAD) + [17] Const(SUIC_BTN_SWITCH_PISTOL) + [18] Const(SUIC_BTN_SWITCH_RIFLE) + [19] Const(SUIC_BTN_PICK) + [20] Const(SUIC_BTN_USE_MEDKIT) + [21] Const(SUIC_BTN_JUMP) + [22] Const(SUIC_WEAPON_PISTOL) + [23] Const(SUIC_WEAPON_RIFLE) + [24] Const(SUIC_ITEM_MEDKIT) + [25] Const(SUIC_ITEM_AMMO_PISTOL) + [26] Const(SUIC_ITEM_AMMO_RIFLE) + [27] Const(SUIC_TERRAIN_SIZE) + [28] Const(SUIC_TERRAIN_SCALE) + [29] Const(SUIC_TERRAIN_MIN) + [30] Const(SUIC_TERRAIN_MAX) + [31] Const(SUIC_NET_MAX_PLAYERS) + [32] Const(SUIC_NET_USERNAME_MAX) + [33] Const(SUIC_NET_MAX_ITEMS) + [34] Struct(GameState) with 0 params + [35] Function(suic_game_init) + [36] Function(suic_game_shutdown) + [37] Function(suic_game_handle_input) + [38] Function(suic_game_update) + [39] Function(suic_game_render) + [40] Function(main) +Type variable check passed! No type variables remain in AST. +Error: Code generation error: Unsupported expression: Do([TypedExpr { kind: Let(BindingId(48), "wpn", Default, None, TypedExpr { kind: Call(TypedExpr { kind: Variable("suic_net_player_weapon"), span: Span { start: 5043, end: 5066, file: "game/client.sui" }, attributes: [], ty: Function([Int], Int) }, [TypedExpr { kind: Variable("my_id"), span: Span { start: 5066, end: 5072, file: "game/client.sui" }, attributes: [], ty: Int }]), span: Span { start: 5043, end: 5084, file: "game/client.sui" }, attributes: [], ty: Int }), span: Span { start: 5033, end: 5084, file: "game/client.sui" }, attributes: [], ty: Int }, TypedExpr { kind: Let(BindingId(49), "rate", Mutable, None, TypedExpr { kind: Variable("RIFLE_FIRE_RATE"), span: Span { start: 5096, end: 5122, file: "game/client.sui" }, attributes: [], ty: Float }), span: Span { start: 5081, end: 5122, file: "game/client.sui" }, attributes: [], ty: Float }, TypedExpr { kind: If(TypedExpr { kind: BinOp(TypedExpr { kind: Variable("wpn"), span: Span { start: 5123, end: 5129, file: "game/client.sui" }, attributes: [], ty: Int }, Eq, TypedExpr { kind: Variable("SUIC_WEAPON_PISTOL"), span: Span { start: 5130, end: 5151, file: "game/client.sui" }, attributes: [], ty: Int }), span: Span { start: 5123, end: 5151, file: "game/client.sui" }, attributes: [], ty: Bool }, TypedExpr { kind: Do([TypedExpr { kind: Assign(TypedExpr { kind: Variable("rate"), span: Span { start: 5164, end: 5170, file: "game/client.sui" }, attributes: [], ty: Float }, TypedExpr { kind: Variable("PISTOL_FIRE_RATE"), span: Span { start: 5171, end: 5199, file: "game/client.sui" }, attributes: [], ty: Float }), span: Span { start: 5164, end: 5199, file: "game/client.sui" }, attributes: [], ty: Unit }]), span: Span { start: 5149, end: 5218, file: "game/client.sui" }, attributes: [], ty: Unit }, None), span: Span { start: 5120, end: 5218, file: "game/client.sui" }, attributes: [], ty: Unit }, TypedExpr { kind: Assign(TypedExpr { kind: Dot(TypedExpr { kind: Variable("game_state"), span: Span { start: 5208, end: 5219, file: "game/client.sui" }, attributes: [], ty: Ptr(Struct("GameState", [])) }, "fire_cooldown"), span: Span { start: 5208, end: 5234, file: "game/client.sui" }, attributes: [], ty: Float }, TypedExpr { kind: BinOp(TypedExpr { kind: Float(1.0), span: Span { start: 5235, end: 5240, file: "game/client.sui" }, attributes: [], ty: Float }, Div, TypedExpr { kind: Variable("rate"), span: Span { start: 5241, end: 5265, file: "game/client.sui" }, attributes: [], ty: Float }), span: Span { start: 5235, end: 5265, file: "game/client.sui" }, attributes: [], ty: Float }), span: Span { start: 5208, end: 5265, file: "game/client.sui" }, attributes: [], ty: Unit }, TypedExpr { kind: Assign(TypedExpr { kind: Dot(TypedExpr { kind: Variable("game_state"), span: Span { start: 5255, end: 5266, file: "game/client.sui" }, attributes: [], ty: Ptr(Struct("GameState", [])) }, "shots_in_burst"), span: Span { start: 5255, end: 5282, file: "game/client.sui" }, attributes: [], ty: Int }, TypedExpr { kind: BinOp(TypedExpr { kind: Dot(TypedExpr { kind: Variable("game_state"), span: Span { start: 5283, end: 5294, file: "game/client.sui" }, attributes: [], ty: Ptr(Struct("GameState", [])) }, "shots_in_burst"), span: Span { start: 5283, end: 5310, file: "game/client.sui" }, attributes: [], ty: Int }, Add, TypedExpr { kind: Int(1), span: Span { start: 5311, end: 5331, file: "game/client.sui" }, attributes: [], ty: Int }), span: Span { start: 5283, end: 5331, file: "game/client.sui" }, attributes: [], ty: Int }), span: Span { start: 5255, end: 5331, file: "game/client.sui" }, attributes: [], ty: Unit }, TypedExpr { kind: Assign(TypedExpr { kind: Dot(TypedExpr { kind: Variable("game_state"), span: Span { start: 5321, end: 5332, file: "game/client.sui" }, attributes: [], ty: Ptr(Struct("GameState", [])) }, "burst_reset_timer"), span: Span { start: 5321, end: 5351, file: "game/client.sui" }, attributes: [], ty: Float }, TypedExpr { kind: Variable("BURST_RESET_TIME"), span: Span { start: 5352, end: 5380, file: "game/client.sui" }, attributes: [], ty: Float }), span: Span { start: 5321, end: 5380, file: "game/client.sui" }, attributes: [], ty: Unit }, TypedExpr { kind: If(TypedExpr { kind: BinOp(TypedExpr { kind: Variable("wpn"), span: Span { start: 5381, end: 5387, file: "game/client.sui" }, attributes: [], ty: Int }, Eq, TypedExpr { kind: Variable("SUIC_WEAPON_PISTOL"), span: Span { start: 5388, end: 5409, file: "game/client.sui" }, attributes: [], ty: Int }), span: Span { start: 5381, end: 5409, file: "game/client.sui" }, attributes: [], ty: Bool }, TypedExpr { kind: Do([TypedExpr { kind: Assign(TypedExpr { kind: Dot(TypedExpr { kind: Variable("game_state"), span: Span { start: 5422, end: 5433, file: "game/client.sui" }, attributes: [], ty: Ptr(Struct("GameState", [])) }, "recoil_pitch"), span: Span { start: 5422, end: 5447, file: "game/client.sui" }, attributes: [], ty: Float }, TypedExpr { kind: BinOp(TypedExpr { kind: Dot(TypedExpr { kind: Variable("game_state"), span: Span { start: 5448, end: 5459, file: "game/client.sui" }, attributes: [], ty: Ptr(Struct("GameState", [])) }, "recoil_pitch"), span: Span { start: 5448, end: 5473, file: "game/client.sui" }, attributes: [], ty: Float }, Add, TypedExpr { kind: Variable("PISTOL_KICK_PITCH"), span: Span { start: 5474, end: 5514, file: "game/client.sui" }, attributes: [], ty: Float }), span: Span { start: 5448, end: 5514, file: "game/client.sui" }, attributes: [], ty: Float }), span: Span { start: 5422, end: 5514, file: "game/client.sui" }, attributes: [], ty: Unit }, TypedExpr { kind: Assign(TypedExpr { kind: Dot(TypedExpr { kind: Variable("game_state"), span: Span { start: 5504, end: 5515, file: "game/client.sui" }, attributes: [], ty: Ptr(Struct("GameState", [])) }, "recoil_yaw"), span: Span { start: 5504, end: 5527, file: "game/client.sui" }, attributes: [], ty: Float }, TypedExpr { kind: BinOp(TypedExpr { kind: Dot(TypedExpr { kind: Variable("game_state"), span: Span { start: 5528, end: 5539, file: "game/client.sui" }, attributes: [], ty: Ptr(Struct("GameState", [])) }, "recoil_yaw"), span: Span { start: 5528, end: 5551, file: "game/client.sui" }, attributes: [], ty: Float }, Add, TypedExpr { kind: BinOp(TypedExpr { kind: BinOp(TypedExpr { kind: Cast(TypedExpr { kind: Call(TypedExpr { kind: Variable("get_random_value"), span: Span { start: 5553, end: 5570, file: "game/client.sui" }, attributes: [], ty: Function([Int, Int], Int) }, [TypedExpr { kind: UnOp(Neg, TypedExpr { kind: Int(1000), span: Span { start: 5571, end: 5576, file: "game/client.sui" }, attributes: [], ty: Int }), span: Span { start: 5570, end: 5576, file: "game/client.sui" }, attributes: [], ty: Int }, TypedExpr { kind: Int(1000), span: Span { start: 5577, end: 5582, file: "game/client.sui" }, attributes: [], ty: Int }]), span: Span { start: 5553, end: 5585, file: "game/client.sui" }, attributes: [], ty: Int }, Cons("float", [])), span: Span { start: 5553, end: 5593, file: "game/client.sui" }, attributes: [], ty: Float }, Div, TypedExpr { kind: Float(1000.0), span: Span { start: 5594, end: 5601, file: "game/client.sui" }, attributes: [], ty: Float }), span: Span { start: 5552, end: 5603, file: "game/client.sui" }, attributes: [], ty: Float }, Mul, TypedExpr { kind: Variable("PISTOL_KICK_YAW"), span: Span { start: 5604, end: 5642, file: "game/client.sui" }, attributes: [], ty: Float }), span: Span { start: 5552, end: 5642, file: "game/client.sui" }, attributes: [], ty: Float }), span: Span { start: 5528, end: 5642, file: "game/client.sui" }, attributes: [], ty: Float }), span: Span { start: 5504, end: 5642, file: "game/client.sui" }, attributes: [], ty: Unit }, TypedExpr { kind: Assign(TypedExpr { kind: Dot(TypedExpr { kind: Variable("game_state"), span: Span { start: 5632, end: 5643, file: "game/client.sui" }, attributes: [], ty: Ptr(Struct("GameState", [])) }, "cross_spread"), span: Span { start: 5632, end: 5657, file: "game/client.sui" }, attributes: [], ty: Float }, TypedExpr { kind: BinOp(TypedExpr { kind: BinOp(TypedExpr { kind: Dot(TypedExpr { kind: Variable("game_state"), span: Span { start: 5658, end: 5669, file: "game/client.sui" }, attributes: [], ty: Ptr(Struct("GameState", [])) }, "cross_spread"), span: Span { start: 5658, end: 5683, file: "game/client.sui" }, attributes: [], ty: Float }, Add, TypedExpr { kind: Variable("PISTOL_CROSS_KICK"), span: Span { start: 5684, end: 5703, file: "game/client.sui" }, attributes: [], ty: Float }), span: Span { start: 5658, end: 5703, file: "game/client.sui" }, attributes: [], ty: Float }, Add, TypedExpr { kind: BinOp(TypedExpr { kind: Cast(TypedExpr { kind: Dot(TypedExpr { kind: Variable("game_state"), span: Span { start: 5704, end: 5715, file: "game/client.sui" }, attributes: [], ty: Ptr(Struct("GameState", [])) }, "shots_in_burst"), span: Span { start: 5704, end: 5732, file: "game/client.sui" }, attributes: [], ty: Int }, Cons("float", [])), span: Span { start: 5704, end: 5740, file: "game/client.sui" }, attributes: [], ty: Float }, Mul, TypedExpr { kind: Variable("PISTOL_SPRAY_GROW"), span: Span { start: 5741, end: 5770, file: "game/client.sui" }, attributes: [], ty: Float }), span: Span { start: 5704, end: 5770, file: "game/client.sui" }, attributes: [], ty: Float }), span: Span { start: 5658, end: 5770, file: "game/client.sui" }, attributes: [], ty: Float }), span: Span { start: 5632, end: 5770, file: "game/client.sui" }, attributes: [], ty: Unit }]), span: Span { start: 5407, end: 5781, file: "game/client.sui" }, attributes: [], ty: Unit }, None), span: Span { start: 5378, end: 5781, file: "game/client.sui" }, attributes: [], ty: Unit }, TypedExpr { kind: If(TypedExpr { kind: BinOp(TypedExpr { kind: Variable("wpn"), span: Span { start: 5782, end: 5788, file: "game/client.sui" }, attributes: [], ty: Int }, Eq, TypedExpr { kind: Variable("SUIC_WEAPON_RIFLE"), span: Span { start: 5789, end: 5809, file: "game/client.sui" }, attributes: [], ty: Int }), span: Span { start: 5782, end: 5809, file: "game/client.sui" }, attributes: [], ty: Bool }, TypedExpr { kind: Do([TypedExpr { kind: Assign(TypedExpr { kind: Dot(TypedExpr { kind: Variable("game_state"), span: Span { start: 5822, end: 5833, file: "game/client.sui" }, attributes: [], ty: Ptr(Struct("GameState", [])) }, "recoil_pitch"), span: Span { start: 5822, end: 5847, file: "game/client.sui" }, attributes: [], ty: Float }, TypedExpr { kind: BinOp(TypedExpr { kind: Dot(TypedExpr { kind: Variable("game_state"), span: Span { start: 5848, end: 5859, file: "game/client.sui" }, attributes: [], ty: Ptr(Struct("GameState", [])) }, "recoil_pitch"), span: Span { start: 5848, end: 5873, file: "game/client.sui" }, attributes: [], ty: Float }, Add, TypedExpr { kind: Variable("RIFLE_KICK_PITCH"), span: Span { start: 5874, end: 5913, file: "game/client.sui" }, attributes: [], ty: Float }), span: Span { start: 5848, end: 5913, file: "game/client.sui" }, attributes: [], ty: Float }), span: Span { start: 5822, end: 5913, file: "game/client.sui" }, attributes: [], ty: Unit }, TypedExpr { kind: Assign(TypedExpr { kind: Dot(TypedExpr { kind: Variable("game_state"), span: Span { start: 5903, end: 5914, file: "game/client.sui" }, attributes: [], ty: Ptr(Struct("GameState", [])) }, "recoil_yaw"), span: Span { start: 5903, end: 5926, file: "game/client.sui" }, attributes: [], ty: Float }, TypedExpr { kind: BinOp(TypedExpr { kind: Dot(TypedExpr { kind: Variable("game_state"), span: Span { start: 5927, end: 5938, file: "game/client.sui" }, attributes: [], ty: Ptr(Struct("GameState", [])) }, "recoil_yaw"), span: Span { start: 5927, end: 5950, file: "game/client.sui" }, attributes: [], ty: Float }, Add, TypedExpr { kind: BinOp(TypedExpr { kind: BinOp(TypedExpr { kind: Cast(TypedExpr { kind: Call(TypedExpr { kind: Variable("get_random_value"), span: Span { start: 5952, end: 5969, file: "game/client.sui" }, attributes: [], ty: Function([Int, Int], Int) }, [TypedExpr { kind: UnOp(Neg, TypedExpr { kind: Int(1000), span: Span { start: 5970, end: 5975, file: "game/client.sui" }, attributes: [], ty: Int }), span: Span { start: 5969, end: 5975, file: "game/client.sui" }, attributes: [], ty: Int }, TypedExpr { kind: Int(1000), span: Span { start: 5976, end: 5981, file: "game/client.sui" }, attributes: [], ty: Int }]), span: Span { start: 5952, end: 5984, file: "game/client.sui" }, attributes: [], ty: Int }, Cons("float", [])), span: Span { start: 5952, end: 5992, file: "game/client.sui" }, attributes: [], ty: Float }, Div, TypedExpr { kind: Float(1000.0), span: Span { start: 5993, end: 6000, file: "game/client.sui" }, attributes: [], ty: Float }), span: Span { start: 5951, end: 6002, file: "game/client.sui" }, attributes: [], ty: Float }, Mul, TypedExpr { kind: Variable("RIFLE_KICK_YAW"), span: Span { start: 6003, end: 6040, file: "game/client.sui" }, attributes: [], ty: Float }), span: Span { start: 5951, end: 6040, file: "game/client.sui" }, attributes: [], ty: Float }), span: Span { start: 5927, end: 6040, file: "game/client.sui" }, attributes: [], ty: Float }), span: Span { start: 5903, end: 6040, file: "game/client.sui" }, attributes: [], ty: Unit }, TypedExpr { kind: Assign(TypedExpr { kind: Dot(TypedExpr { kind: Variable("game_state"), span: Span { start: 6030, end: 6041, file: "game/client.sui" }, attributes: [], ty: Ptr(Struct("GameState", [])) }, "cross_spread"), span: Span { start: 6030, end: 6055, file: "game/client.sui" }, attributes: [], ty: Float }, TypedExpr { kind: BinOp(TypedExpr { kind: BinOp(TypedExpr { kind: Dot(TypedExpr { kind: Variable("game_state"), span: Span { start: 6056, end: 6067, file: "game/client.sui" }, attributes: [], ty: Ptr(Struct("GameState", [])) }, "cross_spread"), span: Span { start: 6056, end: 6081, file: "game/client.sui" }, attributes: [], ty: Float }, Add, TypedExpr { kind: Variable("RIFLE_CROSS_KICK"), span: Span { start: 6082, end: 6100, file: "game/client.sui" }, attributes: [], ty: Float }), span: Span { start: 6056, end: 6100, file: "game/client.sui" }, attributes: [], ty: Float }, Add, TypedExpr { kind: BinOp(TypedExpr { kind: Cast(TypedExpr { kind: Dot(TypedExpr { kind: Variable("game_state"), span: Span { start: 6101, end: 6112, file: "game/client.sui" }, attributes: [], ty: Ptr(Struct("GameState", [])) }, "shots_in_burst"), span: Span { start: 6101, end: 6129, file: "game/client.sui" }, attributes: [], ty: Int }, Cons("float", [])), span: Span { start: 6101, end: 6137, file: "game/client.sui" }, attributes: [], ty: Float }, Mul, TypedExpr { kind: Variable("RIFLE_SPRAY_GROW"), span: Span { start: 6138, end: 6166, file: "game/client.sui" }, attributes: [], ty: Float }), span: Span { start: 6101, end: 6166, file: "game/client.sui" }, attributes: [], ty: Float }), span: Span { start: 6056, end: 6166, file: "game/client.sui" }, attributes: [], ty: Float }), span: Span { start: 6030, end: 6166, file: "game/client.sui" }, attributes: [], ty: Unit }]), span: Span { start: 5807, end: 6195, file: "game/client.sui" }, attributes: [], ty: Unit }, None), span: Span { start: 5779, end: 6195, file: "game/client.sui" }, attributes: [], ty: Unit }, TypedExpr { kind: Call(TypedExpr { kind: Variable("suic_net_send_shoot"), span: Span { start: 6176, end: 6196, file: "game/client.sui" }, attributes: [], ty: Function([Int, Int], Unit) }, [TypedExpr { kind: Variable("my_id"), span: Span { start: 6196, end: 6202, file: "game/client.sui" }, attributes: [], ty: Int }, TypedExpr { kind: Dot(TypedExpr { kind: Variable("game_state"), span: Span { start: 6203, end: 6214, file: "game/client.sui" }, attributes: [], ty: Ptr(Struct("GameState", [])) }, "client_tick"), span: Span { start: 6203, end: 6226, file: "game/client.sui" }, attributes: [], ty: Int }]), span: Span { start: 6176, end: 6234, file: "game/client.sui" }, attributes: [], ty: Unit }]) diff --git a/game/client b/game/client new file mode 100755 index 0000000..47eb6d0 Binary files /dev/null and b/game/client differ diff --git a/game/client.c b/game/client.c new file mode 100644 index 0000000..909617d --- /dev/null +++ b/game/client.c @@ -0,0 +1,393 @@ +#include "libsuicmez/libsuicmez.h" +#include +#include +#include +#include + +void* gc_alloc(const TypeInfo* type, size_t size); +void gc_init(void); +void gc_shutdown(void); + +// Helper for allocating arrays +static void* suic_alloc_array(const TypeInfo* type, size_t elem_size, size_t len, void* init_data) { + void* ptr = gc_alloc(type, elem_size * len); + if (init_data) memcpy(ptr, init_data, elem_size * len); + return ptr; +} + +// Helper for allocating structs +static void* suic_alloc_struct(const TypeInfo* type, size_t size, void* init_data) { + void* ptr = gc_alloc(type, size); + if (init_data) memcpy(ptr, init_data, size); + return ptr; +} + + +; +; +; +struct GameState { + struct SuicVec3 cam_pos; + float yaw; + float pitch; + struct SuicVec3 prev_body_pos; + struct SuicVec3 current_target_body; + float interp_timer; + float recoil_yaw; + float recoil_pitch; + float cross_spread; + bool scoped; + float fire_cooldown; + int shots_in_burst; + float burst_reset_timer; + int client_tick; + struct SuicDirectionalLight light; +}; +; +; + +static const uint8_t sui_bitmap_SuicVec3[] = { 0, 0, 0 }; +static const TypeInfo sui_typeinfo_SuicVec3 = { + .field_count = 3, + .pointer_count = 0, + .pointer_bitmap = sui_bitmap_SuicVec3 +}; +static const uint8_t sui_bitmap_SuicColor[] = { 0, 0, 0, 0 }; +static const TypeInfo sui_typeinfo_SuicColor = { + .field_count = 4, + .pointer_count = 0, + .pointer_bitmap = sui_bitmap_SuicColor +}; +static const uint8_t sui_bitmap_SuicDirectionalLight[] = { 0, 0, 0, 0, 0, 0 }; +static const TypeInfo sui_typeinfo_SuicDirectionalLight = { + .field_count = 6, + .pointer_count = 0, + .pointer_bitmap = sui_bitmap_SuicDirectionalLight +}; +static const uint8_t sui_bitmap_GameState[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +static const TypeInfo sui_typeinfo_GameState = { + .field_count = 15, + .pointer_count = 0, + .pointer_bitmap = sui_bitmap_GameState +}; + + + +void suic_game_init(struct GameState* game_state, char* username, char* server_ip, int port); +void suic_game_shutdown(void); +void suic_game_handle_input(struct GameState* game_state, float dt); +void suic_game_update(struct GameState* game_state, float dt); +void suic_game_render(struct GameState* game_state); +int suic_main(void); + +const float PISTOL_FIRE_RATE = 4.000000; +const float RIFLE_FIRE_RATE = 12.000000; +const float RECOIL_RETURN = 18.000000; +const float CROSS_RETURN = 14.000000; +const float PISTOL_KICK_PITCH = 0.010000; +const float PISTOL_KICK_YAW = 0.004000; +const float RIFLE_KICK_PITCH = 0.018000; +const float RIFLE_KICK_YAW = 0.010000; +const float PISTOL_CROSS_KICK = 2.000000; +const float RIFLE_CROSS_KICK = 4.000000; +const float PISTOL_SPRAY_GROW = 0.400000; +const float RIFLE_SPRAY_GROW = 1.200000; +const float BURST_RESET_TIME = 0.180000; + +void suic_game_init(struct GameState* game_state, char* username, char* server_ip, int port) { + suic_terrain_init(); + suic_net_init(server_ip, port); + suic_net_send_hello(username); + suic_disable_cursor(); +} + +void suic_game_shutdown(void) { + suic_terrain_cleanup(); + suic_net_shutdown(); +} + +void suic_game_handle_input(struct GameState* game_state, float dt) { + (((*game_state))).client_tick = ((((((*game_state))).client_tick)) + ((1))); + (((*game_state))).fire_cooldown = ((((((*game_state))).fire_cooldown)) - ((dt))); + if (((((((*game_state))).fire_cooldown)) < ((0.000000)))) { + (((*game_state))).fire_cooldown = 0.000000; + } + (((*game_state))).burst_reset_timer = ((((((*game_state))).burst_reset_timer)) - ((dt))); + if (((((((*game_state))).burst_reset_timer)) <= ((0.000000)))) { + (((*game_state))).shots_in_burst = 0; + } + float k = (((1.000000)) - ((expf((((((-RECOIL_RETURN)))) * ((dt))))))); + (((*game_state))).recoil_yaw = ((((((*game_state))).recoil_yaw)) + ((((((((0.000000)) - (((((*game_state))).recoil_yaw))))) * ((k)))))); + (((*game_state))).recoil_pitch = ((((((*game_state))).recoil_pitch)) + ((((((((0.000000)) - (((((*game_state))).recoil_pitch))))) * ((k)))))); + float k2 = (((1.000000)) - ((expf((((((-CROSS_RETURN)))) * ((dt))))))); + (((*game_state))).cross_spread = ((((((*game_state))).cross_spread)) + ((((((((0.000000)) - (((((*game_state))).cross_spread))))) * ((k2)))))); + if (((((((*game_state))).cross_spread)) < ((0.010000)))) { + (((*game_state))).cross_spread = 0.000000; + } + struct suic_vec2 md = suic_get_mouse_delta(); + float sens = 0.002500; + (((*game_state))).yaw = ((((((*game_state))).yaw)) - ((((((md).x)) * ((sens)))))); + (((*game_state))).pitch = ((((((*game_state))).pitch)) - ((((((md).y)) * ((sens)))))); + if (((((((*game_state))).pitch)) < ((((-1.500000)))))) { + (((*game_state))).pitch = ((-1.500000)); + } + if (((((((*game_state))).pitch)) > ((1.500000)))) { + (((*game_state))).pitch = 1.500000; + } + float move_x = 0.000000; + float move_z = 0.000000; + if (suic_is_key_down(65)) { + move_x = (((move_x)) + ((1.000000))); + } + if (suic_is_key_down(68)) { + move_x = (((move_x)) - ((1.000000))); + } + if (suic_is_key_down(87)) { + move_z = (((move_z)) + ((1.000000))); + } + if (suic_is_key_down(83)) { + move_z = (((move_z)) - ((1.000000))); + } + int buttons = 0; + if (suic_is_key_pressed(82)) { + buttons = (((buttons)) | ((SUIC_BTN_RELOAD))); + } + if (suic_is_key_pressed(49)) { + buttons = (((buttons)) | ((SUIC_BTN_SWITCH_PISTOL))); + } + if (suic_is_key_pressed(50)) { + buttons = (((buttons)) | ((SUIC_BTN_SWITCH_RIFLE))); + } + if (suic_is_key_pressed(70)) { + buttons = (((buttons)) | ((SUIC_BTN_PICK))); + } + if (suic_is_key_pressed(72)) { + buttons = (((buttons)) | ((SUIC_BTN_USE_MEDKIT))); + } + if (suic_is_key_pressed(32)) { + buttons = (((buttons)) | ((SUIC_BTN_JUMP))); + } + if (suic_is_key_pressed(90)) { + (((*game_state))).scoped = ((!(((*game_state))).scoped)); + } + int my_id = suic_net_get_my_id(); + float view_yaw = ((((((*game_state))).yaw)) + (((((*game_state))).recoil_yaw))); + float view_pitch = ((((((*game_state))).pitch)) + (((((*game_state))).recoil_pitch))); + float clamped_view_pitch = view_pitch; + if ((((clamped_view_pitch)) < ((((-1.500000)))))) { + clamped_view_pitch = ((-1.500000)); + } + if ((((clamped_view_pitch)) > ((1.500000)))) { + clamped_view_pitch = 1.500000; + } + if ((((my_id)) != ((255)))) { + suic_net_send_input(my_id, (((*game_state))).client_tick, move_x, move_z, view_yaw, clamped_view_pitch, buttons); + } + if (((((((((((((my_id)) != ((255))))) && ((suic_is_mouse_button_down(0)))))) && ((((((((*game_state))).fire_cooldown)) <= ((0.000000)))))))) && ((suic_net_player_present(my_id))))) { + int wpn = suic_net_player_weapon(my_id); + float rate = RIFLE_FIRE_RATE; + if ((((wpn)) == ((SUIC_WEAPON_PISTOL)))) { + rate = PISTOL_FIRE_RATE; + } + (((*game_state))).fire_cooldown = (((1.000000)) / ((rate))); + (((*game_state))).shots_in_burst = ((((((*game_state))).shots_in_burst)) + ((1))); + (((*game_state))).burst_reset_timer = BURST_RESET_TIME; + if ((((wpn)) == ((SUIC_WEAPON_PISTOL)))) { + (((*game_state))).recoil_pitch = ((((((*game_state))).recoil_pitch)) + ((PISTOL_KICK_PITCH))); + (((*game_state))).recoil_yaw = ((((((*game_state))).recoil_yaw)) + (((((((((float) GetRandomValue(((-1000)), 1000))) / ((1000.000000))))) * ((PISTOL_KICK_YAW)))))); + (((*game_state))).cross_spread = (((((((((*game_state))).cross_spread)) + ((PISTOL_CROSS_KICK))))) + ((((((float) (((*game_state))).shots_in_burst)) * ((PISTOL_SPRAY_GROW)))))); + } + if ((((wpn)) == ((SUIC_WEAPON_RIFLE)))) { + (((*game_state))).recoil_pitch = ((((((*game_state))).recoil_pitch)) + ((RIFLE_KICK_PITCH))); + (((*game_state))).recoil_yaw = ((((((*game_state))).recoil_yaw)) + (((((((((float) GetRandomValue(((-1000)), 1000))) / ((1000.000000))))) * ((RIFLE_KICK_YAW)))))); + (((*game_state))).cross_spread = (((((((((*game_state))).cross_spread)) + ((RIFLE_CROSS_KICK))))) + ((((((float) (((*game_state))).shots_in_burst)) * ((RIFLE_SPRAY_GROW)))))); + } + suic_net_send_shoot(my_id, (((*game_state))).client_tick); + } +} + +void suic_game_update(struct GameState* game_state, float dt) { + suic_net_poll(); + int my_id = suic_net_get_my_id(); + if (((((((my_id)) != ((255))))) && ((suic_net_player_present(my_id))))) { + (((*game_state))).prev_body_pos = (((*game_state))).current_target_body; + (((*game_state))).current_target_body = suic_v3(suic_net_player_x(my_id), suic_net_player_y(my_id), suic_net_player_z(my_id)); + (((*game_state))).interp_timer = 0.000000; + } + if (((((((my_id)) != ((255))))) && ((suic_net_player_present(my_id))))) { + float interp_factor = ((((((*game_state))).interp_timer)) / (((((1.000000)) / ((20.000000)))))); + if ((((interp_factor)) > ((1.000000)))) { + interp_factor = 1.000000; + } + struct SuicVec3 interp_body = suic_v3_add(suic_v3_mul((((*game_state))).prev_body_pos, (((1.000000)) - ((interp_factor)))), suic_v3_mul((((*game_state))).current_target_body, interp_factor)); + ((((*game_state))).cam_pos).x = (interp_body).x; + ((((*game_state))).cam_pos).y = ((((interp_body).y)) + ((1.000000))); + ((((*game_state))).cam_pos).z = ((((interp_body).z)) + ((0.000100))); + float terrain_h = suic_terrain_height(((((*game_state))).cam_pos).x, ((((*game_state))).cam_pos).z); + ((((*game_state))).cam_pos).y = fmaxf(((((*game_state))).cam_pos).y, (((terrain_h)) + ((1.500000)))); + } + (((*game_state))).interp_timer = ((((((*game_state))).interp_timer)) + ((dt))); + if ((((my_id)) != ((255)))) { + float view_yaw = ((((((*game_state))).yaw)) + (((((*game_state))).recoil_yaw))); + float view_pitch = ((((((*game_state))).pitch)) + (((((*game_state))).recoil_pitch))); + struct SuicVec3 forward = suic_v3((((sinf(view_yaw))) * ((cosf(view_pitch)))), sinf(view_pitch), (((cosf(view_yaw))) * ((cosf(view_pitch))))); + (((*game_state))).cam_pos = suic_v3_add((((*game_state))).cam_pos, suic_v3_mul(forward, 0.300000)); + ((((*game_state))).cam_pos).y = (((((((*game_state))).cam_pos).y)) - ((0.200000))); + } + float terrain_h = suic_terrain_height(((((*game_state))).cam_pos).x, ((((*game_state))).cam_pos).z); + ((((*game_state))).cam_pos).y = fmaxf(((((*game_state))).cam_pos).y, (((terrain_h)) + ((1.500000)))); +} + +void suic_game_render(struct GameState* game_state) { + int sw = GetScreenWidth(); + int sh = GetScreenHeight(); + int my_id = suic_net_get_my_id(); + float view_yaw = ((((((*game_state))).yaw)) + (((((*game_state))).recoil_yaw))); + float view_pitch = ((((((*game_state))).pitch)) + (((((*game_state))).recoil_pitch))); + float clamped_view_pitch = view_pitch; + if ((((clamped_view_pitch)) < ((((-1.500000)))))) { + clamped_view_pitch = ((-1.500000)); + } + if ((((clamped_view_pitch)) > ((1.500000)))) { + clamped_view_pitch = 1.500000; + } + struct SuicVec3 forward = suic_v3((((sinf(view_yaw))) * ((cosf(clamped_view_pitch)))), sinf(clamped_view_pitch), (((cosf(view_yaw))) * ((cosf(clamped_view_pitch))))); + float fov = 75.000000; + if ((((*game_state))).scoped) { + fov = 30.000000; + } + suic_begin_mode3d(((((*game_state))).cam_pos).x, ((((*game_state))).cam_pos).y, ((((*game_state))).cam_pos).z, (((((((*game_state))).cam_pos).x)) + (((forward).x))), (((((((*game_state))).cam_pos).y)) + (((forward).y))), (((((((*game_state))).cam_pos).z)) + (((forward).z))), 0.000000, 1.000000, 0.000000, fov, 0); + suic_terrain_draw((((*game_state))).cam_pos); + float border_size = (((SUIC_TERRAIN_MAX)) - ((SUIC_TERRAIN_MIN))); + suic_draw_cube(0.000000, 0.000000, 0.000000, border_size, 1000.000000, border_size, 255, 0, 0, 100); + int i = 0; + while ((((i)) < ((SUIC_NET_MAX_ITEMS)))) { + if (suic_net_item_present(i)) { + int ic_r = 220; + int ic_g = 220; + int ic_b = 220; + int item_type = suic_net_item_type(i); + if ((((item_type)) == ((SUIC_ITEM_MEDKIT)))) { + ic_r = 120; + ic_g = 255; + ic_b = 120; + } + if ((((item_type)) == ((SUIC_ITEM_AMMO_PISTOL)))) { + ic_r = 255; + ic_g = 220; + ic_b = 120; + } + if ((((item_type)) == ((SUIC_ITEM_AMMO_RIFLE)))) { + ic_r = 255; + ic_g = 180; + ic_b = 120; + } + struct SuicVec3 item_pos = suic_v3(suic_net_item_x(i), suic_net_item_y(i), suic_net_item_z(i)); + struct SuicVec3 item_normal = suic_v3(0.000000, 1.000000, 0.000000); + struct SuicColor base_color = ((struct SuicColor){ .r = (uint8_t) ic_r, .g = (uint8_t) ic_g, .b = (uint8_t) ic_b, .a = 255 }); + struct SuicColor lit_color = suic_light_apply_shadows(base_color, item_normal, item_pos, ((&(((*game_state))).light))); + suic_draw_sphere((item_pos).x, (item_pos).y, (item_pos).z, 0.300000, (int) (lit_color).r, (int) (lit_color).g, (int) (lit_color).b, (int) (lit_color).a); + } + i = (((i)) + ((1))); + } + i = 0; + while ((((i)) < ((SUIC_NET_MAX_PLAYERS)))) { + if (suic_net_player_present(i)) { + float interp_factor = ((((((*game_state))).interp_timer)) / (((((1.000000)) / ((20.000000)))))); + if ((((interp_factor)) > ((1.000000)))) { + interp_factor = 1.000000; + } + struct SuicVec3 prev = suic_v3(suic_net_player_prev_x(i), suic_net_player_prev_y(i), suic_net_player_prev_z(i)); + struct SuicVec3 curr = suic_v3(suic_net_player_x(i), suic_net_player_y(i), suic_net_player_z(i)); + struct SuicVec3 p = suic_v3_add(suic_v3_mul(prev, (((1.000000)) - ((interp_factor)))), suic_v3_mul(curr, interp_factor)); + int c_r = 255; + int c_g = 80; + int c_b = 80; + if ((((i)) == ((my_id)))) { + c_r = 80; + c_g = 180; + c_b = 255; + } + if (((!suic_net_player_alive(i)))) { + c_r = 120; + c_g = 120; + c_b = 120; + } + struct SuicVec3 player_normal = suic_v3(0.000000, 1.000000, 0.000000); + struct SuicColor base_color = ((struct SuicColor){ .r = (uint8_t) c_r, .g = (uint8_t) c_g, .b = (uint8_t) c_b, .a = 255 }); + struct SuicColor lit_color = suic_light_apply_shadows(base_color, player_normal, p, ((&(((*game_state))).light))); + suic_draw_capsule((p).x, ((((p).y)) - ((0.500000))), (p).z, (p).x, ((((p).y)) + ((0.500000))), (p).z, 0.350000, 8, 8, (int) (lit_color).r, (int) (lit_color).g, (int) (lit_color).b, (int) (lit_color).a); + } + i = (((i)) + ((1))); + } + suic_end_mode3d(); + i = 0; + while ((((i)) < ((SUIC_NET_MAX_PLAYERS)))) { + if (suic_net_player_present(i)) { + float interp_factor = ((((((*game_state))).interp_timer)) / (((((1.000000)) / ((20.000000)))))); + if ((((interp_factor)) > ((1.000000)))) { + interp_factor = 1.000000; + } + struct SuicVec3 prev = suic_v3(suic_net_player_prev_x(i), suic_net_player_prev_y(i), suic_net_player_prev_z(i)); + struct SuicVec3 curr = suic_v3(suic_net_player_x(i), suic_net_player_y(i), suic_net_player_z(i)); + struct SuicVec3 p = suic_v3_add(suic_v3_mul(prev, (((1.000000)) - ((interp_factor)))), suic_v3_mul(curr, interp_factor)); + suic_ui_draw_nameplate(sw, sh, (p).x, (p).y, (p).z, suic_net_player_username(i), (((i)) == ((my_id))), ((((*game_state))).cam_pos).x, ((((*game_state))).cam_pos).y, ((((*game_state))).cam_pos).z, (((((((*game_state))).cam_pos).x)) + (((forward).x))), (((((((*game_state))).cam_pos).y)) + (((forward).y))), (((((((*game_state))).cam_pos).z)) + (((forward).z)))); + } + i = (((i)) + ((1))); + } + if (((((((my_id)) == ((255))))) || ((((!suic_net_player_present(my_id))))))) { + suic_draw_text(suic_alloc_array(NULL, sizeof(char), 8, "Offline"), 10, 10, 20, 255, 255, 255, 255); + } + if (((((((my_id)) != ((255))))) && ((suic_net_player_present(my_id))))) { + int weapon = suic_net_player_weapon(my_id); + int mag = suic_net_player_rifle_mag(my_id); + int reserve = suic_net_player_rifle_ammo(my_id); + if ((((weapon)) == ((SUIC_WEAPON_PISTOL)))) { + mag = suic_net_player_pistol_mag(my_id); + reserve = suic_net_player_pistol_ammo(my_id); + } + suic_ui_draw_hud(suic_net_player_hp(my_id), weapon, mag, reserve, suic_net_player_medkits(my_id), suic_net_player_reload_time(my_id)); + } + suic_draw_fps((((sw)) - ((90))), 10); + suic_ui_draw_room_state(sw, sh, suic_net_room_state(), suic_net_countdown(), suic_net_winner_name()); + suic_ui_draw_crosshair(sw, sh, (((*game_state))).cross_spread, (((*game_state))).scoped); +} + +int suic_main(void) { + struct SuicVec3 zero_vec = ((struct SuicVec3){ .x = 0.000000, .y = 0.000000, .z = 0.000000 }); + struct SuicDirectionalLight default_light = ((struct SuicDirectionalLight){ .direction = zero_vec, .color = zero_vec, .intensity = 0.000000, .ambientIntensity = 0.000000, .shadowBias = 0.000000, .shadowIntensity = 0.000000 }); + struct GameState game_state = ((struct GameState){ .cam_pos = ((struct SuicVec3){ .x = 0.000000, .y = 5.000000, .z = 6.000000 }), .yaw = 0.000000, .pitch = 0.000000, .prev_body_pos = ((struct SuicVec3){ .x = 0.000000, .y = 5.000000, .z = 6.000000 }), .current_target_body = ((struct SuicVec3){ .x = 0.000000, .y = 5.000000, .z = 6.000000 }), .interp_timer = 0.000000, .recoil_yaw = 0.000000, .recoil_pitch = 0.000000, .cross_spread = 0.000000, .scoped = false, .fire_cooldown = 0.000000, .shots_in_burst = 0, .burst_reset_timer = 0.000000, .client_tick = 0, .light = default_light }); + int screen_w = 1280; + int screen_h = 720; + suic_init_window(screen_w, screen_h, suic_alloc_array(NULL, sizeof(char), 21, "Voxel Shooter Client")); + suic_set_target_fps(120); + suic_disable_cursor(); + (game_state).light = suic_light_create_default(); + char* username = suic_alloc_array(NULL, sizeof(char), 1, ""); + suic_ui_username_prompt(username, SUIC_NET_USERNAME_MAX); + suic_game_init(((&game_state)), username, suic_alloc_array(NULL, sizeof(char), 10, "127.0.0.1"), 27015); + while ((((suic_window_should_close())) == ((false)))) { + float dt = suic_get_frame_time(); + suic_game_handle_input(((&game_state)), dt); + suic_game_update(((&game_state)), dt); + suic_begin_drawing(); + suic_clear_background(135, 206, 235, 255); + suic_game_render(((&game_state))); + suic_end_drawing(); + } + suic_game_shutdown(); + suic_close_window(); + return 0; +} + +int main(int argc, char* argv[]) { + // Initialize GC + gc_init(); + // init globals + // init event loop + int result = suic_main(); + // Shutdown GC + gc_shutdown(); + return result; +} + diff --git a/game/client.o b/game/client.o new file mode 100755 index 0000000..6d7949b Binary files /dev/null and b/game/client.o differ diff --git a/game/client.sui b/game/client.sui index 7f90f69..d4133a1 100644 --- a/game/client.sui +++ b/game/client.sui @@ -1,22 +1,459 @@ + +# Game Client in Suicmez +# Full rewrite of the C client using Suicmez language + +struct SuicVec3 + x: float, + y: float, + z: float, +end + +struct SuicColor + r: u8, + g: u8, + b: u8, + a: u8, +end + +struct SuicDirectionalLight + direction: SuicVec3, + color: SuicVec3, + intensity: float, + ambientIntensity: float, + shadowBias: float, + shadowIntensity: float, +end + +# Constants +const PISTOL_FIRE_RATE = 4.0 +const RIFLE_FIRE_RATE = 12.0 +const RECOIL_RETURN = 18.0 +const CROSS_RETURN = 14.0 +const PISTOL_KICK_PITCH = 0.010 +const PISTOL_KICK_YAW = 0.004 +const RIFLE_KICK_PITCH = 0.018 +const RIFLE_KICK_YAW = 0.010 +const PISTOL_CROSS_KICK = 2.0 +const RIFLE_CROSS_KICK = 4.0 +const PISTOL_SPRAY_GROW = 0.4 +const RIFLE_SPRAY_GROW = 1.2 +const BURST_RESET_TIME = 0.18 + +const SUIC_BTN_RELOAD = 1 +const SUIC_BTN_SWITCH_PISTOL = 2 +const SUIC_BTN_SWITCH_RIFLE = 4 +const SUIC_BTN_PICK = 8 +const SUIC_BTN_USE_MEDKIT = 16 +const SUIC_BTN_JUMP = 32 + +const SUIC_WEAPON_PISTOL = 0 +const SUIC_WEAPON_RIFLE = 1 + +const SUIC_ITEM_MEDKIT = 1 +const SUIC_ITEM_AMMO_PISTOL = 2 +const SUIC_ITEM_AMMO_RIFLE = 3 + +const SUIC_TERRAIN_SIZE = 256.0 +const SUIC_TERRAIN_SCALE = 1.0 +const SUIC_TERRAIN_MIN = -256.0 * 1.0 / 2.0 +const SUIC_TERRAIN_MAX = 256.0 * 1.0 / 2.0 + +const SUIC_NET_MAX_PLAYERS = 16 +const SUIC_NET_USERNAME_MAX = 16 +const SUIC_NET_MAX_ITEMS = 64 + +struct GameState + cam_pos: SuicVec3, + yaw: float, + pitch: float, + prev_body_pos: SuicVec3, + current_target_body: SuicVec3, + interp_timer: float, + recoil_yaw: float, + recoil_pitch: float, + cross_spread: float, + scoped: bool, + fire_cooldown: float, + shots_in_burst: int, + burst_reset_timer: float, + client_tick: int, + light: SuicDirectionalLight, +end + +fn suic_game_init(game_state: *GameState, username: string, server_ip: string, port: int) -> () do + suic_terrain_init() + suic_net_init(server_ip, port) + suic_net_send_hello(username) + disable_cursor() +end + +fn suic_game_shutdown() -> () do + suic_terrain_cleanup() + suic_net_shutdown() +end + +fn suic_game_handle_input(game_state: *GameState, dt: float) -> () do + game_state.client_tick = game_state.client_tick + 1 + + # Cooldowns + game_state.fire_cooldown = game_state.fire_cooldown - dt + if game_state.fire_cooldown < 0.0 do + game_state.fire_cooldown = 0.0 + end + game_state.burst_reset_timer = game_state.burst_reset_timer - dt + if game_state.burst_reset_timer <= 0.0 do + game_state.shots_in_burst = 0 + end + + # Recoil recovery + let k = 1.0 - expf(-RECOIL_RETURN * dt) + game_state.recoil_yaw = game_state.recoil_yaw + (0.0 - game_state.recoil_yaw) * k + game_state.recoil_pitch = game_state.recoil_pitch + (0.0 - game_state.recoil_pitch) * k + + let k2 = 1.0 - expf(-CROSS_RETURN * dt) + game_state.cross_spread = game_state.cross_spread + (0.0 - game_state.cross_spread) * k2 + if game_state.cross_spread < 0.01 do + game_state.cross_spread = 0.0 + end + + # Mouse look + let md = get_mouse_delta() + let sens = 0.0025 + game_state.yaw = game_state.yaw - md.x * sens + game_state.pitch = game_state.pitch - md.y * sens + if game_state.pitch < -1.5 do + game_state.pitch = -1.5 + end + if game_state.pitch > 1.5 do + game_state.pitch = 1.5 + end + + # Movement input + let mut move_x = 0.0 + let mut move_z = 0.0 + if is_key_down(65) do # KEY_A + move_x = move_x + 1.0 + end + if is_key_down(68) do # KEY_D + move_x = move_x - 1.0 + end + if is_key_down(87) do # KEY_W + move_z = move_z + 1.0 + end + if is_key_down(83) do # KEY_S + move_z = move_z - 1.0 + end + + # Buttons + let mut buttons = 0 + if is_key_pressed(82) do # KEY_R + buttons = buttons bitor SUIC_BTN_RELOAD + end + if is_key_pressed(49) do # KEY_ONE + buttons = buttons bitor SUIC_BTN_SWITCH_PISTOL + end + if is_key_pressed(50) do # KEY_TWO + buttons = buttons bitor SUIC_BTN_SWITCH_RIFLE + end + if is_key_pressed(70) do # KEY_F + buttons = buttons bitor SUIC_BTN_PICK + end + if is_key_pressed(72) do # KEY_H + buttons = buttons bitor SUIC_BTN_USE_MEDKIT + end + if is_key_pressed(32) do # KEY_SPACE + buttons = buttons bitor SUIC_BTN_JUMP + end + if is_key_pressed(90) do # KEY_Z + game_state.scoped = not game_state.scoped + end + + let my_id = suic_net_get_my_id() + let view_yaw = game_state.yaw + game_state.recoil_yaw + let view_pitch = game_state.pitch + game_state.recoil_pitch + let mut clamped_view_pitch = view_pitch + if clamped_view_pitch < -1.5 do + clamped_view_pitch = -1.5 + end + if clamped_view_pitch > 1.5 do + clamped_view_pitch = 1.5 + end + + if my_id != 255 do + suic_net_send_input(my_id, game_state.client_tick, move_x, move_z, view_yaw, clamped_view_pitch, buttons) + end + + # Shooting + if my_id != 255 and is_mouse_button_down(0) and game_state.fire_cooldown <= 0.0 and suic_net_player_present(my_id) do + let wpn = suic_net_player_weapon(my_id) + let mut rate = RIFLE_FIRE_RATE + if wpn == SUIC_WEAPON_PISTOL do + rate = PISTOL_FIRE_RATE + end + game_state.fire_cooldown = 1.0 / rate + + game_state.shots_in_burst = game_state.shots_in_burst + 1 + game_state.burst_reset_timer = BURST_RESET_TIME + + if wpn == SUIC_WEAPON_PISTOL do + game_state.recoil_pitch = game_state.recoil_pitch + PISTOL_KICK_PITCH + game_state.recoil_yaw = game_state.recoil_yaw + (get_random_value(-1000, 1000) as float / 1000.0) * PISTOL_KICK_YAW + game_state.cross_spread = game_state.cross_spread + PISTOL_CROSS_KICK + game_state.shots_in_burst as float * PISTOL_SPRAY_GROW + end + if wpn == SUIC_WEAPON_RIFLE do + game_state.recoil_pitch = game_state.recoil_pitch + RIFLE_KICK_PITCH + game_state.recoil_yaw = game_state.recoil_yaw + (get_random_value(-1000, 1000) as float / 1000.0) * RIFLE_KICK_YAW + game_state.cross_spread = game_state.cross_spread + RIFLE_CROSS_KICK + game_state.shots_in_burst as float * RIFLE_SPRAY_GROW + end + + suic_net_send_shoot(my_id, game_state.client_tick) + end +end + +fn suic_game_update(game_state: *GameState, dt: float) -> () do + suic_net_poll() + + let my_id = suic_net_get_my_id() + if my_id != 255 and suic_net_player_present(my_id) do + game_state.prev_body_pos = game_state.current_target_body + game_state.current_target_body = suic_v3(suic_net_player_x(my_id), + suic_net_player_y(my_id), + suic_net_player_z(my_id)) + game_state.interp_timer = 0.0 + end + + # Interpolate camera + if my_id != 255 and suic_net_player_present(my_id) do + let mut interp_factor = game_state.interp_timer / (1.0 / 20.0) + if interp_factor > 1.0 do + interp_factor = 1.0 + end + let interp_body = suic_v3_add(suic_v3_mul(game_state.prev_body_pos, 1.0 - interp_factor), + suic_v3_mul(game_state.current_target_body, interp_factor)) + game_state.cam_pos.x = interp_body.x + game_state.cam_pos.y = interp_body.y + 1.0 + game_state.cam_pos.z = interp_body.z + 0.0001 + + let terrain_h = suic_terrain_height(game_state.cam_pos.x, game_state.cam_pos.z) + game_state.cam_pos.y = fmaxf(game_state.cam_pos.y, terrain_h + 1.5) + end + game_state.interp_timer = game_state.interp_timer + dt + + # Forward offset + if my_id != 255 do + let view_yaw = game_state.yaw + game_state.recoil_yaw + let view_pitch = game_state.pitch + game_state.recoil_pitch + let forward = suic_v3(sinf(view_yaw) * cosf(view_pitch), sinf(view_pitch), + cosf(view_yaw) * cosf(view_pitch)) + game_state.cam_pos = suic_v3_add(game_state.cam_pos, suic_v3_mul(forward, 0.3)) + game_state.cam_pos.y = game_state.cam_pos.y - 0.2 + end + + let terrain_h = suic_terrain_height(game_state.cam_pos.x, game_state.cam_pos.z) + game_state.cam_pos.y = fmaxf(game_state.cam_pos.y, terrain_h + 1.5) +end + +fn suic_game_render(game_state: *GameState) -> () do + let sw = get_screen_width() + let sh = get_screen_height() + let my_id = suic_net_get_my_id() + + let view_yaw = game_state.yaw + game_state.recoil_yaw + let view_pitch = game_state.pitch + game_state.recoil_pitch + let mut clamped_view_pitch = view_pitch + if clamped_view_pitch < -1.5 do + clamped_view_pitch = -1.5 + end + if clamped_view_pitch > 1.5 do + clamped_view_pitch = 1.5 + end + + let forward = suic_v3(sinf(view_yaw) * cosf(clamped_view_pitch), sinf(clamped_view_pitch), + cosf(view_yaw) * cosf(clamped_view_pitch)) + + # Camera setup (simplified - need to implement Camera3D struct) + let mut fov = 75.0 + if game_state.scoped do + fov = 30.0 + end + begin_mode3d(game_state.cam_pos.x, game_state.cam_pos.y, game_state.cam_pos.z, + game_state.cam_pos.x + forward.x, game_state.cam_pos.y + forward.y, game_state.cam_pos.z + forward.z, + 0.0, 1.0, 0.0, fov, 0) + + # Terrain + suic_terrain_draw(game_state.cam_pos) + + # Border + let border_size = SUIC_TERRAIN_MAX - SUIC_TERRAIN_MIN + draw_cube(0.0, 0.0, 0.0, border_size, 1000.0, border_size, 255, 0, 0, 100) + + # Items + let mut i = 0 + while i < SUIC_NET_MAX_ITEMS do + if suic_net_item_present(i) do + let mut ic_r = 220 + let mut ic_g = 220 + let mut ic_b = 220 + let item_type = suic_net_item_type(i) + if item_type == SUIC_ITEM_MEDKIT do + ic_r = 120 + ic_g = 255 + ic_b = 120 + end + if item_type == SUIC_ITEM_AMMO_PISTOL do + ic_r = 255 + ic_g = 220 + ic_b = 120 + end + if item_type == SUIC_ITEM_AMMO_RIFLE do + ic_r = 255 + ic_g = 180 + ic_b = 120 + end + + let item_pos = suic_v3(suic_net_item_x(i), suic_net_item_y(i), suic_net_item_z(i)) + let item_normal = suic_v3(0.0, 1.0, 0.0) + let base_color = SuicColor { r: ic_r as u8, g: ic_g as u8, b: ic_b as u8, a: 255 } + let lit_color = suic_light_apply_shadows(base_color, item_normal, item_pos, &game_state.light) + draw_sphere(item_pos.x, item_pos.y, item_pos.z, 0.3, lit_color.r as int, lit_color.g as int, lit_color.b as int, lit_color.a as int) + end + i = i + 1 + end + + # Players + i = 0 + while i < SUIC_NET_MAX_PLAYERS do + if suic_net_player_present(i) do + let mut interp_factor = game_state.interp_timer / (1.0 / 20.0) + if interp_factor > 1.0 do + interp_factor = 1.0 + end + let prev = suic_v3(suic_net_player_prev_x(i), suic_net_player_prev_y(i), suic_net_player_prev_z(i)) + let curr = suic_v3(suic_net_player_x(i), suic_net_player_y(i), suic_net_player_z(i)) + let p = suic_v3_add(suic_v3_mul(prev, 1.0 - interp_factor), suic_v3_mul(curr, interp_factor)) + + let mut c_r = 255 + let mut c_g = 80 + let mut c_b = 80 + if i == my_id do + c_r = 80 + c_g = 180 + c_b = 255 + end + if not suic_net_player_alive(i) do + c_r = 120 + c_g = 120 + c_b = 120 + end + + let player_normal = suic_v3(0.0, 1.0, 0.0) + let base_color = SuicColor { r: c_r as u8, g: c_g as u8, b: c_b as u8, a: 255 } + let lit_color = suic_light_apply_shadows(base_color, player_normal, p, &game_state.light) + draw_capsule(p.x, p.y - 0.5, p.z, p.x, p.y + 0.5, p.z, 0.35, 8, 8, lit_color.r as int, lit_color.g as int, lit_color.b as int, lit_color.a as int) + end + i = i + 1 + end + + end_mode3d() + + # Nameplates + i = 0 + while i < SUIC_NET_MAX_PLAYERS do + if suic_net_player_present(i) do + let mut interp_factor = game_state.interp_timer / (1.0 / 20.0) + if interp_factor > 1.0 do + interp_factor = 1.0 + end + let prev = suic_v3(suic_net_player_prev_x(i), suic_net_player_prev_y(i), suic_net_player_prev_z(i)) + let curr = suic_v3(suic_net_player_x(i), suic_net_player_y(i), suic_net_player_z(i)) + let p = suic_v3_add(suic_v3_mul(prev, 1.0 - interp_factor), suic_v3_mul(curr, interp_factor)) + + suic_ui_draw_nameplate(sw, sh, p.x, p.y, p.z, suic_net_player_username(i), i == my_id, + game_state.cam_pos.x, game_state.cam_pos.y, game_state.cam_pos.z, + game_state.cam_pos.x + forward.x, game_state.cam_pos.y + forward.y, game_state.cam_pos.z + forward.z) + end + i = i + 1 + end + + # HUD + if my_id == 255 or not suic_net_player_present(my_id) do + draw_text("Offline", 10, 10, 20, 255, 255, 255, 255) + end + if my_id != 255 and suic_net_player_present(my_id) do + let weapon = suic_net_player_weapon(my_id) + let mut mag = suic_net_player_rifle_mag(my_id) + let mut reserve = suic_net_player_rifle_ammo(my_id) + if weapon == SUIC_WEAPON_PISTOL do + mag = suic_net_player_pistol_mag(my_id) + reserve = suic_net_player_pistol_ammo(my_id) + end + suic_ui_draw_hud(suic_net_player_hp(my_id), weapon, mag, reserve, suic_net_player_medkits(my_id), suic_net_player_reload_time(my_id)) + end + + draw_fps(sw - 90, 10) + + # Room state + suic_ui_draw_room_state(sw, sh, suic_net_room_state(), suic_net_countdown(), suic_net_winner_name()) + + # Crosshair + suic_ui_draw_crosshair(sw, sh, game_state.cross_spread, game_state.scoped) +end + fn main() -> int do - init_window(1280, 720, "Voxel Shooter Client") + let zero_vec = SuicVec3 { x: 0.0, y: 0.0, z: 0.0 } + let default_light = SuicDirectionalLight { direction: zero_vec, color: zero_vec, intensity: 0.0, ambientIntensity: 0.0, shadowBias: 0.0, shadowIntensity: 0.0 } + # Game state + let mut game_state = GameState { + cam_pos: SuicVec3 { x: 0.0, y: 5.0, z: 6.0 }, + yaw: 0.0, + pitch: 0.0, + prev_body_pos: SuicVec3 { x: 0.0, y: 5.0, z: 6.0 }, + current_target_body: SuicVec3 { x: 0.0, y: 5.0, z: 6.0 }, + interp_timer: 0.0, + recoil_yaw: 0.0, + recoil_pitch: 0.0, + cross_spread: 0.0, + scoped: false, + fire_cooldown: 0.0, + shots_in_burst: 0, + burst_reset_timer: 0.0, + client_tick: 0, + light: default_light + } + + let screen_w = 1280 + let screen_h = 720 + + init_window(screen_w, screen_h, "Voxel Shooter Client") defer close_window() set_target_fps(120) disable_cursor() + game_state.light = suic_light_create_default() + + # Get username + let mut username = "" + suic_ui_username_prompt(username, SUIC_NET_USERNAME_MAX) + + # Initialize game subsystems + suic_game_init(&game_state, username, "127.0.0.1", 27015) + # Main loop while window_should_close() == false do let dt = get_frame_time() - # Rendering + suic_game_handle_input(&game_state, dt) + suic_game_update(&game_state, dt) + begin_drawing() clear_background(135, 206, 235, 255) - # Draw FPS - draw_fps(10, 10) + suic_game_render(&game_state) end_drawing() end + suic_game_shutdown() + 0 -end \ No newline at end of file +end diff --git a/game/inc/suic_app.c b/game/inc/suic_app.c index c024e0c..1ef6eaf 100644 --- a/game/inc/suic_app.c +++ b/game/inc/suic_app.c @@ -1,6 +1,6 @@ #include "suic_app.h" -#include "../libsuicmez/libsuicmez.h" -#include "runtime.h" +#include "../../libsuicmez/libsuicmez.h" +// #include "runtime.h" // Not available void suic_app_init(int width, int height, const char* title) { suic_init_window(width, height, (char*)title); @@ -18,7 +18,7 @@ void suic_app_frame_begin(void) { } void suic_app_frame_end(void) { - draw_fps(10, 40); + suic_draw_fps(10, 40); suic_end_drawing(); } diff --git a/game/inc/suic_gameplay.c b/game/inc/suic_gameplay.c index 9ec4781..a9caf23 100644 --- a/game/inc/suic_gameplay.c +++ b/game/inc/suic_gameplay.c @@ -1,5 +1,5 @@ #include "suic_app.h" -#include "runtime.h" +// #include "runtime.h" // Not available #include const char* suic_item_name(uint8_t t) { diff --git a/game/inc/suic_terrain.c b/game/inc/suic_terrain.c index 4590576..ab48a2f 100644 --- a/game/inc/suic_terrain.c +++ b/game/inc/suic_terrain.c @@ -245,7 +245,7 @@ void suic_terrain_cleanup(void) { SuicTerrainShader suic_terrain_shader_load(void) { SuicTerrainShader ts = {0}; - ts.shader = LoadShader("terrain.vs", "terrain.fs"); + ts.shader = LoadShader("game/terrain.vs", "game/terrain.fs"); if (ts.shader.id != 0) { ts.locViewPos = GetShaderLocation(ts.shader, "viewPos"); diff --git a/game/junk b/game/junk new file mode 100644 index 0000000..d022b0d --- /dev/null +++ b/game/junk @@ -0,0 +1,457 @@ +# Game Client in Suicmez +# Full rewrite of the C client using Suicmez language + +struct SuicVec3 + x: float, + y: float, + z: float, +end + +struct SuicColor + r: u8, + g: u8, + b: u8, + a: u8, +end + +struct SuicDirectionalLight + direction: SuicVec3, + color: SuicVec3, + intensity: float, + ambientIntensity: float, + shadowBias: float, + shadowIntensity: float, +end + +# Constants +const PISTOL_FIRE_RATE = 4.0 +const RIFLE_FIRE_RATE = 12.0 +const RECOIL_RETURN = 18.0 +const CROSS_RETURN = 14.0 +const PISTOL_KICK_PITCH = 0.010 +const PISTOL_KICK_YAW = 0.004 +const RIFLE_KICK_PITCH = 0.018 +const RIFLE_KICK_YAW = 0.010 +const PISTOL_CROSS_KICK = 2.0 +const RIFLE_CROSS_KICK = 4.0 +const PISTOL_SPRAY_GROW = 0.4 +const RIFLE_SPRAY_GROW = 1.2 +const BURST_RESET_TIME = 0.18 + +const SUIC_BTN_RELOAD = 1 +const SUIC_BTN_SWITCH_PISTOL = 2 +const SUIC_BTN_SWITCH_RIFLE = 4 +const SUIC_BTN_PICK = 8 +const SUIC_BTN_USE_MEDKIT = 16 +const SUIC_BTN_JUMP = 32 + +const SUIC_WEAPON_PISTOL = 0 +const SUIC_WEAPON_RIFLE = 1 + +const SUIC_ITEM_MEDKIT = 1 +const SUIC_ITEM_AMMO_PISTOL = 2 +const SUIC_ITEM_AMMO_RIFLE = 3 + +const SUIC_TERRAIN_SIZE = 256.0 +const SUIC_TERRAIN_SCALE = 1.0 +const SUIC_TERRAIN_MIN = -256.0 * 1.0 / 2.0 +const SUIC_TERRAIN_MAX = 256.0 * 1.0 / 2.0 + +const SUIC_NET_MAX_PLAYERS = 16 +const SUIC_NET_USERNAME_MAX = 16 +const SUIC_NET_MAX_ITEMS = 64 + +struct GameState + cam_pos: SuicVec3, + yaw: float, + pitch: float, + prev_body_pos: SuicVec3, + current_target_body: SuicVec3, + interp_timer: float, + recoil_yaw: float, + recoil_pitch: float, + cross_spread: float, + scoped: bool, + fire_cooldown: float, + shots_in_burst: int, + burst_reset_timer: float, + client_tick: int, + light: SuicDirectionalLight, +end + +fn suic_game_init(game_state: *GameState, username: string, server_ip: string, port: int) -> () do + suic_terrain_init() + suic_net_init(server_ip, port) + suic_net_send_hello(username) + disable_cursor() +end + +fn suic_game_shutdown() -> () do + suic_terrain_cleanup() + suic_net_shutdown() +end + +fn suic_game_handle_input(game_state: *GameState, dt: float) -> () do + game_state.client_tick = game_state.client_tick + 1 + + # Cooldowns + game_state.fire_cooldown = game_state.fire_cooldown - dt + if game_state.fire_cooldown < 0.0 do + game_state.fire_cooldown = 0.0 + end + game_state.burst_reset_timer = game_state.burst_reset_timer - dt + if game_state.burst_reset_timer <= 0.0 do + game_state.shots_in_burst = 0 + end + + # Recoil recovery + let k = 1.0 - expf(-RECOIL_RETURN * dt) + game_state.recoil_yaw = game_state.recoil_yaw + (0.0 - game_state.recoil_yaw) * k + game_state.recoil_pitch = game_state.recoil_pitch + (0.0 - game_state.recoil_pitch) * k + + let k2 = 1.0 - expf(-CROSS_RETURN * dt) + game_state.cross_spread = game_state.cross_spread + (0.0 - game_state.cross_spread) * k2 + if game_state.cross_spread < 0.01 do + game_state.cross_spread = 0.0 + end + + # Mouse look + let md = get_mouse_delta() + let sens = 0.0025 + game_state.yaw = game_state.yaw - md.x * sens + game_state.pitch = game_state.pitch - md.y * sens + if game_state.pitch < -1.5 do + game_state.pitch = -1.5 + end + if game_state.pitch > 1.5 do + game_state.pitch = 1.5 + end + + # Movement input + let mut move_x = 0.0 + let mut move_z = 0.0 + if is_key_down(65) do # KEY_A + move_x = move_x + 1.0 + end + if is_key_down(68) do # KEY_D + move_x = move_x - 1.0 + end + if is_key_down(87) do # KEY_W + move_z = move_z + 1.0 + end + if is_key_down(83) do # KEY_S + move_z = move_z - 1.0 + end + + # Buttons + let mut buttons = 0 + if is_key_pressed(82) do # KEY_R + buttons = buttons bitor SUIC_BTN_RELOAD + end + if is_key_pressed(49) do # KEY_ONE + buttons = buttons bitor SUIC_BTN_SWITCH_PISTOL + end + if is_key_pressed(50) do # KEY_TWO + buttons = buttons bitor SUIC_BTN_SWITCH_RIFLE + end + if is_key_pressed(70) do # KEY_F + buttons = buttons bitor SUIC_BTN_PICK + end + if is_key_pressed(72) do # KEY_H + buttons = buttons bitor SUIC_BTN_USE_MEDKIT + end + if is_key_pressed(32) do # KEY_SPACE + buttons = buttons bitor SUIC_BTN_JUMP + end + if is_key_pressed(90) do # KEY_Z + game_state.scoped = not game_state.scoped + end + + let my_id = suic_net_get_my_id() + let view_yaw = game_state.yaw + game_state.recoil_yaw + let view_pitch = game_state.pitch + game_state.recoil_pitch + let mut clamped_view_pitch = view_pitch + if clamped_view_pitch < -1.5 do + clamped_view_pitch = -1.5 + end + if clamped_view_pitch > 1.5 do + clamped_view_pitch = 1.5 + end + + if my_id != 255 do + suic_net_send_input(my_id, game_state.client_tick, move_x, move_z, view_yaw, clamped_view_pitch, buttons) + end + + # Shooting + if my_id != 255 and is_mouse_button_down(0) and game_state.fire_cooldown <= 0.0 and suic_net_player_present(my_id) do + let wpn = suic_net_player_weapon(my_id) + let mut rate = RIFLE_FIRE_RATE + if wpn == SUIC_WEAPON_PISTOL do + rate = PISTOL_FIRE_RATE + end + game_state.fire_cooldown = 1.0 / rate + + game_state.shots_in_burst = game_state.shots_in_burst + 1 + game_state.burst_reset_timer = BURST_RESET_TIME + + if wpn == SUIC_WEAPON_PISTOL do + game_state.recoil_pitch = game_state.recoil_pitch + PISTOL_KICK_PITCH + game_state.recoil_yaw = game_state.recoil_yaw + (get_random_value(-1000, 1000) as float / 1000.0) * PISTOL_KICK_YAW + game_state.cross_spread = game_state.cross_spread + PISTOL_CROSS_KICK + game_state.shots_in_burst as float * PISTOL_SPRAY_GROW + end + if wpn == SUIC_WEAPON_RIFLE do + game_state.recoil_pitch = game_state.recoil_pitch + RIFLE_KICK_PITCH + game_state.recoil_yaw = game_state.recoil_yaw + (get_random_value(-1000, 1000) as float / 1000.0) * RIFLE_KICK_YAW + game_state.cross_spread = game_state.cross_spread + RIFLE_CROSS_KICK + game_state.shots_in_burst as float * RIFLE_SPRAY_GROW + end + + suic_net_send_shoot(my_id, game_state.client_tick) + end +end + +fn suic_game_update(game_state: *GameState, dt: float) -> () do + suic_net_poll() + + let my_id = suic_net_get_my_id() + if my_id != 255 and suic_net_player_present(my_id) do + game_state.prev_body_pos = game_state.current_target_body + game_state.current_target_body = suic_v3(suic_net_player_x(my_id), + suic_net_player_y(my_id), + suic_net_player_z(my_id)) + game_state.interp_timer = 0.0 + end + + # Interpolate camera + if my_id != 255 and suic_net_player_present(my_id) do + let mut interp_factor = game_state.interp_timer / (1.0 / 20.0) + if interp_factor > 1.0 do + interp_factor = 1.0 + end + let interp_body = suic_v3_add(suic_v3_mul(game_state.prev_body_pos, 1.0 - interp_factor), + suic_v3_mul(game_state.current_target_body, interp_factor)) + game_state.cam_pos.x = interp_body.x + game_state.cam_pos.y = interp_body.y + 1.0 + game_state.cam_pos.z = interp_body.z + 0.0001 + + let terrain_h = suic_terrain_height(game_state.cam_pos.x, game_state.cam_pos.z) + game_state.cam_pos.y = fmaxf(game_state.cam_pos.y, terrain_h + 1.5) + end + game_state.interp_timer = game_state.interp_timer + dt + + # Forward offset + if my_id != 255 do + let view_yaw = game_state.yaw + game_state.recoil_yaw + let view_pitch = game_state.pitch + game_state.recoil_pitch + let forward = suic_v3(sinf(view_yaw) * cosf(view_pitch), sinf(view_pitch), + cosf(view_yaw) * cosf(view_pitch)) + game_state.cam_pos = suic_v3_add(game_state.cam_pos, suic_v3_mul(forward, 0.3)) + game_state.cam_pos.y = game_state.cam_pos.y - 0.2 + end + + let terrain_h = suic_terrain_height(game_state.cam_pos.x, game_state.cam_pos.z) + game_state.cam_pos.y = fmaxf(game_state.cam_pos.y, terrain_h + 1.5) +end + +fn suic_game_render(game_state: *GameState) -> () do + let sw = get_screen_width() + let sh = get_screen_height() + let my_id = suic_net_get_my_id() + + let view_yaw = game_state.yaw + game_state.recoil_yaw + let view_pitch = game_state.pitch + game_state.recoil_pitch + let mut clamped_view_pitch = view_pitch + if clamped_view_pitch < -1.5 do + clamped_view_pitch = -1.5 + end + if clamped_view_pitch > 1.5 do + clamped_view_pitch = 1.5 + end + + let forward = suic_v3(sinf(view_yaw) * cosf(clamped_view_pitch), sinf(clamped_view_pitch), + cosf(view_yaw) * cosf(clamped_view_pitch)) + + # Camera setup (simplified - need to implement Camera3D struct) + let mut fov = 75.0 + if game_state.scoped do + fov = 30.0 + end + begin_mode3d(game_state.cam_pos.x, game_state.cam_pos.y, game_state.cam_pos.z, + game_state.cam_pos.x + forward.x, game_state.cam_pos.y + forward.y, game_state.cam_pos.z + forward.z, + 0.0, 1.0, 0.0, fov, 0) + + # Terrain + suic_terrain_draw(game_state.cam_pos) + + # Border + let border_size = SUIC_TERRAIN_MAX - SUIC_TERRAIN_MIN + draw_cube(0.0, 0.0, 0.0, border_size, 1000.0, border_size, 255, 0, 0, 100) + + # Items + let mut i = 0 + while i < SUIC_NET_MAX_ITEMS do + if suic_net_item_present(i) do + let mut ic_r = 220 + let mut ic_g = 220 + let mut ic_b = 220 + let item_type = suic_net_item_type(i) + if item_type == SUIC_ITEM_MEDKIT do + ic_r = 120 + ic_g = 255 + ic_b = 120 + end + if item_type == SUIC_ITEM_AMMO_PISTOL do + ic_r = 255 + ic_g = 220 + ic_b = 120 + end + if item_type == SUIC_ITEM_AMMO_RIFLE do + ic_r = 255 + ic_g = 180 + ic_b = 120 + end + + let item_pos = suic_v3(suic_net_item_x(i), suic_net_item_y(i), suic_net_item_z(i)) + let item_normal = suic_v3(0.0, 1.0, 0.0) + let base_color = SuicColor { r: ic_r as u8, g: ic_g as u8, b: ic_b as u8, a: 255 } + let lit_color = suic_light_apply_shadows(base_color, item_normal, item_pos, game_state.light) + draw_sphere(item_pos.x, item_pos.y, item_pos.z, 0.3, lit_color.r as int, lit_color.g as int, lit_color.b as int, lit_color.a as int) + end + i = i + 1 + end + + # Players + i = 0 + while i < SUIC_NET_MAX_PLAYERS do + if suic_net_player_present(i) do + let mut interp_factor = game_state.interp_timer / (1.0 / 20.0) + if interp_factor > 1.0 do + interp_factor = 1.0 + end + let prev = suic_v3(suic_net_player_prev_x(i), suic_net_player_prev_y(i), suic_net_player_prev_z(i)) + let curr = suic_v3(suic_net_player_x(i), suic_net_player_y(i), suic_net_player_z(i)) + let p = suic_v3_add(suic_v3_mul(prev, 1.0 - interp_factor), suic_v3_mul(curr, interp_factor)) + + let mut c_r = 255 + let mut c_g = 80 + let mut c_b = 80 + if i == my_id do + c_r = 80 + c_g = 180 + c_b = 255 + end + if not suic_net_player_alive(i) do + c_r = 120 + c_g = 120 + c_b = 120 + end + + let player_normal = suic_v3(0.0, 1.0, 0.0) + let base_color = SuicColor { r: c_r as u8, g: c_g as u8, b: c_b as u8, a: 255 } + let lit_color = suic_light_apply_shadows(base_color, player_normal, p, game_state.light) + draw_capsule(p.x, p.y - 0.5, p.z, p.x, p.y + 0.5, p.z, 0.35, 8, 8, lit_color.r as int, lit_color.g as int, lit_color.b as int, lit_color.a as int) + end + i = i + 1 + end + + end_mode3d() + + # Nameplates + i = 0 + while i < SUIC_NET_MAX_PLAYERS do + if suic_net_player_present(i) do + let mut interp_factor = game_state.interp_timer / (1.0 / 20.0) + if interp_factor > 1.0 do + interp_factor = 1.0 + end + let prev = suic_v3(suic_net_player_prev_x(i), suic_net_player_prev_y(i), suic_net_player_prev_z(i)) + let curr = suic_v3(suic_net_player_x(i), suic_net_player_y(i), suic_net_player_z(i)) + let p = suic_v3_add(suic_v3_mul(prev, 1.0 - interp_factor), suic_v3_mul(curr, interp_factor)) + + suic_ui_draw_nameplate(sw, sh, p.x, p.y, p.z, suic_net_player_username(i), i == my_id, + g_cam_pos.x, g_cam_pos.y, g_cam_pos.z, + g_cam_pos.x + forward.x, g_cam_pos.y + forward.y, g_cam_pos.z + forward.z) + i = i + 1 + end + + # HUD + if my_id == 255 or not suic_net_player_present(my_id) do + draw_text("Offline", 10, 10, 20, 255, 255, 255, 255) + end + if my_id != 255 and suic_net_player_present(my_id) do + let weapon = suic_net_player_weapon(my_id) + let mut mag = suic_net_player_rifle_mag(my_id) + let mut reserve = suic_net_player_rifle_ammo(my_id) + if weapon == SUIC_WEAPON_PISTOL do + mag = suic_net_player_pistol_mag(my_id) + reserve = suic_net_player_pistol_ammo(my_id) + end + suic_ui_draw_hud(suic_net_player_hp(my_id), weapon, mag, reserve, suic_net_player_medkits(my_id), suic_net_player_reload_time(my_id)) + end + + draw_fps(sw - 90, 10) + + # Room state + suic_ui_draw_room_state(sw, sh, suic_net_room_state(), suic_net_countdown(), suic_net_winner_name()) + + # Crosshair + suic_ui_draw_crosshair(sw, sh, game_state.cross_spread, game_state.scoped) +end + +fn main() -> int do + let zero_vec = SuicVec3 { x: 0.0, y: 0.0, z: 0.0 } + let default_light = SuicDirectionalLight { direction: zero_vec, color: zero_vec, intensity: 0.0, ambientIntensity: 0.0, shadowBias: 0.0, shadowIntensity: 0.0 } + # Game state + let mut game_state = GameState { + cam_pos: SuicVec3 { x: 0.0, y: 5.0, z: 6.0 }, + yaw: 0.0, + pitch: 0.0, + prev_body_pos: SuicVec3 { x: 0.0, y: 5.0, z: 6.0 }, + current_target_body: SuicVec3 { x: 0.0, y: 5.0, z: 6.0 }, + interp_timer: 0.0, + recoil_yaw: 0.0, + recoil_pitch: 0.0, + cross_spread: 0.0, + scoped: false, + fire_cooldown: 0.0, + shots_in_burst: 0, + burst_reset_timer: 0.0, + client_tick: 0, + light: default_light + } + + let screen_w = 1280 + let screen_h = 720 + + init_window(screen_w, screen_h, "Voxel Shooter Client") + defer close_window() + set_target_fps(120) + disable_cursor() + + game_state.light = suic_light_create_default() + + # Get username + let mut username = "" + suic_ui_username_prompt(&username, SUIC_NET_USERNAME_MAX) + + # Initialize game subsystems + suic_game_init(&game_state, username, "127.0.0.1", 27015) + + # Main loop + while window_should_close() == false do + let dt = get_frame_time() + + suic_game_handle_input(&game_state, dt) + suic_game_update(&game_state, dt) + + begin_drawing() + clear_background(135, 206, 235, 255) + + suic_game_render(&game_state) + + end_drawing() + end + + suic_game_shutdown() + + 0 +end diff --git a/libsuicmez/libsuicmez.c b/libsuicmez/libsuicmez.c index a9f1f9b..8748272 100644 --- a/libsuicmez/libsuicmez.c +++ b/libsuicmez/libsuicmez.c @@ -83,6 +83,10 @@ void suic_set_target_fps(int fps) { SetTargetFPS(fps); } void suic_enable_msaa_4x(void) { SetConfigFlags(FLAG_MSAA_4X_HINT); } +int suic_get_screen_width(void) { return GetScreenWidth(); } + +int suic_get_screen_height(void) { return GetScreenHeight(); } + // Timing float suic_get_frame_time(void) { return GetFrameTime(); } @@ -148,6 +152,14 @@ void suic_draw_cube_wires(float x, float y, float z, float width, float height, DrawCubeWires((Vector3){x, y, z}, width, height, length, (Color){r, g, b, a}); } +void suic_draw_sphere(float x, float y, float z, float radius, unsigned char r, unsigned char g, unsigned char b, unsigned char a) { + DrawSphere((Vector3){x, y, z}, radius, (Color){r, g, b, a}); +} + +void suic_draw_capsule(float start_x, float start_y, float start_z, float end_x, float end_y, float end_z, float radius, int slices, int rings, unsigned char r, unsigned char g, unsigned char b, unsigned char a) { + DrawCapsule((Vector3){start_x, start_y, start_z}, (Vector3){end_x, end_y, end_z}, radius, slices, rings, (Color){r, g, b, a}); +} + // 2D Drawing (UI Framework Utils) void suic_draw_text(const char *text, int x, int y, int font_size, unsigned char r, unsigned char g, unsigned char b, unsigned char a) { DrawText(text, x, y, font_size, (Color){r, g, b, a}); @@ -173,6 +185,10 @@ void suic_draw_line(int start_x, int start_y, int end_x, int end_y, unsigned cha DrawLine(start_x, start_y, end_x, end_y, (Color){r, g, b, a}); } +void suic_draw_fps(int x, int y) { + DrawFPS(x, y); +} + // Input - Keyboard bool suic_is_key_down(int key) { return IsKeyDown(key); } @@ -205,6 +221,9 @@ float suic_get_mouse_delta_x(void) { return GetMouseDelta().x; } float suic_get_mouse_delta_y(void) { return GetMouseDelta().y; } +// Random number generation +int suic_get_random_value(int min, int max) { return GetRandomValue(min, max); } + // Input - Helper functions for gamedev int suic_is_movement_input(void) { // Returns bitmask: 1=W (forward), 2=A (left), 4=S (backward), 8=D (right) @@ -996,3 +1015,15 @@ int suic_fishsoup_packet_send(int fd, fishsoup_packet_t* packet) { int suic_fishsoup_packet_recv(int fd, fishsoup_packet_t* packet) { return fishsoup_packet_recv(fd, packet); } + +// ============================================================================ +// MATH FUNCTIONS +// ============================================================================ + +float suic_sinf(float x) { return sinf(x); } + +float suic_cosf(float x) { return cosf(x); } + +float suic_expf(float x) { return expf(x); } + +float suic_fmaxf(float a, float b) { return fmaxf(a, b); } diff --git a/libsuicmez/libsuicmez.h b/libsuicmez/libsuicmez.h index 07d6ef9..7e2e7a0 100644 --- a/libsuicmez/libsuicmez.h +++ b/libsuicmez/libsuicmez.h @@ -16,8 +16,12 @@ // ODE support #include -// Perlin noise support -#include "perlin/perlin.h" +// Internal game headers +#include "../game/inc/suic_math.h" +#include "../game/inc/suic_net.h" +#include "../game/inc/suic_ui.h" +#include "../game/inc/suic_terrain.h" +#include "../game/inc/suic_lighting.h" // Logging levels typedef enum suic_log_level { @@ -98,6 +102,8 @@ void suic_close_window(void); bool suic_window_should_close(void); void suic_set_target_fps(int fps); void suic_enable_msaa_4x(void); +int suic_get_screen_width(void); +int suic_get_screen_height(void); // Timing float suic_get_frame_time(void); @@ -127,6 +133,8 @@ void suic_end_mode3d(void); // 3D Drawing void suic_draw_cube(float x, float y, float z, float width, float height, float length, unsigned char r, unsigned char g, unsigned char b, unsigned char a); void suic_draw_cube_wires(float x, float y, float z, float width, float height, float length, unsigned char r, unsigned char g, unsigned char b, unsigned char a); +void suic_draw_sphere(float x, float y, float z, float radius, unsigned char r, unsigned char g, unsigned char b, unsigned char a); +void suic_draw_capsule(float start_x, float start_y, float start_z, float end_x, float end_y, float end_z, float radius, int slices, int rings, unsigned char r, unsigned char g, unsigned char b, unsigned char a); // 2D Drawing (UI Framework Utils) void suic_draw_text(const char *text, int x, int y, int font_size, unsigned char r, unsigned char g, unsigned char b, unsigned char a); @@ -135,6 +143,7 @@ void suic_draw_rectangle_lines(int x, int y, int width, int height, unsigned cha int suic_measure_text(const char *text, int font_size); void suic_draw_circle(int center_x, int center_y, float radius, unsigned char r, unsigned char g, unsigned char b, unsigned char a); void suic_draw_line(int start_x, int start_y, int end_x, int end_y, unsigned char r, unsigned char g, unsigned char b, unsigned char a); +void suic_draw_fps(int x, int y); // Input - Keyboard bool suic_is_key_down(int key); @@ -157,6 +166,9 @@ int suic_is_movement_input(void); // Returns bitmask: 1=W, 2=A, 4=S, 8=D int suic_is_sprint_held(void); int suic_is_jump_pressed(void); +// Random number generation +int suic_get_random_value(int min, int max); + // Cursor management void suic_disable_cursor(void); void suic_enable_cursor(void); @@ -337,6 +349,13 @@ void suic_player_controller_jump(suic_player_controller* player); void suic_player_controller_get_position(suic_player_controller* player, float *x, float *y, float *z); void suic_player_controller_set_position(suic_player_controller* player, float x, float y, float z); +/* ===== MATH FUNCTIONS ===== */ + +float suic_sinf(float x); +float suic_cosf(float x); +float suic_expf(float x); +float suic_fmaxf(float a, float b); + /* ===== FILE I/O API ===== */ typedef void* FileHandle; diff --git a/src/c_lowerer/declaration_transpiler.rs b/src/c_lowerer/declaration_transpiler.rs index 0a987de..577a09d 100644 --- a/src/c_lowerer/declaration_transpiler.rs +++ b/src/c_lowerer/declaration_transpiler.rs @@ -143,7 +143,7 @@ impl DeclarationTranspiler { Some(TypeAnnot::Cons(name, args)) if args.is_empty() => convert_to_c_type(name), Some(TypeAnnot::Cons(name, _args)) => { // Generic types - for now just use the base name - Ok(CType::Ptr(Box::new(CType::Struct(name.clone())))) + Ok(CType::Struct(name.clone())) } Some(TypeAnnot::Ptr(inner)) => { let inner_type = self.type_annot_to_c_type(&Some(*inner.clone()))?; @@ -192,6 +192,8 @@ pub fn convert_to_c_type(name: &String) -> Result { "bool" => Ok(CType::Bool), "string" => Ok(CType::Ptr(Box::new(CType::Char))), "()" => Ok(CType::Void), // Unit type - _ => Ok(CType::Ptr(Box::new(CType::Struct(name.clone())))), // Assume heap-allocated struct + "unit" => Ok(CType::Void), + "Vec2" => Ok(CType::Struct("Vector2".to_string())), + _ => Ok(CType::Struct(name.clone())), // Map to value type by default } } diff --git a/src/c_lowerer/statements_transpiler.rs b/src/c_lowerer/statements_transpiler.rs index be26df7..cf21848 100644 --- a/src/c_lowerer/statements_transpiler.rs +++ b/src/c_lowerer/statements_transpiler.rs @@ -405,14 +405,18 @@ impl StatementsTranspiler { c_stmts.push(defer_stmt); } if let Some(last) = last_stmt { - match &last.kind { - TypedExprKind::Return(_) => { - c_stmts.push(self.transpile_stmt(last)?); - } - _ => { - // Convert to return statement - let c_expr = self.transpile_expr(last)?; - c_stmts.push(CStmt::Return(Some(c_expr))); + if expr.ty == Type::Unit { + c_stmts.push(self.transpile_stmt(last)?); + } else { + match &last.kind { + TypedExprKind::Return(_) => { + c_stmts.push(self.transpile_stmt(last)?); + } + _ => { + // Convert to return statement + let c_expr = self.transpile_expr(last)?; + c_stmts.push(CStmt::Return(Some(c_expr))); + } } } } @@ -506,8 +510,8 @@ impl StatementsTranspiler { let inner_type = self.type_to_ctype(inner)?; Ok(CType::FixedArray(Box::new(inner_type), *size)) } - Type::Struct(name, _) => Ok(CType::Ptr(Box::new(CType::Struct(name.clone())))), - Type::Enum(name, _) => Ok(CType::Ptr(Box::new(CType::Struct(name.clone())))), + Type::Struct(name, _) => Ok(CType::Struct(name.clone())), + Type::Enum(name, _) => Ok(CType::Struct(name.clone())), Type::Tuple(types) => { let mut fields = Vec::new(); for (i, inner_ty) in types.iter().enumerate() { diff --git a/src/codegen/transpiler.rs b/src/codegen/transpiler.rs index ec2a448..50b6d95 100644 --- a/src/codegen/transpiler.rs +++ b/src/codegen/transpiler.rs @@ -74,7 +74,7 @@ impl Transpiler { /// Check if a CType is a pointer type (needs to be tracked in GC bitmap) fn is_pointer_type(ty: &CType) -> bool { - matches!(ty, CType::Ptr(_) | CType::Array(_) | CType::Struct(_)) + matches!(ty, CType::Ptr(_) | CType::Array(_)) } /// Generate pointer bitmap for a struct's fields @@ -272,6 +272,10 @@ impl Transpiler { } } TypedASTNodeKind::Const(c) => { + // Skip constants that are already defined in headers as macros + if matches!(c.name.as_str(), "SUIC_TERRAIN_SIZE" | "SUIC_TERRAIN_SCALE" | "SUIC_TERRAIN_MIN" | "SUIC_TERRAIN_MAX" | "SUIC_NET_MAX_PLAYERS" | "SUIC_NET_USERNAME_MAX" | "SUIC_NET_MAX_ITEMS" | "SUIC_BTN_RELOAD" | "SUIC_BTN_SWITCH_PISTOL" | "SUIC_BTN_SWITCH_RIFLE" | "SUIC_BTN_PICK" | "SUIC_BTN_USE_MEDKIT" | "SUIC_BTN_JUMP" | "SUIC_WEAPON_PISTOL" | "SUIC_WEAPON_RIFLE" | "SUIC_ITEM_MEDKIT" | "SUIC_ITEM_AMMO_PISTOL" | "SUIC_ITEM_AMMO_RIFLE") { + return Ok(()); + } let value_code = self.stmt_transpiler.transpile_expr(&c.value)?; let var_decl = CVarDecl { name: c.name.clone(), @@ -338,7 +342,7 @@ impl Transpiler { fn generate_struct_decl(&self, struct_decl: &CStructDecl) -> String { // Skip predefined structs that are already declared in included headers - if matches!(struct_decl.name.as_str(), "Shader" | "Color") { + if matches!(struct_decl.name.as_str(), "Shader" | "Color" | "SuicVec3" | "SuicColor" | "SuicDirectionalLight") { return String::new(); } let mut output = format!("struct {} {{\n", struct_decl.name); @@ -675,8 +679,9 @@ impl Transpiler { "begin_mode3d" => "suic_begin_mode3d", "end_mode3d" => "suic_end_mode3d", "draw_cube" => "suic_draw_cube", + "draw_sphere" => "suic_draw_sphere", + "draw_capsule" => "suic_draw_capsule", "draw_cube_wires" => "suic_draw_cube_wires", - "enable_msaa_4x" => "suic_enable_msaa_4x", "enable_msaa_8x" => "suic_enable_msaa_8x", "enable_msaa_16x" => "suic_enable_msaa_16x", // Input - Keyboard @@ -687,7 +692,7 @@ impl Transpiler { "is_mouse_button_down" => "suic_is_mouse_button_down", "is_mouse_button_pressed" => "suic_is_mouse_button_pressed", "is_mouse_button_released" => "suic_is_mouse_button_released", - "get_mouse_position" => "suic_get_mouse_position", + "get_mouse_position" => "GetMousePosition", "get_mouse_delta" => "suic_get_mouse_delta", "get_mouse_x" => "suic_get_mouse_x", "get_mouse_y" => "suic_get_mouse_y", @@ -707,13 +712,13 @@ impl Transpiler { "rl_translate_f" => "suic_rl_translate_f", "rl_rotate_f" => "suic_rl_rotate_f", // UI Framework Utils - "draw_text" => "DrawText", - "draw_rectangle" => "DrawRectangle", - "draw_rectangle_lines" => "DrawRectangleLines", - "measure_text" => "MeasureText", - "draw_circle" => "DrawCircle", - "draw_line" => "DrawLine", - "draw_fps" => "DrawFPS", + "draw_text" => "suic_draw_text", + "draw_rectangle" => "suic_draw_rectangle", + "draw_rectangle_lines" => "suic_draw_rectangle_lines", + "measure_text" => "suic_measure_text", + "draw_circle" => "suic_draw_circle", + "draw_line" => "suic_draw_line", + "draw_fps" => "suic_draw_fps", // Player controller functions "player_controller_create" => "suic_player_controller_create", "player_controller_destroy" => "suic_player_controller_destroy", @@ -728,6 +733,24 @@ impl Transpiler { "sin" => "sin", "cos" => "cos", "floor" => "floor", + "get_random_value" => "GetRandomValue", + "get_screen_width" => "GetScreenWidth", + "get_screen_height" => "GetScreenHeight", + "fmaxf" => "fmaxf", + "fminf" => "fminf", + "expf" => "expf", + "sinf" => "sinf", + "cosf" => "cosf", + "tanf" => "tanf", + "asinf" => "asinf", + "acosf" => "acosf", + "atanf" => "atanf", + "atan2f" => "atan2f", + "powf" => "powf", + "sqrtf" => "sqrtf", + "floorf" => "floorf", + "ceilf" => "ceilf", + "roundf" => "roundf", // Perlin noise functions "pnoise1d" => "pnoise1d", "pnoise2d" => "pnoise2d", @@ -771,14 +794,14 @@ impl Transpiler { } CExpr::BinOp(lhs, op, rhs) => { format!( - "({} {} {})", + "((({})) {} (({})))", self.generate_expr(lhs), op.to_string(), self.generate_expr(rhs) ) } CExpr::UnOp(op, expr) => { - format!("{}{}", op.to_string(), self.generate_expr(expr)) + format!("(({}{}))", op.to_string(), self.generate_expr(expr)) } CExpr::Cast(expr, ty) => { match (*expr.clone(), ty.clone()) { @@ -786,9 +809,9 @@ impl Transpiler { _ => format!("({}) {}", ty.to_string(), self.generate_expr(expr)), } } - CExpr::AddrOf(expr) => format!("&{}", self.generate_expr(expr)), - CExpr::Deref(expr) => format!("*{}", self.generate_expr(expr)), - CExpr::Dot(expr, field) => format!("(*{}).{}", self.generate_expr(expr), field), + CExpr::AddrOf(expr) => format!("(&({}))", self.generate_expr(expr)), + CExpr::Deref(expr) => format!("(*({}))", self.generate_expr(expr)), + CExpr::Dot(expr, field) => format!("({}).{}", self.generate_expr(expr), field), CExpr::Index(array, index) => format!( "{}[{}]", self.generate_expr(array), @@ -804,7 +827,7 @@ impl Transpiler { "Vec3" => "suic_vec3".to_string(), _ => format!("struct {}", struct_name), }; - format!("suic_alloc_struct(&sui_typeinfo_{}, sizeof({}), &(({}){{ {} }}))", struct_name, c_type_name, c_type_name, field_inits.join(", ")) + format!("(({}){{ {} }})", c_type_name, field_inits.join(", ")) } CExpr::EnumLit(enum_name, variant_name, args) => { // Find the variant index - for simplicity, assume variants are in order @@ -832,8 +855,8 @@ impl Transpiler { }; format!( - "suic_alloc_struct(&sui_typeinfo_{}, sizeof(struct {}), &(struct {}){{ .discriminant = {}, .data = {{ .{} = {} }} }})", - enum_name, enum_name, enum_name, variant_index, union_field_name, variant_init + "(struct {}){{ .discriminant = {}, .data = {{ .{} = {} }} }}", + enum_name, variant_index, union_field_name, variant_init ) } diff --git a/src/main.rs b/src/main.rs index 85f7886..3b547ac 100644 --- a/src/main.rs +++ b/src/main.rs @@ -123,6 +123,7 @@ fn main() { if let Err(e) = run_file(&filename, args.debug) { eprintln!("Error: {}", e); + std::process::exit(1); } } else { eprintln!( diff --git a/src/parser.rs b/src/parser.rs index 79eb17a..3235bd1 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -231,8 +231,20 @@ impl Parser { let start = self.peek_span().unwrap_or(0..0).start; let name = match self.next() { Some((Token::Variable(n), _)) => n, - Some((_, span)) => return self.error("Expected constant name after 'const' keyword. Example: const PI = 3.14".to_string(), span), - None => return self.error("Expected constant name after 'const' keyword. Example: const PI = 3.14".to_string(), start..start), + Some((_, span)) => { + return self.error( + "Expected constant name after 'const' keyword. Example: const PI = 3.14" + .to_string(), + span, + ); + } + None => { + return self.error( + "Expected constant name after 'const' keyword. Example: const PI = 3.14" + .to_string(), + start..start, + ); + } }; let typ = if matches!(self.peek(), Some(Token::Colon)) { @@ -949,10 +961,14 @@ impl Parser { let size = match self.next() { Some((Token::Int(n), _)) => n, Some((_, span)) => { - return self.error("Expected integer size for fixed array".to_string(), span); + return self + .error("Expected integer size for fixed array".to_string(), span); } None => { - return self.error("Expected integer size for fixed array".to_string(), start..start); + return self.error( + "Expected integer size for fixed array".to_string(), + start..start, + ); } }; self.expect(Token::RBracket)?; @@ -1699,10 +1715,14 @@ impl Parser { let size = match self.next() { Some((Token::Int(n), _)) => n, Some((_, span)) => { - return self.error("Expected integer size for fixed array".to_string(), span); + return self + .error("Expected integer size for fixed array".to_string(), span); } None => { - return self.error("Expected integer size for fixed array".to_string(), start..start); + return self.error( + "Expected integer size for fixed array".to_string(), + start..start, + ); } }; self.expect(Token::RBracket)?; @@ -1979,7 +1999,7 @@ impl Parser { } Some(token) => { let span = self.peek_span().unwrap_or(start..start); - self.error(format!("Unexpected token in pattern: {:?}. Expected variable names, struct patterns like Struct {{ field }}, or enum patterns like Enum::Variant", token), span) + self.error(format!("Unexpected token while parsing for expression: {:?}. Expected variable names, struct patterns like Struct {{ field }}, or enum patterns like Enum::Variant", token), span) } None => self.error( "Unexpected end of file in pattern. Expected a complete pattern.".to_string(), diff --git a/src/typechecker.rs b/src/typechecker.rs index 3c22207..60eac16 100644 --- a/src/typechecker.rs +++ b/src/typechecker.rs @@ -313,6 +313,12 @@ impl TypeEnv { } } + fn update_var_type(&mut self, id: &crate::ast::BindingId, ty: Type) { + if let Some(var_info) = self.vars.get_mut(id) { + var_info.ty = ty; + } + } + fn add_type(&mut self, name: String, info: TypeInfo) { self.types.insert(name, info); } @@ -371,6 +377,16 @@ impl TypeChecker { fn add_builtin_functions(&mut self) { // Builtin types + self.env.types.insert( + "suic_vec2".to_string(), + TypeInfo { + kind: TypeInfoKind::Struct(vec![ + ("x".to_string(), TypeAnnot::Var("float".to_string())), + ("y".to_string(), TypeAnnot::Var("float".to_string())), + ]), + parameters: vec![], + }, + ); self.env.types.insert( "Vector3".to_string(), TypeInfo { @@ -425,6 +441,43 @@ impl TypeChecker { parameters: vec![], }, ); + self.env.types.insert( + "SuicVec3".to_string(), + TypeInfo { + kind: TypeInfoKind::Struct(vec![ + ("x".to_string(), TypeAnnot::Var("float".to_string())), + ("y".to_string(), TypeAnnot::Var("float".to_string())), + ("z".to_string(), TypeAnnot::Var("float".to_string())), + ]), + parameters: vec![], + }, + ); + self.env.types.insert( + "SuicColor".to_string(), + TypeInfo { + kind: TypeInfoKind::Struct(vec![ + ("r".to_string(), TypeAnnot::Var("u8".to_string())), + ("g".to_string(), TypeAnnot::Var("u8".to_string())), + ("b".to_string(), TypeAnnot::Var("u8".to_string())), + ("a".to_string(), TypeAnnot::Var("u8".to_string())), + ]), + parameters: vec![], + }, + ); + self.env.types.insert( + "SuicDirectionalLight".to_string(), + TypeInfo { + kind: TypeInfoKind::Struct(vec![ + ("direction".to_string(), TypeAnnot::Cons("SuicVec3".to_string(), vec![])), + ("color".to_string(), TypeAnnot::Cons("SuicVec3".to_string(), vec![])), + ("intensity".to_string(), TypeAnnot::Var("float".to_string())), + ("ambientIntensity".to_string(), TypeAnnot::Var("float".to_string())), + ("shadowBias".to_string(), TypeAnnot::Var("float".to_string())), + ("shadowIntensity".to_string(), TypeAnnot::Var("float".to_string())), + ]), + parameters: vec![], + }, + ); // Builtin functions // Raylib @@ -497,7 +550,7 @@ impl TypeChecker { FunctionType { type_params: vec![], params: vec![], - return_type: Type::Struct("Vector2".to_string(), vec![]), + return_type: Type::Struct("suic_vec2".to_string(), vec![]), }, ); self.env.functions.insert( @@ -1329,7 +1382,7 @@ impl TypeChecker { FunctionType { type_params: vec![], params: vec![], - return_type: Type::Struct("Vec2".to_string(), vec![]), // suic_vec2 + return_type: Type::Struct("suic_vec2".to_string(), vec![]), // suic_vec2 }, ); self.env.functions.insert( @@ -1369,7 +1422,7 @@ impl TypeChecker { FunctionType { type_params: vec![], params: vec![Type::Int], // mouse button - return_type: Type::Int, + return_type: Type::Bool, }, ); self.env.functions.insert( @@ -1377,7 +1430,7 @@ impl TypeChecker { FunctionType { type_params: vec![], params: vec![Type::Int], // mouse button - return_type: Type::Int, + return_type: Type::Bool, }, ); self.env.functions.insert( @@ -1385,7 +1438,7 @@ impl TypeChecker { FunctionType { type_params: vec![], params: vec![Type::Int], // mouse button - return_type: Type::Int, + return_type: Type::Bool, }, ); @@ -1778,6 +1831,557 @@ impl TypeChecker { return_type: Type::Int, }, ); + + // Math functions + self.env.functions.insert( + "suic_v3".to_string(), + FunctionType { + type_params: vec![], + params: vec![Type::Float, Type::Float, Type::Float], + return_type: Type::Struct("SuicVec3".to_string(), vec![]), + }, + ); + self.env.functions.insert( + "suic_v3_add".to_string(), + FunctionType { + type_params: vec![], + params: vec![Type::Struct("SuicVec3".to_string(), vec![]), Type::Struct("SuicVec3".to_string(), vec![])], + return_type: Type::Struct("SuicVec3".to_string(), vec![]), + }, + ); + self.env.functions.insert( + "suic_v3_sub".to_string(), + FunctionType { + type_params: vec![], + params: vec![Type::Struct("SuicVec3".to_string(), vec![]), Type::Struct("SuicVec3".to_string(), vec![])], + return_type: Type::Struct("SuicVec3".to_string(), vec![]), + }, + ); + self.env.functions.insert( + "suic_v3_mul".to_string(), + FunctionType { + type_params: vec![], + params: vec![Type::Struct("SuicVec3".to_string(), vec![]), Type::Float], + return_type: Type::Struct("SuicVec3".to_string(), vec![]), + }, + ); + self.env.functions.insert( + "suic_v3_dot".to_string(), + FunctionType { + type_params: vec![], + params: vec![Type::Struct("SuicVec3".to_string(), vec![]), Type::Struct("SuicVec3".to_string(), vec![])], + return_type: Type::Float, + }, + ); + self.env.functions.insert( + "suic_v3_len".to_string(), + FunctionType { + type_params: vec![], + params: vec![Type::Struct("SuicVec3".to_string(), vec![])], + return_type: Type::Float, + }, + ); + self.env.functions.insert( + "suic_v3_norm".to_string(), + FunctionType { + type_params: vec![], + params: vec![Type::Struct("SuicVec3".to_string(), vec![])], + return_type: Type::Struct("SuicVec3".to_string(), vec![]), + }, + ); + + // Terrain functions + self.env.functions.insert( + "suic_simplex_noise_2d".to_string(), + FunctionType { + type_params: vec![], + params: vec![Type::Float, Type::Float], + return_type: Type::Float, + }, + ); + self.env.functions.insert( + "suic_fbm_noise".to_string(), + FunctionType { + type_params: vec![], + params: vec![Type::Float, Type::Float, Type::Int], + return_type: Type::Float, + }, + ); + self.env.functions.insert( + "suic_terrain_height".to_string(), + FunctionType { + type_params: vec![], + params: vec![Type::Float, Type::Float], + return_type: Type::Float, + }, + ); + self.env.functions.insert( + "suic_terrain_init".to_string(), + FunctionType { + type_params: vec![], + params: vec![], + return_type: Type::Unit, + }, + ); + self.env.functions.insert( + "suic_terrain_draw".to_string(), + FunctionType { + type_params: vec![], + params: vec![Type::Struct("SuicVec3".to_string(), vec![])], + return_type: Type::Unit, + }, + ); + self.env.functions.insert( + "suic_terrain_cleanup".to_string(), + FunctionType { + type_params: vec![], + params: vec![], + return_type: Type::Unit, + }, + ); + + // Lighting functions + self.env.functions.insert( + "suic_light_create".to_string(), + FunctionType { + type_params: vec![], + params: vec![Type::Float, Type::Float, Type::Float, Type::Float, Type::Float], + return_type: Type::Struct("SuicDirectionalLight".to_string(), vec![]), + }, + ); + self.env.functions.insert( + "suic_light_create_default".to_string(), + FunctionType { + type_params: vec![], + params: vec![], + return_type: Type::Struct("SuicDirectionalLight".to_string(), vec![]), + }, + ); + self.env.functions.insert( + "suic_light_apply".to_string(), + FunctionType { + type_params: vec![], + params: vec![Type::Struct("SuicColor".to_string(), vec![]), Type::Struct("SuicVec3".to_string(), vec![]), Type::Ptr(Box::new(Type::Struct("SuicDirectionalLight".to_string(), vec![])))], + return_type: Type::Struct("SuicColor".to_string(), vec![]), + }, + ); + self.env.functions.insert( + "suic_light_apply_shadows".to_string(), + FunctionType { + type_params: vec![], + params: vec![Type::Struct("SuicColor".to_string(), vec![]), Type::Struct("SuicVec3".to_string(), vec![]), Type::Struct("SuicVec3".to_string(), vec![]), Type::Ptr(Box::new(Type::Struct("SuicDirectionalLight".to_string(), vec![])))], + return_type: Type::Struct("SuicColor".to_string(), vec![]), + }, + ); + self.env.functions.insert( + "suic_light_apply_shadows".to_string(), + FunctionType { + type_params: vec![], + params: vec![Type::Struct("SuicColor".to_string(), vec![]), Type::Struct("SuicVec3".to_string(), vec![]), Type::Struct("SuicVec3".to_string(), vec![]), Type::Ptr(Box::new(Type::Struct("SuicDirectionalLight".to_string(), vec![])))], + return_type: Type::Struct("SuicColor".to_string(), vec![]), + }, + ); + + // Network functions + self.env.functions.insert( + "suic_net_init".to_string(), + FunctionType { + type_params: vec![], + params: vec![Type::String, Type::Int], + return_type: Type::Int, + }, + ); + self.env.functions.insert( + "suic_net_shutdown".to_string(), + FunctionType { + type_params: vec![], + params: vec![], + return_type: Type::Unit, + }, + ); + self.env.functions.insert( + "suic_net_is_connected".to_string(), + FunctionType { + type_params: vec![], + params: vec![], + return_type: Type::Bool, + }, + ); + self.env.functions.insert( + "suic_net_send_hello".to_string(), + FunctionType { + type_params: vec![], + params: vec![Type::String], + return_type: Type::Unit, + }, + ); + self.env.functions.insert( + "suic_net_send_input".to_string(), + FunctionType { + type_params: vec![], + params: vec![Type::Int, Type::Int, Type::Float, Type::Float, Type::Float, Type::Float, Type::Int], + return_type: Type::Unit, + }, + ); + self.env.functions.insert( + "suic_net_send_shoot".to_string(), + FunctionType { + type_params: vec![], + params: vec![Type::Int, Type::Int], + return_type: Type::Unit, + }, + ); + self.env.functions.insert( + "suic_net_poll".to_string(), + FunctionType { + type_params: vec![], + params: vec![], + return_type: Type::Bool, + }, + ); + self.env.functions.insert( + "suic_net_get_my_id".to_string(), + FunctionType { + type_params: vec![], + params: vec![], + return_type: Type::Int, + }, + ); + // Player accessor functions + self.env.functions.insert( + "suic_net_player_present".to_string(), + FunctionType { + type_params: vec![], + params: vec![Type::Int], + return_type: Type::Bool, + }, + ); + self.env.functions.insert( + "suic_net_player_alive".to_string(), + FunctionType { + type_params: vec![], + params: vec![Type::Int], + return_type: Type::Bool, + }, + ); + self.env.functions.insert( + "suic_net_player_hp".to_string(), + FunctionType { + type_params: vec![], + params: vec![Type::Int], + return_type: Type::Int, + }, + ); + self.env.functions.insert( + "suic_net_player_x".to_string(), + FunctionType { + type_params: vec![], + params: vec![Type::Int], + return_type: Type::Float, + }, + ); + self.env.functions.insert( + "suic_net_player_y".to_string(), + FunctionType { + type_params: vec![], + params: vec![Type::Int], + return_type: Type::Float, + }, + ); + self.env.functions.insert( + "suic_net_player_z".to_string(), + FunctionType { + type_params: vec![], + params: vec![Type::Int], + return_type: Type::Float, + }, + ); + self.env.functions.insert( + "suic_net_player_prev_x".to_string(), + FunctionType { + type_params: vec![], + params: vec![Type::Int], + return_type: Type::Float, + }, + ); + self.env.functions.insert( + "suic_net_player_prev_y".to_string(), + FunctionType { + type_params: vec![], + params: vec![Type::Int], + return_type: Type::Float, + }, + ); + self.env.functions.insert( + "suic_net_player_prev_z".to_string(), + FunctionType { + type_params: vec![], + params: vec![Type::Int], + return_type: Type::Float, + }, + ); + self.env.functions.insert( + "suic_net_player_yaw".to_string(), + FunctionType { + type_params: vec![], + params: vec![Type::Int], + return_type: Type::Float, + }, + ); + self.env.functions.insert( + "suic_net_player_pitch".to_string(), + FunctionType { + type_params: vec![], + params: vec![Type::Int], + return_type: Type::Float, + }, + ); + self.env.functions.insert( + "suic_net_player_weapon".to_string(), + FunctionType { + type_params: vec![], + params: vec![Type::Int], + return_type: Type::Int, + }, + ); + self.env.functions.insert( + "suic_net_player_pistol_mag".to_string(), + FunctionType { + type_params: vec![], + params: vec![Type::Int], + return_type: Type::Int, + }, + ); + self.env.functions.insert( + "suic_net_player_rifle_mag".to_string(), + FunctionType { + type_params: vec![], + params: vec![Type::Int], + return_type: Type::Int, + }, + ); + self.env.functions.insert( + "suic_net_player_pistol_ammo".to_string(), + FunctionType { + type_params: vec![], + params: vec![Type::Int], + return_type: Type::Int, + }, + ); + self.env.functions.insert( + "suic_net_player_rifle_ammo".to_string(), + FunctionType { + type_params: vec![], + params: vec![Type::Int], + return_type: Type::Int, + }, + ); + self.env.functions.insert( + "suic_net_player_medkits".to_string(), + FunctionType { + type_params: vec![], + params: vec![Type::Int], + return_type: Type::Int, + }, + ); + self.env.functions.insert( + "suic_net_player_reload_time".to_string(), + FunctionType { + type_params: vec![], + params: vec![Type::Int], + return_type: Type::Int, + }, + ); + self.env.functions.insert( + "suic_net_player_username".to_string(), + FunctionType { + type_params: vec![], + params: vec![Type::Int], + return_type: Type::String, + }, + ); + // Item accessor functions + self.env.functions.insert( + "suic_net_item_present".to_string(), + FunctionType { + type_params: vec![], + params: vec![Type::Int], + return_type: Type::Bool, + }, + ); + self.env.functions.insert( + "suic_net_item_type".to_string(), + FunctionType { + type_params: vec![], + params: vec![Type::Int], + return_type: Type::Int, + }, + ); + self.env.functions.insert( + "suic_net_item_x".to_string(), + FunctionType { + type_params: vec![], + params: vec![Type::Int], + return_type: Type::Float, + }, + ); + self.env.functions.insert( + "suic_net_item_y".to_string(), + FunctionType { + type_params: vec![], + params: vec![Type::Int], + return_type: Type::Float, + }, + ); + self.env.functions.insert( + "suic_net_item_z".to_string(), + FunctionType { + type_params: vec![], + params: vec![Type::Int], + return_type: Type::Float, + }, + ); + // Room state functions + self.env.functions.insert( + "suic_net_room_state".to_string(), + FunctionType { + type_params: vec![], + params: vec![], + return_type: Type::Int, + }, + ); + self.env.functions.insert( + "suic_net_countdown".to_string(), + FunctionType { + type_params: vec![], + params: vec![], + return_type: Type::Float, + }, + ); + self.env.functions.insert( + "suic_net_winner_name".to_string(), + FunctionType { + type_params: vec![], + params: vec![], + return_type: Type::String, + }, + ); + + // UI functions + self.env.functions.insert( + "suic_ui_username_prompt".to_string(), + FunctionType { + type_params: vec![], + params: vec![Type::String, Type::Int], + return_type: Type::Unit, + }, + ); + self.env.functions.insert( + "suic_ui_draw_hud".to_string(), + FunctionType { + type_params: vec![], + params: vec![Type::Int, Type::Int, Type::Int, Type::Int, Type::Int, Type::Int], + return_type: Type::Unit, + }, + ); + self.env.functions.insert( + "suic_ui_draw_crosshair".to_string(), + FunctionType { + type_params: vec![], + params: vec![Type::Int, Type::Int, Type::Float, Type::Bool], + return_type: Type::Unit, + }, + ); + self.env.functions.insert( + "suic_ui_draw_room_state".to_string(), + FunctionType { + type_params: vec![], + params: vec![Type::Int, Type::Int, Type::Int, Type::Float, Type::String], + return_type: Type::Unit, + }, + ); + self.env.functions.insert( + "suic_ui_draw_nameplate".to_string(), + FunctionType { + type_params: vec![], + params: vec![Type::Int, Type::Int, Type::Float, Type::Float, Type::Float, Type::String, Type::Bool, Type::Float, Type::Float, Type::Float, Type::Float, Type::Float, Type::Float], + return_type: Type::Unit, + }, + ); + + // Additional Raylib functions + self.env.functions.insert( + "begin_mode3d".to_string(), + FunctionType { + type_params: vec![], + params: vec![Type::Float, Type::Float, Type::Float, Type::Float, Type::Float, Type::Float, Type::Float, Type::Float, Type::Float, Type::Float, Type::Int], + return_type: Type::Unit, + }, + ); + self.env.functions.insert( + "end_mode3d".to_string(), + FunctionType { + type_params: vec![], + params: vec![], + return_type: Type::Unit, + }, + ); + self.env.functions.insert( + "draw_cube".to_string(), + FunctionType { + type_params: vec![], + params: vec![Type::Float, Type::Float, Type::Float, Type::Float, Type::Float, Type::Float, Type::Int, Type::Int, Type::Int, Type::Int], + return_type: Type::Unit, + }, + ); + self.env.functions.insert( + "draw_cube_wires".to_string(), + FunctionType { + type_params: vec![], + params: vec![Type::Float, Type::Float, Type::Float, Type::Float, Type::Float, Type::Float, Type::Int, Type::Int, Type::Int, Type::Int], + return_type: Type::Unit, + }, + ); + self.env.functions.insert( + "draw_sphere".to_string(), + FunctionType { + type_params: vec![], + params: vec![Type::Float, Type::Float, Type::Float, Type::Float, Type::Int, Type::Int, Type::Int, Type::Int], + return_type: Type::Unit, + }, + ); + self.env.functions.insert( + "draw_capsule".to_string(), + FunctionType { + type_params: vec![], + params: vec![Type::Float, Type::Float, Type::Float, Type::Float, Type::Float, Type::Float, Type::Float, Type::Int, Type::Int, Type::Int, Type::Int, Type::Int, Type::Int], + return_type: Type::Unit, + }, + ); + self.env.functions.insert( + "get_screen_width".to_string(), + FunctionType { + type_params: vec![], + params: vec![], + return_type: Type::Int, + }, + ); + self.env.functions.insert( + "get_screen_height".to_string(), + FunctionType { + type_params: vec![], + params: vec![], + return_type: Type::Int, + }, + ); + self.env.functions.insert( + "get_random_value".to_string(), + FunctionType { + type_params: vec![], + params: vec![Type::Int, Type::Int], + return_type: Type::Int, + }, + ); } fn collect_definitions(&mut self, node: &ASTNode) -> Result<(), TypeError> { @@ -1902,6 +2506,11 @@ impl TypeChecker { methods, }); } + ASTNodeKind::Const(c) => { + let ty = c.typ.as_ref().map(|t| self.type_annot_to_type(t)).unwrap_or(Type::Unknown); + let id = self.next_binding_id(); + self.env.add_var(id, c.name.clone(), ty, BindingKind::Default, node.span.clone()); + } _ => {} } Ok(()) @@ -1911,6 +2520,12 @@ impl TypeChecker { let ty = match &node.kind { ASTNodeKind::Const(c) => { let typed_value = self.typecheck_expr(&c.value)?; + + // Update the type in the environment + if let Some((id, _)) = self.env.get_var_by_name(&c.name) { + self.env.update_var_type(&id, typed_value.ty.clone()); + } + return Ok(TypedASTNode { kind: TypedASTNodeKind::Const(TypedConst { name: c.name.clone(), @@ -2569,51 +3184,17 @@ impl TypeChecker { ExprKind::Dot(obj, field) => { let typed_obj = self.typecheck_expr(obj)?; - let field_type = match &typed_obj.ty { - Type::Struct(name, _) => { - if let Some(type_info) = self.env.get_type(name) { - if let TypeInfoKind::Struct(fields) = &type_info.kind { - if let Some(field_ty) = fields - .iter() - .find(|(f, _)| f == field) - .map(|(_, ty)| self.type_annot_to_type(ty)) - { - field_ty - } else { - // Check for methods in impls - let mut method_type = None; - for impl_info in &self.env.impls { - if impl_info.target == *name { - if let Some(func_type) = impl_info.methods.get(field) { - method_type = Some(Type::Function( - func_type.params.clone(), - Box::new(func_type.return_type.clone()), - )); - break; - } - } - } - method_type.ok_or_else(|| TypeError { - kind: TypeErrorKind::UndefinedField( - field.clone(), - typed_obj.ty.clone(), - ), - span: expr.span.clone(), - })? - } - } else { - return Err(TypeError { - kind: TypeErrorKind::NotAStruct(typed_obj.ty.clone()), - span: obj.span.clone(), - }); - } - } else { + let (name, is_ptr) = match &typed_obj.ty { + Type::Struct(name, _) => (name.clone(), false), + Type::Ptr(inner) => match &**inner { + Type::Struct(name, _) => (name.clone(), true), + _ => { return Err(TypeError { - kind: TypeErrorKind::UndefinedType(name.clone()), + kind: TypeErrorKind::NotAStruct(typed_obj.ty.clone()), span: obj.span.clone(), }); } - } + }, ty => { return Err(TypeError { kind: TypeErrorKind::NotAStruct(ty.clone()), @@ -2622,8 +3203,66 @@ impl TypeChecker { } }; + let field_type = if let Some(type_info) = self.env.get_type(&name) { + if let TypeInfoKind::Struct(fields) = &type_info.kind { + if let Some(field_ty) = fields + .iter() + .find(|(f, _)| f == field) + .map(|(_, ty)| self.type_annot_to_type(ty)) + { + field_ty + } else { + // Check for methods in impls + let mut method_type = None; + for impl_info in &self.env.impls { + if impl_info.target == name { + if let Some(func_type) = impl_info.methods.get(field) { + method_type = Some(Type::Function( + func_type.params.clone(), + Box::new(func_type.return_type.clone()), + )); + break; + } + } + } + method_type.ok_or_else(|| TypeError { + kind: TypeErrorKind::UndefinedField( + field.clone(), + typed_obj.ty.clone(), + ), + span: expr.span.clone(), + })? + } + } else { + return Err(TypeError { + kind: TypeErrorKind::NotAStruct(typed_obj.ty.clone()), + span: obj.span.clone(), + }); + } + } else { + return Err(TypeError { + kind: TypeErrorKind::UndefinedType(name.clone()), + span: obj.span.clone(), + }); + }; + + let final_obj = if is_ptr { + let inner_ty = match &typed_obj.ty { + Type::Ptr(inner) => (**inner).clone(), + _ => unreachable!(), + }; + TypedExpr { + kind: TypedExprKind::UnOp(UnOp::Deref, Box::new(typed_obj.clone())), + span: typed_obj.span.clone(), + attributes: vec![], + ty: inner_ty, + } + } else { + typed_obj + }; + ( - TypedExprKind::Dot(Box::new(typed_obj), field.clone()), + TypedExprKind::Dot(Box::new(final_obj), field.clone()), field_type, ) } @@ -2817,31 +3456,40 @@ impl TypeChecker { } ExprKind::Assign(lhs, rhs) => { - // Check if lhs is a mutable variable - if let ExprKind::Variable(name) = &lhs.kind { - if let Some((_, var_info)) = self.env.get_var_by_name(name) { - if var_info.kind != BindingKind::Mutable { - return Err(TypeError { - kind: TypeErrorKind::MutableityError(format!( - "Cannot assign to immutable variable '{}'", - name - )), - span: lhs.span.clone(), - }); - } - } - } else { - return Err(TypeError { - kind: TypeErrorKind::MutableityError( - "Invalid left-hand side of assignment".to_string(), - ), - span: lhs.span.clone(), - }); - } - let typed_lhs = self.typecheck_expr(lhs)?; let typed_rhs = self.typecheck_expr(rhs)?; + // Check if lhs is a valid l-value and mutable if it's a variable + match &lhs.kind { + ExprKind::Variable(name) => { + if let Some((_, var_info)) = self.env.get_var_by_name(name) { + if var_info.kind != BindingKind::Mutable { + return Err(TypeError { + kind: TypeErrorKind::MutableityError(format!( + "Cannot assign to immutable variable '{}'", + name + )), + span: lhs.span.clone(), + }); + } + } + } + ExprKind::Dot(_, _) + | ExprKind::Index(_, _) + | ExprKind::UnOp(UnOp::Deref, _) => { + // Valid l-values. For now we don't check nested mutability + // (e.g. if the base struct is mutable), but pointers are always mutable. + } + _ => { + return Err(TypeError { + kind: TypeErrorKind::MutableityError( + "Invalid left-hand side of assignment".to_string(), + ), + span: lhs.span.clone(), + }); + } + } + if !self.types_compatible(&typed_rhs.ty, &typed_lhs.ty) { return Err(TypeError { kind: TypeErrorKind::TypeMismatch(typed_lhs.ty.clone(), typed_rhs.ty),