549 lines
19 KiB
Text
549 lines
19 KiB
Text
# Suicmez port of game/client.c
|
|
|
|
# Constants
|
|
const PROTOCOL_VERSION = 67
|
|
const SERVER_PORT = 27015
|
|
const MAX_PLAYERS = 16
|
|
const USERNAME_MAX = 16
|
|
const TERRAIN_SIZE = 256
|
|
const TERRAIN_SCALE = 1.0
|
|
const TERRAIN_MIN = - (TERRAIN_SIZE * TERRAIN_SCALE / 2.0)
|
|
const TERRAIN_MAX = TERRAIN_SIZE * TERRAIN_SCALE / 2.0
|
|
|
|
const WEAPON_PISTOL = 0
|
|
const WEAPON_RIFLE = 1
|
|
|
|
const ITEM_NONE = 0
|
|
const ITEM_MEDKIT = 1
|
|
const ITEM_AMMO_PISTOL = 2
|
|
const ITEM_AMMO_RIFLE = 3
|
|
|
|
const MAX_ITEMS = 64
|
|
|
|
const BTN_RELOAD = (1 << 0)
|
|
const BTN_SWITCH_PISTOL = (1 << 1)
|
|
const BTN_SWITCH_RIFLE = (1 << 2)
|
|
const BTN_PICK = (1 << 3)
|
|
const BTN_USE_MEDKIT = (1 << 4)
|
|
const BTN_JUMP = (1 << 5)
|
|
|
|
# Structs
|
|
struct DirectionalLight
|
|
direction: suic_vector3
|
|
color: suic_vector3
|
|
intensity: float
|
|
ambient_intensity: float
|
|
shadow_bias: float
|
|
shadow_intensity: float
|
|
end
|
|
|
|
struct TerrainShader
|
|
shader: suic_shader_handle
|
|
loc_view_pos: int
|
|
loc_light_dir: int
|
|
loc_light_color: int
|
|
loc_light_intensity: int
|
|
loc_ambient_intensity: int
|
|
loc_terrain_color: int
|
|
end
|
|
|
|
# Network message types
|
|
enum MsgType
|
|
MSG_HELLO
|
|
MSG_WELCOME
|
|
MSG_INPUT
|
|
MSG_SNAPSHOT
|
|
MSG_SHOOT
|
|
MSG_ITEMS
|
|
MSG_ROOM_STATE
|
|
end
|
|
|
|
struct MsgHello
|
|
type_: u8
|
|
protocol: u32
|
|
username: string
|
|
end
|
|
|
|
struct MsgWelcome
|
|
type_: u8
|
|
player_id: u8
|
|
server_tick: u32
|
|
end
|
|
|
|
struct MsgInput
|
|
type_: u8
|
|
player_id: u8
|
|
client_tick: u32
|
|
move_x: float
|
|
move_z: float
|
|
yaw: float
|
|
pitch: float
|
|
buttons: u8
|
|
end
|
|
|
|
struct MsgShoot
|
|
type_: u8
|
|
player_id: u8
|
|
client_tick: u32
|
|
end
|
|
|
|
struct PlayerStateNet
|
|
id: u8
|
|
alive: u8
|
|
hp: i16
|
|
x: float
|
|
y: float
|
|
z: float
|
|
yaw: float
|
|
pitch: float
|
|
weapon: u8
|
|
pistol_mag: i16
|
|
rifle_mag: i16
|
|
pistol_ammo: i16
|
|
rifle_ammo: i16
|
|
medkits: i16
|
|
reload_time_left: i16
|
|
username: string
|
|
end
|
|
|
|
struct MsgSnapshot
|
|
type_: u8
|
|
server_tick: u32
|
|
count: u8
|
|
p: [PlayerStateNet]
|
|
end
|
|
|
|
struct ItemNet
|
|
id: u16
|
|
type_: u8
|
|
qty: i16
|
|
x: float
|
|
y: float
|
|
z: float
|
|
end
|
|
|
|
struct MsgItems
|
|
type_: u8
|
|
server_tick: u32
|
|
count: u8
|
|
items: [ItemNet]
|
|
end
|
|
|
|
struct MsgRoomState
|
|
type_: u8
|
|
state: u8
|
|
countdown_remaining: float
|
|
winner_id: u8
|
|
winner_name: string
|
|
end
|
|
|
|
struct RemotePlayer
|
|
present: int
|
|
alive: int
|
|
hp: int
|
|
pos: suic_vector3
|
|
prev_pos: suic_vector3
|
|
yaw: float
|
|
pitch: float
|
|
weapon: u8
|
|
pistol_mag: i16
|
|
rifle_mag: i16
|
|
pistol_ammo: i16
|
|
rifle_ammo: i16
|
|
medkits: i16
|
|
reload_time_left: i16
|
|
username: [u8; 16]
|
|
end
|
|
|
|
struct WorldItem
|
|
present: int
|
|
id: u16
|
|
type_: u8
|
|
qty: i16
|
|
pos: suic_vector3
|
|
end
|
|
|
|
# Main function
|
|
fn main() -> int do
|
|
let sw = 1280
|
|
let sh = 720
|
|
suic_init_window(sw, sh, "Voxel Shooter - Client (Suicmez)")
|
|
suic_set_target_fps(120)
|
|
|
|
# Username prompt
|
|
let my_name = [0; 16]
|
|
suic_username_prompt(my_name)
|
|
|
|
suic_disable_cursor()
|
|
|
|
# Generate terrain mesh
|
|
let terrain_mesh = suic_generate_terrain_mesh()
|
|
let terrain_model = suic_load_model_from_mesh(terrain_mesh)
|
|
|
|
# Setup directional light
|
|
let dir_light = DirectionalLight {
|
|
direction: suic_vector3 { x: -0.8, y: -1.0, z: -0.6 },
|
|
color: suic_vector3 { x: 1.0, y: 1.0, z: 1.0 },
|
|
intensity: 1.2,
|
|
ambient_intensity: 0.3,
|
|
shadow_bias: 0.005,
|
|
shadow_intensity: 0.4
|
|
}
|
|
|
|
let sock = suic_udp_socket_create()
|
|
if sock < 0 do
|
|
return 1
|
|
end
|
|
suic_udp_set_nonblocking(sock)
|
|
|
|
let my_id = 255
|
|
let client_tick = 0
|
|
|
|
let rp = [RemotePlayer { present: 0 }; 16]
|
|
let wi = [WorldItem { present: 0 }; 64]
|
|
|
|
# Room state
|
|
let room_state = 0
|
|
let countdown_remaining = 0.0
|
|
let winner_name = [0; 16]
|
|
|
|
let cam_pos = suic_vector3 { x: 0, y: 5.0, z: 6 }
|
|
let yaw = 0.0
|
|
let pitch = 0.0
|
|
|
|
let prev_body_pos = suic_vector3 { x: 0, y: 5.0, z: 6 }
|
|
let current_target_body = suic_vector3 { x: 0, y: 5.0, z: 6 }
|
|
let interp_timer = 0.0
|
|
|
|
let recoil_yaw = 0.0
|
|
let recoil_pitch = 0.0
|
|
let cross_spread = 0.0
|
|
let scoped = 0
|
|
let fire_cooldown = 0.0
|
|
let shots_in_burst = 0
|
|
let burst_reset_timer = 0.0
|
|
|
|
let pistol_fire_rate = 4.0
|
|
let rifle_fire_rate = 12.0
|
|
let recoil_return = 18.0
|
|
let cross_return = 14.0
|
|
let pistol_kick_pitch = 0.010
|
|
let pistol_kick_yaw = 0.004
|
|
let rifle_kick_pitch = 0.018
|
|
let rifle_kick_yaw = 0.010
|
|
let pistol_cross_kick = 2.0
|
|
let rifle_cross_kick = 4.0
|
|
let pistol_spray_grow = 0.4
|
|
let rifle_spray_grow = 1.2
|
|
let burst_reset_time = 0.18
|
|
|
|
let hello_buf = [0; 21] # sizeof(MsgHello)
|
|
suic_msg_hello_pack(hello_buf, MSG_HELLO, PROTOCOL_VERSION, my_name)
|
|
suic_udp_sendto(sock, hello_buf, 21, "127.0.0.1", SERVER_PORT)
|
|
|
|
while not suic_window_should_close() do
|
|
client_tick = client_tick + 1
|
|
let dt = suic_get_frame_time()
|
|
|
|
fire_cooldown = fire_cooldown - dt
|
|
if fire_cooldown < 0.0 do fire_cooldown = 0.0 end
|
|
burst_reset_timer = burst_reset_timer - dt
|
|
if burst_reset_timer <= 0.0 do shots_in_burst = 0 end
|
|
|
|
let k = 1.0 - suic_expf(-recoil_return * dt)
|
|
recoil_yaw = recoil_yaw + (0.0 - recoil_yaw) * k
|
|
recoil_pitch = recoil_pitch + (0.0 - recoil_pitch) * k
|
|
|
|
k = 1.0 - suic_expf(-cross_return * dt)
|
|
cross_spread = cross_spread + (0.0 - cross_spread) * k
|
|
if cross_spread < 0.01 do cross_spread = 0.0 end
|
|
|
|
# Network receive
|
|
let buf = [0; 1400]
|
|
let from_host = [0; 256]
|
|
let from_port = 0
|
|
let n = suic_udp_recvfrom(sock, buf, 1400, from_host, from_port)
|
|
if n > 0 do
|
|
let type_ = buf[0]
|
|
if type_ == MSG_WELCOME do
|
|
my_id = suic_msg_welcome_unpack(buf)
|
|
end else if type_ == MSG_SNAPSHOT do
|
|
suic_msg_snapshot_unpack(buf, rp, 16)
|
|
if my_id != 255 and rp[my_id].present do
|
|
prev_body_pos = current_target_body
|
|
current_target_body = rp[my_id].pos
|
|
interp_timer = 0.0
|
|
end
|
|
end else if type_ == MSG_ITEMS do
|
|
suic_msg_items_unpack(buf, wi, 64)
|
|
end else if type_ == MSG_ROOM_STATE do
|
|
suic_msg_room_state_unpack(buf, &room_state, &countdown_remaining, winner_name)
|
|
end
|
|
end
|
|
|
|
# Interpolate camera position
|
|
if my_id != 255 and rp[my_id].present do
|
|
let interp_factor = interp_timer / (1.0 / 20.0)
|
|
if interp_factor > 1.0 do interp_factor = 1.0 end
|
|
let interp_body = suic_vector3 {
|
|
x: prev_body_pos.x * (1.0 - interp_factor) + current_target_body.x * interp_factor,
|
|
y: prev_body_pos.y * (1.0 - interp_factor) + current_target_body.y * interp_factor,
|
|
z: prev_body_pos.z * (1.0 - interp_factor) + current_target_body.z * interp_factor
|
|
}
|
|
cam_pos.x = interp_body.x
|
|
cam_pos.y = interp_body.y + 1.0
|
|
cam_pos.z = interp_body.z + 0.0001
|
|
|
|
# Prevent camera from clipping into terrain
|
|
let terrain_h = suic_get_terrain_height(cam_pos.x, cam_pos.z)
|
|
cam_pos.y = suic_fmaxf(cam_pos.y, terrain_h + 1.5)
|
|
end
|
|
interp_timer = interp_timer + dt
|
|
|
|
let md = suic_get_mouse_delta()
|
|
let sens = 0.0025
|
|
yaw = yaw - md.x * sens
|
|
pitch = pitch - md.y * sens
|
|
if pitch < -1.5 do pitch = -1.5 end
|
|
if pitch > 1.5 do pitch = 1.5 end
|
|
|
|
let view_yaw = yaw + recoil_yaw
|
|
let view_pitch = pitch + recoil_pitch
|
|
if view_pitch < -1.5 do view_pitch = -1.5 end
|
|
if view_pitch > 1.5 do view_pitch = 1.5 end
|
|
|
|
let move_x = 0.0
|
|
let move_z = 0.0
|
|
if suic_is_key_down(KEY_W) do move_z = move_z + 1.0 end
|
|
if suic_is_key_down(KEY_A) do move_x = move_x + 1.0 end
|
|
if suic_is_key_down(KEY_S) do move_z = move_z - 1.0 end
|
|
if suic_is_key_down(KEY_D) do move_x = move_x - 1.0 end
|
|
|
|
let buttons = 0
|
|
if suic_is_key_pressed(KEY_ONE) do buttons = buttons bitor BTN_SWITCH_PISTOL end
|
|
if suic_is_key_pressed(KEY_TWO) do buttons = buttons bitor BTN_SWITCH_RIFLE end
|
|
if suic_is_key_pressed(KEY_R) do buttons = buttons bitor BTN_RELOAD end
|
|
if suic_is_key_pressed(KEY_F) do buttons = buttons bitor BTN_PICK end
|
|
if suic_is_key_pressed(KEY_H) do buttons = buttons bitor BTN_USE_MEDKIT end
|
|
if suic_is_key_pressed(KEY_SPACE) do buttons = buttons bitor BTN_JUMP end
|
|
if suic_is_key_pressed(KEY_Z) do scoped = not scoped end
|
|
|
|
if my_id != 255 do
|
|
let in_buf = [0; 23] # sizeof(MsgInput)
|
|
suic_msg_input_pack(in_buf, MSG_INPUT, my_id, client_tick, move_x, move_z, view_yaw, view_pitch, buttons as u8)
|
|
suic_udp_sendto(sock, in_buf, 23, "127.0.0.1", SERVER_PORT)
|
|
end
|
|
|
|
if my_id != 255 and suic_is_mouse_button_down(MOUSE_BUTTON_LEFT) and fire_cooldown <= 0.0 and rp[my_id].present do
|
|
let wpn = rp[my_id].weapon
|
|
let rate = rifle_fire_rate
|
|
if wpn == WEAPON_PISTOL do rate = pistol_fire_rate end
|
|
fire_cooldown = 1.0 / rate
|
|
|
|
shots_in_burst = shots_in_burst + 1
|
|
burst_reset_timer = burst_reset_time
|
|
|
|
if wpn == WEAPON_PISTOL do
|
|
recoil_pitch = recoil_pitch + pistol_kick_pitch
|
|
recoil_yaw = recoil_yaw + (suic_get_random_value(-1000, 1000) as float / 1000.0) * pistol_kick_yaw
|
|
cross_spread = cross_spread + pistol_cross_kick + shots_in_burst as float * pistol_spray_grow
|
|
end else do
|
|
recoil_pitch = recoil_pitch + rifle_kick_pitch
|
|
recoil_yaw = recoil_yaw + (suic_get_random_value(-1000, 1000) as float / 1000.0) * rifle_kick_yaw
|
|
cross_spread = cross_spread + rifle_cross_kick + shots_in_burst as float * rifle_spray_grow
|
|
end
|
|
|
|
let sh_buf = [0; 6] # sizeof(MsgShoot)
|
|
suic_msg_shoot_pack(sh_buf, MSG_SHOOT, my_id, client_tick)
|
|
suic_udp_sendto(sock, sh_buf, 6, "127.0.0.1", SERVER_PORT)
|
|
end
|
|
|
|
let forward = suic_vector3 {
|
|
x: suic_sinf(view_yaw) * suic_cosf(view_pitch),
|
|
y: suic_sinf(view_pitch),
|
|
z: suic_cosf(view_yaw) * suic_cosf(view_pitch)
|
|
}
|
|
|
|
# Offset camera position: 0.3m in front, 0.2m below
|
|
cam_pos = suic_vector3 {
|
|
x: cam_pos.x + forward.x * 0.3,
|
|
y: cam_pos.y + forward.y * 0.3 - 0.2,
|
|
z: cam_pos.z + forward.z * 0.3
|
|
}
|
|
|
|
# Ensure camera doesn't clip into terrain after offset
|
|
let terrain_h = suic_get_terrain_height(cam_pos.x, cam_pos.z)
|
|
cam_pos.y = suic_fmaxf(cam_pos.y, terrain_h + 1.5)
|
|
|
|
suic_begin_drawing()
|
|
suic_clear_background(135, 206, 235, 255) # Sky blue
|
|
|
|
let fovy = 75.0
|
|
if scoped do fovy = 30.0 end
|
|
suic_begin_mode3d(cam_pos.x, cam_pos.y, cam_pos.z, forward.x, forward.y, forward.z, suic_vector3 { x: 0, y: 1, z: 0 }, fovy, 0)
|
|
|
|
# Draw terrain
|
|
suic_draw_model(terrain_model, 0, 0, 0, 1.0, 1.0, 1.0, 80, 140, 70, 255)
|
|
|
|
# Draw border
|
|
suic_draw_cube(0, 0, 0, 256.0, 1000.0, 256.0, 255, 0, 0, 100)
|
|
|
|
# Items
|
|
let i = 0
|
|
while i < 64 do
|
|
if wi[i].present do
|
|
let ic = suic_color { r: 220, g: 220, b: 220, a: 255 }
|
|
if wi[i].type_ == ITEM_MEDKIT do
|
|
ic = suic_color { r: 120, g: 255, b: 120, a: 255 }
|
|
end else do
|
|
if wi[i].type_ == ITEM_AMMO_PISTOL do
|
|
ic = suic_color { r: 255, g: 220, b: 120, a: 255 }
|
|
end else do
|
|
if wi[i].type_ == ITEM_AMMO_RIFLE do
|
|
ic = suic_color { r: 255, g: 180, b: 120, a: 255 }
|
|
end
|
|
end
|
|
end
|
|
|
|
# Apply basic lighting
|
|
let item_normal = suic_vector3 { x: 0, y: 1, z: 0 }
|
|
let lit_color = suic_apply_lighting_with_shadows(ic, item_normal, wi[i].pos, dir_light.direction, dir_light.intensity, dir_light.ambient_intensity, dir_light.shadow_bias)
|
|
suic_draw_sphere(wi[i].pos.x, wi[i].pos.y, wi[i].pos.z, 0.3, lit_color.r, lit_color.g, lit_color.b, lit_color.a)
|
|
end
|
|
i = i + 1
|
|
end
|
|
|
|
# Players
|
|
let i = 0
|
|
while i < 16 do
|
|
if rp[i].present do
|
|
let interp_factor = interp_timer / (1.0 / 20.0)
|
|
if interp_factor > 1.0 do interp_factor = 1.0 end
|
|
let p = suic_vector3 {
|
|
x: rp[i].prev_pos.x * (1.0 - interp_factor) + rp[i].pos.x * interp_factor,
|
|
y: rp[i].prev_pos.y * (1.0 - interp_factor) + rp[i].pos.y * interp_factor,
|
|
z: rp[i].prev_pos.z * (1.0 - interp_factor) + rp[i].pos.z * interp_factor
|
|
}
|
|
let c = suic_color { r: 255, g: 80, b: 80, a: 255 }
|
|
if i == my_id do
|
|
c = suic_color { r: 80, g: 180, b: 255, a: 255 }
|
|
end
|
|
if not rp[i].alive do
|
|
c = suic_color { r: 120, g: 120, b: 120, a: 255 }
|
|
end
|
|
|
|
# Apply basic lighting
|
|
let player_normal = suic_vector3 { x: 0, y: 1, z: 0 }
|
|
let lit_player_color = suic_apply_lighting_with_shadows(c, player_normal, p, dir_light.direction, dir_light.intensity, dir_light.ambient_intensity, dir_light.shadow_bias)
|
|
suic_draw_capsule(p.x, p.y - 0.5, p.z, p.x, p.y + 0.5, p.z, 0.35, 8, 8, lit_player_color.r, lit_player_color.g, lit_player_color.b, lit_player_color.a)
|
|
end
|
|
i = i + 1
|
|
end
|
|
|
|
suic_end_mode3d()
|
|
|
|
# Nameplates - removed for debugging
|
|
|
|
# HUD
|
|
if my_id == 255 or not rp[my_id].present do
|
|
suic_draw_text("Connecting...", 10, 10, 20, 255, 255, 255, 255)
|
|
end
|
|
if my_id != 255 and rp[my_id].present do
|
|
suic_draw_rectangle(10, 10, 280, 110, 0, 0, 0, 120)
|
|
suic_draw_text("HP", 20, 20, 20, 255, 255, 255, 255)
|
|
let health_bar_width = (rp[my_id].hp * 220) / 100
|
|
let health_color = suic_color { r: 255, g: 80, b: 80, a: 120 }
|
|
if rp[my_id].hp > 60 do
|
|
health_color = suic_color { r: 80, g: 255, b: 80, a: 120 }
|
|
end else do
|
|
if rp[my_id].hp > 30 do
|
|
health_color = suic_color { r: 255, g: 200, b: 80, a: 120 }
|
|
end
|
|
end
|
|
suic_draw_rectangle(20, 45, 220, 20, 40, 40, 40, 255)
|
|
suic_draw_rectangle(20, 45, health_bar_width, 20, health_color.r, health_color.g, health_color.b, health_color.a)
|
|
suic_draw_text(suic_text_format("%d", rp[my_id].hp), 250, 47, 18, 255, 255, 255, 255)
|
|
let medkit_color_r = 120
|
|
let medkit_color_g = 120
|
|
let medkit_color_b = 120
|
|
if rp[my_id].medkits > 0 do
|
|
medkit_color_g = 255
|
|
medkit_color_b = 120
|
|
end
|
|
suic_draw_text(suic_text_format("Medkits: %d (H to use)", rp[my_id].medkits), 20, 75, 18, 120, medkit_color_g, medkit_color_b, 120)
|
|
suic_draw_text("1=Pistol 2=Rifle R=Reload F=Pick SPACE=Jump", 20, 95, 12, 150, 150, 150, 150)
|
|
|
|
let weapon_name = "RIFLE"
|
|
let weapon_color = suic_color { r: 255, g: 150, b: 100, a: 255 }
|
|
let current_mag = rp[my_id].rifle_mag
|
|
let reserve_ammo = rp[my_id].rifle_ammo
|
|
if rp[my_id].weapon == WEAPON_PISTOL do
|
|
weapon_name = "PISTOL"
|
|
weapon_color = suic_color { r: 100, g: 200, b: 255, a: 255 }
|
|
current_mag = rp[my_id].pistol_mag
|
|
reserve_ammo = rp[my_id].pistol_ammo
|
|
end
|
|
|
|
suic_draw_rectangle(sw - 260, sh - 120, 250, 110, 0, 0, 0, 180)
|
|
suic_draw_text(weapon_name, sw - 250, sh - 110, 28, weapon_color.r, weapon_color.g, weapon_color.b, weapon_color.a)
|
|
suic_draw_text(suic_text_format("%d", current_mag), sw - 250, sh - 75, 40, 255, 255, 255, 255)
|
|
suic_draw_text(suic_text_format("/ %d", reserve_ammo), sw - 140, sh - 65, 24, 180, 180, 180, 255)
|
|
if rp[my_id].reload_time_left > 0 do
|
|
suic_draw_text("RELOADING...", sw - 250, sh - 30, 20, 255, 200, 80, 255)
|
|
end else if current_mag == 0 do
|
|
suic_draw_text("RELOAD!", sw - 250, sh - 30, 20, 255, 80, 80, 255)
|
|
end
|
|
end
|
|
|
|
suic_draw_fps(sw - 90, 10)
|
|
|
|
# Room state overlay
|
|
if room_state == 0 do # Waiting
|
|
suic_draw_rectangle(sw / 2 - 150, sh / 2 - 50, 350, 100, 0, 0, 0, 200)
|
|
suic_draw_text("WAITING FOR PLAYERS", sw / 2 - 120, sh / 2 - 30, 24, 255, 255, 255, 255)
|
|
suic_draw_text("Need at least 2 players", sw / 2 - 100, sh / 2 - 5, 18, 200, 200, 200, 255)
|
|
end
|
|
if room_state == 1 do # Counting down
|
|
suic_draw_rectangle(sw / 2 - 150, sh / 2 - 50, 350, 100, 0, 0, 0, 200)
|
|
suic_draw_text("GAME STARTING SOON", sw / 2 - 120, sh / 2 - 30, 24, 255, 255, 80, 255)
|
|
suic_draw_text(suic_text_format("%.1f seconds", countdown_remaining), sw / 2 - 60, sh / 2 - 5, 20, 255, 255, 255, 255)
|
|
end
|
|
if room_state == 3 do # Finished
|
|
suic_draw_rectangle(sw / 2 - 200, sh / 2 - 50, 450, 100, 0, 0, 0, 200)
|
|
suic_draw_text("GAME FINISHED", sw / 2 - 80, sh / 2 - 30, 28, 255, 80, 80, 255)
|
|
if winner_name[0] do
|
|
suic_draw_text(suic_text_format("Winner: %s", winner_name), sw / 2 - 100, sh / 2 - 5, 24, 255, 255, 80, 255)
|
|
end
|
|
if not winner_name[0] do
|
|
suic_draw_text("No winner", sw / 2 - 50, sh / 2 - 5, 24, 255, 255, 255, 255)
|
|
end
|
|
end
|
|
|
|
# Crosshair
|
|
let cx = sw / 2
|
|
let cy = sh / 2
|
|
let gap = 6 + cross_spread as int
|
|
let len = 10
|
|
let radius = 3.0
|
|
if scoped do
|
|
gap = 2
|
|
len = 5
|
|
radius = 1.5
|
|
end
|
|
let thick = 2
|
|
let col = suic_color { r: 240, g: 240, b: 245, a: 220 }
|
|
suic_draw_line(cx - gap - len, cy - thick / 2, cx - gap, cy - thick / 2, col.r, col.g, col.b, col.a)
|
|
suic_draw_line(cx + gap, cy - thick / 2, cx + gap + len, cy - thick / 2, col.r, col.g, col.b, col.a)
|
|
suic_draw_line(cx - thick / 2, cy - gap - len, cx - thick / 2, cy - gap, col.r, col.g, col.b, col.a)
|
|
suic_draw_line(cx - thick / 2, cy + gap, cx - thick / 2, cy + gap + len, col.r, col.g, col.b, col.a)
|
|
suic_draw_circle(cx, cy, radius, 240, 240, 245, 160)
|
|
|
|
suic_end_drawing()
|
|
end
|
|
|
|
suic_unload_model(terrain_model)
|
|
suic_unload_mesh(terrain_mesh)
|
|
suic_close_window()
|
|
suic_udp_socket_close(sock)
|
|
0
|
|
end
|