# 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