From e953d6fc634ed445332f9fdc47c9c4a6a205ea75 Mon Sep 17 00:00:00 2001 From: Aldrik Ramaekers Date: Mon, 2 Dec 2024 19:39:19 +0100 Subject: save file writing --- project-base/src/windows/platform.c | 14 ++ src/include/world.h | 7 +- src/main.c | 4 +- src/scenes/save_state_select.c | 323 ++++++++++++++++++++++++++++++++++-- src/world.c | 11 +- 5 files changed, 333 insertions(+), 26 deletions(-) diff --git a/project-base/src/windows/platform.c b/project-base/src/windows/platform.c index 119e08e..6e2c9b9 100644 --- a/project-base/src/windows/platform.c +++ b/project-base/src/windows/platform.c @@ -307,6 +307,19 @@ void platform_create_config_directory(char *directory) } } +char* platform_get_save_location(char *buffer, char *directory) +{ + if(SUCCEEDED(SHGetFolderPathA(0, CSIDL_LOCAL_APPDATA|CSIDL_FLAG_CREATE, NULL, 0, buffer))) + { + string_appendn(buffer, "\\", MAX_INPUT_LENGTH); + string_appendn(buffer, directory, MAX_INPUT_LENGTH); + string_appendn(buffer, "\\", MAX_INPUT_LENGTH); + return buffer; + } + + return 0; +} + char* platform_get_config_save_location(char *buffer, char *directory) { if(SUCCEEDED(SHGetFolderPathA(0, CSIDL_LOCAL_APPDATA|CSIDL_FLAG_CREATE, NULL, 0, buffer))) @@ -1298,6 +1311,7 @@ bool platform_write_file_content(char *path, const char *mode, char *buffer, s32 //done: fclose(file); + return true; done_failure: return result; } diff --git a/src/include/world.h b/src/include/world.h index 58e41fa..4f11c5d 100644 --- a/src/include/world.h +++ b/src/include/world.h @@ -194,14 +194,15 @@ typedef struct t_active_job s16 day; s16 timeslot; bool stay_at_destination; - job_offer offer; - employee assignee; - truck assigned_truck; time_t duration_sec; time_t left_at; time_t done_at; bool reversed; + job_offer offer; + employee assignee; + truck assigned_truck; + // dynamic vec2f px_pos; bool is_hovered; diff --git a/src/main.c b/src/main.c index 1b10c0a..e93a4da 100644 --- a/src/main.c +++ b/src/main.c @@ -27,6 +27,8 @@ font* fnt_rd40; font* fnt_rd44; font* fnt_rd48; +#define CONFIG_DIRECTORY "trucker_x" + #include "include/settings.h" #include "include/ui/colors.h" #include "include/ui/animation.h" @@ -68,8 +70,6 @@ font* fnt_rd48; #include "scenes/settings_scene.c" #include "ui/selectors.c" -#define CONFIG_DIRECTORY "trucker_x" - static void draw_debug_overlay(platform_window* window) { static bool enabled = false; diff --git a/src/scenes/save_state_select.c b/src/scenes/save_state_select.c index 464c2fe..a901864 100644 --- a/src/scenes/save_state_select.c +++ b/src/scenes/save_state_select.c @@ -1,13 +1,78 @@ -void save_state_select_scene_init() +typedef struct t_save_file_entry { + bool exists; + char text[30]; + char path[4000]; +} save_file_entry; + +save_file_entry save_entries[6] = {0}; + +void save_state_select_scene_init() +{ + memset(save_entries, 0, sizeof(save_entries)); + + array files = array_create(sizeof(found_file)); + array filters = string_split("*.json"); + bool is_cancelled = false; + + char save_path[4000]; + platform_get_save_location(save_path, CONFIG_DIRECTORY); + + platform_list_files_block(&files, save_path, filters, true, 0, true, &is_cancelled, 0); + printf("Save folder: %s, found %d potential save files.\n", save_path, files.length); + for (s32 i = 0; i < files.length; i++) + { + found_file *file = array_at(&files, i); + + if (platform_file_exists(file->path)) + { + save_file_entry entry; + entry.exists = true; + sprintf(entry.path, file->path); + + file_content name = platform_read_file_content(file->path, "rb"); + if (name.file_error) continue; + + cJSON *json_object = cJSON_Parse(name.content); + if (!json_object) continue; + + cJSON* name_entry = cJSON_GetObjectItem(json_object, "name"); + if (!name_entry) continue; + char name_buf[50]; + string_copyn(name_buf, name_entry->valuestring, 50); + sprintf(entry.text, name_buf); + + cJSON* index_entry = cJSON_GetObjectItem(json_object, "save_index"); + if (!index_entry) continue; + + platform_destroy_file_content(&name); + + if (index_entry->valueint < 0 || index_entry->valueint > 5) continue; + + printf("Found save file: %d %s %s\n", index_entry->valueint, name_buf, file->path); + + save_entries[index_entry->valueint] = entry; + } + } + + array_destroy(&files); + array_destroy(&filters); } -static bool push_save_state_button(float scale, bool enabled, s32 x, s32 y, s32 size) +static bool push_save_state_button(float scale, bool enabled, s32 x, s32 y, s32 size, save_file_entry entry) { if (enabled) { - return button_render(scale, true, 0, x, y, size, size); + s32 pad = 5*scale; + bool success = button_render(scale, true, 0, x, y, size, size); + + s32 title_w = renderer->calculate_text_width(fnt_rd20, "Save file"); + renderer->render_text(fnt_rd20, x+(size/2)-(title_w/2), y+pad*3, "Save file", COLOR_TEXT); + + renderer->render_text_cutoff(fnt_rd24, x+pad + 5*scale, y+pad + ((size-pad*2)/2)-(fnt_rd24->px_h), entry.text, COLOR_TEXT, size-(pad*2)); + + return success; } else { bool success = button_render(scale, true, 0, x, y, size, size); @@ -59,6 +124,23 @@ static void write_save_file(s32 index) log_info("Writing save file."); cJSON *world = cJSON_CreateObject(); + + { + time_t rawtime; + struct tm * timeinfo; + + time ( &rawtime ); + timeinfo = localtime ( &rawtime ); + + char name_buf[40]; + sprintf(name_buf, "%d/%d/%d\n%02d:%02d:%02d", timeinfo->tm_mday, + timeinfo->tm_mon + 1, timeinfo->tm_year + 1900, + timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec); + cJSON_AddItemToObject(world, "name", cJSON_CreateString(name_buf)); + } + + cJSON_AddItemToObject(world, "save_index", cJSON_CreateNumber(index)); + cJSON_AddItemToObject(world, "simulation_time", cJSON_CreateNumber(_active_world->simulation_time)); cJSON_AddItemToObject(world, "start_year", cJSON_CreateNumber(_active_world->start_year)); cJSON_AddItemToObject(world, "money", cJSON_CreateNumber(_active_world->money)); @@ -213,8 +295,8 @@ static void write_save_file(s32 index) cJSON *connections = cJSON_CreateArray(); for (s32 s = 0; s < o->connections.length; s++) { - world_location* loc = *(world_location**)array_at(&o->connections, s); - cJSON_AddItemToArray(connections, cJSON_CreateNumber(loc->id)); + world_location* conn_loc = *(world_location**)array_at(&o->connections, s); + cJSON_AddItemToArray(connections, cJSON_CreateNumber(conn_loc->id)); } cJSON_AddItemToObject(offer, "connections", connections); @@ -247,7 +329,7 @@ static void write_save_file(s32 index) cJSON *truck = cJSON_CreateObject(); cJSON_AddItemToObject(truck, "id", cJSON_CreateNumber(emp->id)); - cJSON_AddItemToObject(truck, "employee_id", cJSON_CreateNumber(emp->assigned_employee == 0 ? 0 : emp->assigned_employee->id)); + cJSON_AddItemToObject(truck, "employee_id", cJSON_CreateNumber(emp->assigned_employee == 0 ? INVALID_ID : emp->assigned_employee->id)); cJSON_AddItemToObject(truck, "type", cJSON_CreateNumber(emp->type)); cJSON_AddItemToArray(trucks, truck); @@ -262,23 +344,232 @@ static void write_save_file(s32 index) { scheduled_job* scheduled_job = array_at(&location->schedule.jobs, r); - // + cJSON *sj = cJSON_CreateObject(); + cJSON_AddItemToObject(sj, "location_id", cJSON_CreateNumber(scheduled_job->location->id)); + cJSON_AddItemToObject(sj, "trust", cJSON_CreateNumber(scheduled_job->trust)); + + cJSON *timeslots = cJSON_CreateArray(); + for (s32 t = 0; t < scheduled_job->offer.shipday_count; t++) + { + scheduled_job_time scheduled_time = scheduled_job->timeslots[t]; + + cJSON *sj_time = cJSON_CreateObject(); + cJSON_AddItemToObject(sj_time, "day", cJSON_CreateNumber(scheduled_time.day)); + cJSON_AddItemToObject(sj_time, "timeslot", cJSON_CreateNumber(scheduled_time.timeslot)); + cJSON_AddItemToObject(sj_time, "stay_at_destination", cJSON_CreateNumber(scheduled_time.stay_at_destination)); + cJSON_AddItemToObject(sj_time, "assignee_id", cJSON_CreateNumber(scheduled_time.assignee == 0 ? INVALID_ID : scheduled_time.assignee->id)); + + cJSON_AddItemToArray(timeslots, sj_time); + } + cJSON_AddItemToObject(sj, "timeslots", timeslots); + + // offer of scheduled job. + { + cJSON *offer = cJSON_CreateObject(); + cJSON_AddItemToObject(offer, "id", cJSON_CreateNumber(scheduled_job->offer.id)); + cJSON_AddItemToObject(offer, "expire_date", cJSON_CreateNumber(scheduled_job->offer.expire_date)); + cJSON_AddItemToObject(offer, "company_id", cJSON_CreateString(scheduled_job->offer.company->name)); + cJSON_AddItemToObject(offer, "product", cJSON_CreateString(scheduled_job->offer.product->name)); + cJSON_AddItemToObject(offer, "shipday_count", cJSON_CreateNumber(scheduled_job->offer.shipday_count)); + + cJSON *shipdays = cJSON_CreateArray(); + for (s32 s = 0; s < MAX_SHIPDAYS; s++) + { + cJSON_AddItemToArray(shipdays, cJSON_CreateNumber(scheduled_job->offer.shipdays[s])); + } + cJSON_AddItemToObject(offer, "shipdays", shipdays); + cJSON_AddItemToObject(offer, "total_distance", cJSON_CreateNumber(scheduled_job->offer.total_distance)); + + cJSON_AddItemToObject(offer, "boat_distance", cJSON_CreateNumber(scheduled_job->offer.boat_distance)); + cJSON_AddItemToObject(offer, "duration_sec_min", cJSON_CreateNumber(scheduled_job->offer.duration_sec_min)); + cJSON_AddItemToObject(offer, "duration_sec_max", cJSON_CreateNumber(scheduled_job->offer.duration_sec_max)); + cJSON_AddItemToObject(offer, "duration_sec_min_excluding_boat", cJSON_CreateNumber(scheduled_job->offer.duration_sec_min_excluding_boat)); + + cJSON *connections = cJSON_CreateArray(); + for (s32 s = 0; s < scheduled_job->offer.connections.length; s++) + { + world_location* conn_loc = *(world_location**)array_at(&scheduled_job->offer.connections, s); + cJSON_AddItemToArray(connections, cJSON_CreateNumber(conn_loc->id)); + } + cJSON_AddItemToObject(offer, "connections", connections); + + cJSON_AddItemToObject(sj, "offer", offer); + } + + cJSON_AddItemToArray(schedule, sj); } cJSON_AddItemToObject(loc, "schedule", schedule); } - // TODO: insights + // Location insights. + cJSON *insights = cJSON_CreateArray(); + { + for (s32 i = 0; i < location->insights.length; i++) + { + money_data_collection* data = array_at(&location->insights, i); + + cJSON *year = cJSON_CreateObject(); + cJSON_AddItemToObject(year, "index", cJSON_CreateNumber(i)); + + cJSON *months = cJSON_CreateArray(); + { + for (s32 x = 0; x < MONTHS_IN_YEAR; x++) + { + money_data month_data = data->months[x]; + + if (isnan(month_data.total_income)) continue; // end of data + + cJSON *month = cJSON_CreateObject(); + cJSON_AddItemToObject(month, "index", cJSON_CreateNumber(x)); + cJSON_AddItemToObject(month, "total_income", cJSON_CreateNumber(month_data.total_income)); + cJSON_AddItemToObject(month, "total_expenses", cJSON_CreateNumber(month_data.total_expenses)); + cJSON_AddItemToObject(month, "total_profit", cJSON_CreateNumber(month_data.total_profit)); + cJSON_AddItemToObject(month, "income_from_trips", cJSON_CreateNumber(month_data.income_from_trips)); + + cJSON_AddItemToObject(month, "expenses_from_trucks", cJSON_CreateNumber(month_data.expenses_from_trucks)); + cJSON_AddItemToObject(month, "expenses_from_utility", cJSON_CreateNumber(month_data.expenses_from_utility)); + cJSON_AddItemToObject(month, "expenses_from_loans", cJSON_CreateNumber(month_data.expenses_from_loans)); + cJSON_AddItemToObject(month, "expenses_from_healthcare", cJSON_CreateNumber(month_data.expenses_from_healthcare)); + cJSON_AddItemToObject(month, "expenses_from_repairs", cJSON_CreateNumber(month_data.expenses_from_repairs)); + cJSON_AddItemToObject(month, "expenses_from_fuel", cJSON_CreateNumber(month_data.expenses_from_fuel)); + cJSON_AddItemToObject(month, "expenses_from_employees", cJSON_CreateNumber(month_data.expenses_from_employees)); + + cJSON_AddItemToArray(months, month); + } + } + cJSON_AddItemToObject(year, "months", months); + + cJSON_AddItemToArray(insights, year); + } + } + cJSON_AddItemToObject(loc, "insights", insights); + cJSON_AddItemToArray(locations, loc); } - cJSON_AddItemToObject(world, "locations", locations); + + // Global insights. + cJSON *insights = cJSON_CreateArray(); + { + for (s32 i = 0; i < _active_world->insights.length; i++) + { + money_data_collection* data = array_at(&_active_world->insights, i); + + cJSON *year = cJSON_CreateObject(); + cJSON_AddItemToObject(year, "index", cJSON_CreateNumber(i)); + + cJSON *months = cJSON_CreateArray(); + { + for (s32 x = 0; x < MONTHS_IN_YEAR; x++) + { + money_data month_data = data->months[x]; + + if (isnan(month_data.total_income)) continue; // end of data + + cJSON *month = cJSON_CreateObject(); + cJSON_AddItemToObject(month, "index", cJSON_CreateNumber(x)); + cJSON_AddItemToObject(month, "total_income", cJSON_CreateNumber(month_data.total_income)); + cJSON_AddItemToObject(month, "total_expenses", cJSON_CreateNumber(month_data.total_expenses)); + cJSON_AddItemToObject(month, "total_profit", cJSON_CreateNumber(month_data.total_profit)); + cJSON_AddItemToObject(month, "income_from_trips", cJSON_CreateNumber(month_data.income_from_trips)); + + cJSON_AddItemToObject(month, "expenses_from_trucks", cJSON_CreateNumber(month_data.expenses_from_trucks)); + cJSON_AddItemToObject(month, "expenses_from_utility", cJSON_CreateNumber(month_data.expenses_from_utility)); + cJSON_AddItemToObject(month, "expenses_from_loans", cJSON_CreateNumber(month_data.expenses_from_loans)); + cJSON_AddItemToObject(month, "expenses_from_healthcare", cJSON_CreateNumber(month_data.expenses_from_healthcare)); + cJSON_AddItemToObject(month, "expenses_from_repairs", cJSON_CreateNumber(month_data.expenses_from_repairs)); + cJSON_AddItemToObject(month, "expenses_from_fuel", cJSON_CreateNumber(month_data.expenses_from_fuel)); + cJSON_AddItemToObject(month, "expenses_from_employees", cJSON_CreateNumber(month_data.expenses_from_employees)); + + cJSON_AddItemToArray(months, month); + } + } + cJSON_AddItemToObject(year, "months", months); + + cJSON_AddItemToArray(insights, year); + } + } + cJSON_AddItemToObject(world, "insights", insights); + + cJSON *active_jobs = cJSON_CreateArray(); + { + for (s32 i = 0; i < _active_world->active_jobs.length; i++) + { + active_job* aj = array_at(&_active_world->active_jobs, i); + + cJSON *job = cJSON_CreateObject(); + cJSON_AddItemToObject(job, "day", cJSON_CreateNumber(aj->day)); + cJSON_AddItemToObject(job, "timeslot", cJSON_CreateNumber(aj->timeslot)); + cJSON_AddItemToObject(job, "stay_at_destination", cJSON_CreateNumber(aj->stay_at_destination)); + cJSON_AddItemToObject(job, "duration_sec", cJSON_CreateNumber(aj->duration_sec)); + cJSON_AddItemToObject(job, "left_at", cJSON_CreateNumber(aj->left_at)); + cJSON_AddItemToObject(job, "done_at", cJSON_CreateNumber(aj->done_at)); + cJSON_AddItemToObject(job, "reversed", cJSON_CreateNumber(aj->reversed)); + + cJSON_AddItemToObject(job, "assignee_id", cJSON_CreateNumber(aj->assignee.id)); + cJSON_AddItemToObject(job, "truck_id", cJSON_CreateNumber(aj->assigned_truck.id)); + + cJSON *offer = cJSON_CreateObject(); + { + cJSON_AddItemToObject(offer, "id", cJSON_CreateNumber(aj->offer.id)); + cJSON_AddItemToObject(offer, "expire_date", cJSON_CreateNumber(aj->offer.expire_date)); + cJSON_AddItemToObject(offer, "company_id", cJSON_CreateString(aj->offer.company->name)); + cJSON_AddItemToObject(offer, "product", cJSON_CreateString(aj->offer.product->name)); + cJSON_AddItemToObject(offer, "shipday_count", cJSON_CreateNumber(aj->offer.shipday_count)); + + cJSON *shipdays = cJSON_CreateArray(); + for (s32 s = 0; s < MAX_SHIPDAYS; s++) + { + cJSON_AddItemToArray(shipdays, cJSON_CreateNumber(aj->offer.shipdays[s])); + } + cJSON_AddItemToObject(offer, "shipdays", shipdays); + cJSON_AddItemToObject(offer, "total_distance", cJSON_CreateNumber(aj->offer.total_distance)); + + cJSON_AddItemToObject(offer, "boat_distance", cJSON_CreateNumber(aj->offer.boat_distance)); + cJSON_AddItemToObject(offer, "duration_sec_min", cJSON_CreateNumber(aj->offer.duration_sec_min)); + cJSON_AddItemToObject(offer, "duration_sec_max", cJSON_CreateNumber(aj->offer.duration_sec_max)); + cJSON_AddItemToObject(offer, "duration_sec_min_excluding_boat", cJSON_CreateNumber(aj->offer.duration_sec_min_excluding_boat)); + + cJSON *connections = cJSON_CreateArray(); + for (s32 s = 0; s < aj->offer.connections.length; s++) + { + world_location* conn_loc = *(world_location**)array_at(&aj->offer.connections, s); + cJSON_AddItemToArray(connections, cJSON_CreateNumber(conn_loc->id)); + } + cJSON_AddItemToObject(offer, "connections", connections); + + cJSON_AddItemToObject(job, "offer", offer); + } + + cJSON_AddItemToArray(active_jobs, job); + } + } + cJSON_AddItemToObject(world, "active_jobs", active_jobs); } char* str = cJSON_Print(world); - printf(str); + + char save_path[4000]; + platform_get_save_location(save_path, CONFIG_DIRECTORY); + + char filename_buf[50]; + sprintf(filename_buf, "save%d.json", index); + strcat(save_path, filename_buf); + + printf("Writing file to %s.\n", save_path); + if (platform_write_file_content(save_path, "w", str, strlen(str))) { + printf("Write success\n", save_path); + } + else { + perror("Error opening file: "); + printf("Write error\n", save_path); + } + cJSON_Delete(world); + + game_set_active_scene(GAME_STATE_WORLD_MAP); } static void save_state_draw_options(platform_window* window) @@ -300,8 +591,8 @@ static void save_state_draw_options(platform_window* window) // top row for (s32 i = 0; i < 3; i++) { - if (push_save_state_button(scale, 0, panel_x + horizontal_pad + (panel_item_size*i) + - (spacing*i), panel_y + horizontal_pad, panel_item_size)) { + if (push_save_state_button(scale, save_entries[i].exists, panel_x + horizontal_pad + (panel_item_size*i) + + (spacing*i), panel_y + horizontal_pad, panel_item_size, save_entries[i])) { if (is_selecting_save_location) write_save_file(i); else load_save_file(i); } @@ -309,10 +600,10 @@ static void save_state_draw_options(platform_window* window) // bottom row for (s32 i = 0; i < 3; i++) { - if (push_save_state_button(scale, 0, panel_x + horizontal_pad + (panel_item_size*i) + (spacing*i), - panel_y + horizontal_pad + panel_item_size+spacing, panel_item_size)) { - if (is_selecting_save_location) write_save_file(3 + i); - else load_save_file(3 + i); + if (push_save_state_button(scale, save_entries[2 + i].exists, panel_x + horizontal_pad + (panel_item_size*i) + (spacing*i), + panel_y + horizontal_pad + panel_item_size+spacing, panel_item_size, save_entries[2 + i])) { + if (is_selecting_save_location) write_save_file(2 + i); + else load_save_file(2 + i); } } diff --git a/src/world.c b/src/world.c index ae09d79..b95e0d9 100644 --- a/src/world.c +++ b/src/world.c @@ -634,7 +634,7 @@ world* world_create_new() world* new_world = mem_alloc(sizeof(world)); new_world->simulation_time = time(NULL); new_world->start_year = gmtime(&new_world->simulation_time)->tm_year; - new_world->money = 10000000.0f; + new_world->money = 100000.0f; new_world->next_id = 1; new_world->active_jobs = array_create(sizeof(active_job)); new_world->investments = (company_investments){10000,10000,10000,10000}; @@ -1529,7 +1529,7 @@ static void world_update_location_scores(world* world) for (s32 i = 0; i < world->locations.length; i++) { world_location* location = array_at(&world->locations, i); - location->is_accessible = true;//world_check_location_accessibility(location, location, 1, 0); + location->is_accessible = world_check_location_accessibility(location, location, 1, 0); if (!location->is_owned) continue; @@ -1754,11 +1754,12 @@ static void draw_dotted_line(float x1, float y1, float x2, float y2, color line_ float px2 = x1 + (x2-x1) * percentage; float py2 = y1 + (y2-y1) * percentage; - if (!world_map_location_is_in_sun((vec2f){px1, py1})) { - line_clr = rgba(255,255,0, 50); + color clr = line_clr; + if (!world_map_location_is_in_sun((vec2f){px2, py2})) { + clr = rgba(255,255,0, 50); } - renderer->render_line(px1, py1, px2, py2, 2, line_clr); + renderer->render_line(px1, py1, px2, py2, 2, clr); } } -- cgit v1.2.3-70-g09d2