GAME READY
This commit is contained in:
parent
0bbbb20487
commit
5a60e1c62a
18 changed files with 2506 additions and 249 deletions
|
|
@ -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"
|
||||
|
|
|
|||
222
compiler_out.txt
Normal file
222
compiler_out.txt
Normal file
File diff suppressed because one or more lines are too long
BIN
game/client
Executable file
BIN
game/client
Executable file
Binary file not shown.
393
game/client.c
Normal file
393
game/client.c
Normal file
|
|
@ -0,0 +1,393 @@
|
|||
#include "libsuicmez/libsuicmez.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
BIN
game/client.o
Executable file
BIN
game/client.o
Executable file
Binary file not shown.
447
game/client.sui
447
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
|
||||
end
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
#include "suic_app.h"
|
||||
#include "runtime.h"
|
||||
// #include "runtime.h" // Not available
|
||||
#include <math.h>
|
||||
|
||||
const char* suic_item_name(uint8_t t) {
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
457
game/junk
Normal file
457
game/junk
Normal file
|
|
@ -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
|
||||
|
|
@ -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); }
|
||||
|
|
|
|||
|
|
@ -16,8 +16,12 @@
|
|||
// ODE support
|
||||
#include <ode/ode.h>
|
||||
|
||||
// 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;
|
||||
|
|
|
|||
|
|
@ -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<CType, String> {
|
|||
"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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -123,6 +123,7 @@ fn main() {
|
|||
|
||||
if let Err(e) = run_file(&filename, args.debug) {
|
||||
eprintln!("Error: {}", e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
} else {
|
||||
eprintln!(
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue