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, save_file_entry entry) { if (enabled) { 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); float close_size = size/3; float close_x = x + (size/2)-(close_size/2); float close_y = y + (size/2)-(close_size/2); renderer->render_image(img_close, close_x, close_y, close_size, close_size); return success; } return false; } static bool push_back_button(float scale, s32 back_x, s32 back_y, s32 back_w, s32 back_h) { bool result = false; color tint = COLOR_WHITE; if (mouse_interacts(back_x,back_y,back_w,back_h)) { tint = COLOR_BUTTON_ACTIVE_TINT; platform_set_cursor(main_window, CURSOR_POINTER); if (is_left_clicked()) { result = true; audio_play_sound(snd_click, AUDIO_CHANNEL_SFX_1); } } renderer->render_image_tint(img_back, back_x, back_y, back_w, back_h, tint); font* font_sml = fnt_rd20; char* back_text = "Back"; s32 back_text_width = renderer->calculate_text_width(font_sml, back_text); s32 text_x = back_x + (back_w/2) - (back_text_width/2) + (back_w/12); s32 text_y = back_y + (back_h/2) - (font_sml->px_h/2); renderer->render_text(font_sml, text_x+2, text_y+2, back_text, COLOR_TEXT_SHADOW); renderer->render_text(font_sml, text_x, text_y, back_text, COLOR_TEXT); return result; } extern world* _active_world; static void load_save_file(s32 index) { log_info("Loading save file."); save_file_entry entry = save_entries[index]; if (!entry.exists) return; file_content save_file = platform_read_file_content(entry.path, "rb"); if (save_file.file_error) return; printf("Loading file: %s.\n", entry.path); cJSON *json_object = cJSON_Parse(save_file.content); if (!json_object) { log_info("Failed to load save file."); game_set_active_scene(GAME_STATE_ERROR); return; } world* new_world = world_create_new(false); if (!new_world) { log_info("Failed to load save file."); game_set_active_scene(GAME_STATE_ERROR); return; } // TODO return on error here in release. #define GET_PROP(_prop, _obj, _str)\ cJSON* _prop = cJSON_GetObjectItem(_obj, _str);\ if (!_prop) {\ log_info("Failed to load save file.");\ game_set_active_scene(GAME_STATE_ERROR);\ } GET_PROP(version, json_object, "version"); if (version->valueint != 1) return; GET_PROP(simulation_time, json_object, "simulation_time"); new_world->simulation_time = (s64)simulation_time->valuedouble; GET_PROP(start_year, json_object, "start_year"); new_world->start_year = (u16)start_year->valueint; GET_PROP(money, json_object, "money"); new_world->money = (float)money->valuedouble; GET_PROP(next_id, json_object, "next_id"); new_world->next_id = (u32)next_id->valueint; GET_PROP(simulation_speed, json_object, "simulation_speed"); new_world->simulation_speed = (u8)simulation_speed->valueint; GET_PROP(days_since_last_random_event, json_object, "days_since_last_random_event"); new_world->days_since_last_random_event = (u16)days_since_last_random_event->valueint; GET_PROP(bank_info, json_object, "bank_info"); GET_PROP(loan1, bank_info, "loan1"); GET_PROP(loan2, bank_info, "loan2"); GET_PROP(loan3, bank_info, "loan3"); { // Loan 1 GET_PROP(amount, loan1, "amount"); new_world->bank_info.loan1.amount = (s32)amount->valueint; GET_PROP(interest, loan1, "interest"); new_world->bank_info.loan1.interest = (float)interest->valuedouble; GET_PROP(days_left, loan1, "days_left"); new_world->bank_info.loan1.days_left = (s32)days_left->valueint; GET_PROP(monthly_payment, loan1, "monthly_payment"); new_world->bank_info.loan1.monthly_payment = (float)monthly_payment->valuedouble; GET_PROP(is_active, loan1, "is_active"); new_world->bank_info.loan1.is_active = (bool)is_active->valueint; } { // Loan 2 GET_PROP(amount, loan2, "amount"); new_world->bank_info.loan2.amount = (s32)amount->valueint; GET_PROP(interest, loan2, "interest"); new_world->bank_info.loan2.interest = (float)interest->valuedouble; GET_PROP(days_left, loan2, "days_left"); new_world->bank_info.loan2.days_left = (s32)days_left->valueint; GET_PROP(monthly_payment, loan2, "monthly_payment"); new_world->bank_info.loan2.monthly_payment = (float)monthly_payment->valuedouble; GET_PROP(is_active, loan2, "is_active"); new_world->bank_info.loan2.is_active = (bool)is_active->valueint; } { // Loan 3 GET_PROP(amount, loan3, "amount"); new_world->bank_info.loan3.amount = (s32)amount->valueint; GET_PROP(interest, loan3, "interest"); new_world->bank_info.loan3.interest = (float)interest->valuedouble; GET_PROP(days_left, loan3, "days_left"); new_world->bank_info.loan3.days_left = (s32)days_left->valueint; GET_PROP(monthly_payment, loan3, "monthly_payment"); new_world->bank_info.loan3.monthly_payment = (float)monthly_payment->valuedouble; GET_PROP(is_active, loan3, "is_active"); new_world->bank_info.loan3.is_active = (bool)is_active->valueint; } GET_PROP(investments, json_object, "investments"); { GET_PROP(marketing, investments, "marketing"); new_world->investments.marketing = (u32)marketing->valueint; GET_PROP(human_resources, investments, "human_resources"); new_world->investments.human_resources = (u32)human_resources->valueint; GET_PROP(training, investments, "training"); new_world->investments.training = (u32)training->valueint; GET_PROP(legal, investments, "legal"); new_world->investments.legal = (u32)legal->valueint; } GET_PROP(log, json_object, "log"); { GET_PROP(write_cursor, log, "write_cursor"); new_world->log.write_cursor = (u16)write_cursor->valueint; GET_PROP(has_unread_messages, log, "has_unread_messages"); new_world->log.has_unread_messages = (bool)has_unread_messages->valueint; GET_PROP(events, log, "events"); cJSON* event_iter; cJSON_ArrayForEach(event_iter, events) { GET_PROP(type, event_iter, "type"); GET_PROP(message, event_iter, "message"); event new_event; new_event.data = 0; new_event.type = (event_type)type->valueint; memset(&new_event.job_time, 0, sizeof(scheduled_job_time)); string_copyn(new_event.message, message->valuestring, MAX_EVENT_MESSAGE_LENGTH); array_push(&new_world->log.events, &new_event); } } GET_PROP(locations, json_object, "locations"); cJSON* location_iter; cJSON_ArrayForEach(location_iter, locations) { GET_PROP(name, location_iter, "name"); world_location* existing_loc = world_get_location_by_name(new_world, name->valuestring); if (existing_loc == 0) { printf("LOAD ERROR: location '%s' does not exist.\n", name->valuestring); continue; } GET_PROP(is_owned, location_iter, "is_owned"); existing_loc->is_owned = (bool)is_owned->valueint; GET_PROP(purchase_year, location_iter, "purchase_year"); existing_loc->purchase_year = (u16)purchase_year->valueint; GET_PROP(score, location_iter, "score"); existing_loc->score = (float)score->valuedouble; GET_PROP(employees, location_iter, "employees"); cJSON* employee_iter; cJSON_ArrayForEach(employee_iter, employees) { employee* employee1 = mem_alloc(sizeof(employee)); GET_PROP(id, employee_iter, "id"); employee1->id = (u32)id->valuedouble; GET_PROP(name, employee_iter, "name"); string_copyn(employee1->name, name->valuestring, MAX_EMPLOYEE_NAME_LENGTH); GET_PROP(age, employee_iter, "age"); employee1->age = (u8)age->valueint; GET_PROP(experience, employee_iter, "experience"); employee1->experience = (float)experience->valuedouble; GET_PROP(salary, employee_iter, "salary"); employee1->salary = (float)salary->valuedouble; GET_PROP(happiness, employee_iter, "happiness"); employee1->happiness = (float)happiness->valuedouble; GET_PROP(days_below_happiness_treshold, employee_iter, "days_below_happiness_treshold"); employee1->days_below_happiness_treshold = (s16)days_below_happiness_treshold->valueint; GET_PROP(current_location_id, employee_iter, "current_location_id"); employee1->current_location_id = (u32)current_location_id->valuedouble; GET_PROP(original_location_id, employee_iter, "original_location_id"); employee1->original_location_id = (u32)original_location_id->valuedouble; GET_PROP(active_job_id, employee_iter, "active_job_id"); employee1->active_job_id = (u32)active_job_id->valueint; employee1->assigned_truck = 0; // Is set after loading trucks. GET_PROP(portrait_hair_type, employee_iter, "portrait_hair_type"); employee1->portrait_hair_type = (u8)portrait_hair_type->valueint; GET_PROP(hair_color, employee_iter, "hair_color"); GET_PROP(face_color, employee_iter, "face_color"); GET_PROP(body_color, employee_iter, "body_color"); { // hair_color GET_PROP(r, hair_color, "r"); GET_PROP(g, hair_color, "g"); GET_PROP(b, hair_color, "b"); GET_PROP(a, hair_color, "a"); employee1->hair_color = rgba(r->valueint, g->valueint, b->valueint, a->valueint); } { // face_color GET_PROP(r, face_color, "r"); GET_PROP(g, face_color, "g"); GET_PROP(b, face_color, "b"); GET_PROP(a, face_color, "a"); employee1->face_color = rgba(r->valueint, g->valueint, b->valueint, a->valueint); } { // body_color GET_PROP(r, body_color, "r"); GET_PROP(g, body_color, "g"); GET_PROP(b, body_color, "b"); GET_PROP(a, body_color, "a"); employee1->body_color = rgba(r->valueint, g->valueint, b->valueint, a->valueint); } array_push(&existing_loc->employees, &employee1); } GET_PROP(job_offers, location_iter, "job_offers"); cJSON* joboffer_iter; cJSON_ArrayForEach(joboffer_iter, job_offers) { job_offer new_offer; new_offer.connections = array_create(sizeof(world_location*)); GET_PROP(id, joboffer_iter, "id"); new_offer.id = (u32)id->valueint; GET_PROP(expire_date, joboffer_iter, "expire_date"); new_offer.expire_date = (s64)expire_date->valuedouble; GET_PROP(company_id, joboffer_iter, "company_id"); new_offer.company = world_get_company_by_name(new_world, company_id->valuestring); GET_PROP(prod, joboffer_iter, "product"); new_offer.product = world_get_product_by_name(new_world, prod->valuestring); GET_PROP(shipday_count, joboffer_iter, "shipday_count"); new_offer.shipday_count = (s32)shipday_count->valueint; GET_PROP(reward, joboffer_iter, "reward"); new_offer.reward = (u32)reward->valueint; GET_PROP(total_distance, joboffer_iter, "total_distance"); new_offer.total_distance = (float)total_distance->valuedouble; GET_PROP(boat_distance, joboffer_iter, "boat_distance"); new_offer.boat_distance = (float)boat_distance->valuedouble; GET_PROP(duration_sec_min, joboffer_iter, "duration_sec_min"); new_offer.duration_sec_min = (time_t)duration_sec_min->valuedouble; GET_PROP(duration_sec_max, joboffer_iter, "duration_sec_max"); new_offer.duration_sec_max = (time_t)duration_sec_max->valuedouble; GET_PROP(duration_sec_min_excluding_boat, joboffer_iter, "duration_sec_min_excluding_boat"); new_offer.duration_sec_min_excluding_boat = (time_t)duration_sec_min_excluding_boat->valuedouble; GET_PROP(shipdays, joboffer_iter, "shipdays"); cJSON* shipday_iter; int shipday_index = 0; cJSON_ArrayForEach(shipday_iter, shipdays) { new_offer.shipdays[shipday_index] = (weekday)shipday_iter->valueint; shipday_index++; } GET_PROP(connections, joboffer_iter, "connections"); cJSON* connection_iter; cJSON_ArrayForEach(connection_iter, connections) { u64 id = (u32)connection_iter->valuedouble; world_location* loc = get_world_location_by_id(new_world, id); if (loc) array_push(&new_offer.connections, &loc); } array_push(&existing_loc->job_offers, &new_offer); } GET_PROP(resumes, location_iter, "resumes"); cJSON* resume_iter; cJSON_ArrayForEach(resume_iter, resumes) { resume new_resume; new_resume.animation = animation_create(RESUME_FADEOUT_MS); GET_PROP(expire_date, resume_iter, "expire_date"); new_resume.expire_date = (s64)expire_date->valuedouble; new_resume.hired = false; // Employee is not hired yet so just make up a new employee. new_resume.employee = create_employee(new_world, existing_loc); array_push(&existing_loc->resumes, &new_resume); } GET_PROP(trucks, location_iter, "trucks"); cJSON* truck_iter; cJSON_ArrayForEach(truck_iter, trucks) { GET_PROP(type, truck_iter, "type"); s32 truck_type = (s32)type->valuedouble; truck* new_truck = world_get_truck_by_type(new_world, truck_type); GET_PROP(id, truck_iter, "id"); new_truck->id = (s32)id->valuedouble; GET_PROP(employee_id, truck_iter, "employee_id"); u32 emp_id = (u32)employee_id->valuedouble; employee* emp = 0; if (emp_id == 0) { new_truck->assigned_employee = 0; } else { emp = get_employee_by_id(existing_loc, emp_id); new_truck->assigned_employee = emp; } array_push(&existing_loc->trucks, new_truck); if (emp) { emp->assigned_truck = array_at(&existing_loc->trucks, existing_loc->trucks.length-1); } } GET_PROP(schedule, location_iter, "schedule"); cJSON* job_iter; cJSON_ArrayForEach(job_iter, schedule) { scheduled_job existing_job; existing_job.location = existing_loc; GET_PROP(trust, job_iter, "trust"); existing_job.trust = (float)trust->valuedouble; GET_PROP(offer, job_iter, "offer"); { // Load job offer of existing job. job_offer new_offer; new_offer.connections = array_create(sizeof(world_location*)); GET_PROP(id, offer, "id"); new_offer.id = (u32)id->valueint; GET_PROP(expire_date, offer, "expire_date"); new_offer.expire_date = (s64)expire_date->valuedouble; GET_PROP(company_id, offer, "company_id"); new_offer.company = world_get_company_by_name(new_world, company_id->valuestring); GET_PROP(prod, offer, "product"); new_offer.product = world_get_product_by_name(new_world, prod->valuestring); GET_PROP(shipday_count, offer, "shipday_count"); new_offer.shipday_count = (s32)shipday_count->valueint; GET_PROP(reward, offer, "reward"); new_offer.reward = (u32)reward->valueint; GET_PROP(total_distance, offer, "total_distance"); new_offer.total_distance = (float)total_distance->valuedouble; GET_PROP(boat_distance, offer, "boat_distance"); new_offer.boat_distance = (float)boat_distance->valuedouble; GET_PROP(duration_sec_min, offer, "duration_sec_min"); new_offer.duration_sec_min = (time_t)duration_sec_min->valuedouble; GET_PROP(duration_sec_max, offer, "duration_sec_max"); new_offer.duration_sec_max = (time_t)duration_sec_max->valuedouble; GET_PROP(duration_sec_min_excluding_boat, offer, "duration_sec_min_excluding_boat"); new_offer.duration_sec_min_excluding_boat = (time_t)duration_sec_min_excluding_boat->valuedouble; GET_PROP(shipdays, offer, "shipdays"); cJSON* shipday_iter; int shipday_index = 0; cJSON_ArrayForEach(shipday_iter, shipdays) { new_offer.shipdays[shipday_index] = (weekday)shipday_iter->valueint; shipday_index++; } GET_PROP(connections, offer, "connections"); cJSON* connection_iter; cJSON_ArrayForEach(connection_iter, connections) { u64 id = (u32)connection_iter->valuedouble; world_location* loc = get_world_location_by_id(new_world, id); if (loc) array_push(&new_offer.connections, &loc); } existing_job.offer = new_offer; } for (s32 i = 0; i < MAX_SHIPDAYS; i++) { existing_job.timeslots[i] = (scheduled_job_time){-1, -1, 0, 0}; } GET_PROP(timeslots, job_iter, "timeslots"); cJSON* timeslot_iter; int timeslot_index = 0; cJSON_ArrayForEach(timeslot_iter, timeslots) { scheduled_job_time job_time; GET_PROP(day, timeslot_iter, "day"); job_time.day = (s16)day->valueint; GET_PROP(timeslot, timeslot_iter, "timeslot"); job_time.timeslot = (s16)timeslot->valueint; GET_PROP(stay_at_destination, timeslot_iter, "stay_at_destination"); job_time.stay_at_destination = (bool)stay_at_destination->valueint; GET_PROP(employee_id, timeslot_iter, "assignee_id"); u32 emp_id = (u32)employee_id->valuedouble; job_time.assignee = get_employee_by_id_global(new_world, emp_id); existing_job.timeslots[timeslot_index] = job_time; timeslot_index++; } array_push(&existing_loc->schedule.jobs, &existing_job); } GET_PROP(insights, location_iter, "insights"); cJSON* insight_iter; cJSON_ArrayForEach(insight_iter, insights) { money_data_collection year_data; memset(&year_data, 0, sizeof(money_data_collection)); for (int i = 0; i < MONTHS_IN_YEAR; i++) { year_data.months[i].total_income = NAN; } GET_PROP(months, insight_iter, "months"); cJSON* month_iter; cJSON_ArrayForEach(month_iter, months) { money_data month; GET_PROP(total_income, month_iter, "total_income"); month.total_income = (float)total_income->valuedouble; GET_PROP(total_expenses, month_iter, "total_expenses"); month.total_expenses = (float)total_expenses->valuedouble; GET_PROP(total_profit, month_iter, "total_profit"); month.total_profit = (float)total_profit->valuedouble; GET_PROP(income_from_trips, month_iter, "income_from_trips"); month.income_from_trips = (float)income_from_trips->valuedouble; GET_PROP(expenses_from_trucks, month_iter, "expenses_from_trucks"); month.expenses_from_trucks = (float)expenses_from_trucks->valuedouble; GET_PROP(expenses_from_utility, month_iter, "expenses_from_utility"); month.expenses_from_utility = (float)expenses_from_utility->valuedouble; GET_PROP(expenses_from_loans, month_iter, "expenses_from_loans"); month.expenses_from_loans = (float)expenses_from_loans->valuedouble; GET_PROP(expenses_from_healthcare, month_iter, "expenses_from_healthcare"); month.expenses_from_healthcare = (float)expenses_from_healthcare->valuedouble; GET_PROP(expenses_from_repairs, month_iter, "expenses_from_repairs"); month.expenses_from_repairs = (float)expenses_from_repairs->valuedouble; GET_PROP(expenses_from_fuel, month_iter, "expenses_from_fuel"); month.expenses_from_fuel = (float)expenses_from_fuel->valuedouble; GET_PROP(expenses_from_employees, month_iter, "expenses_from_employees"); month.expenses_from_employees = (float)expenses_from_employees->valuedouble; GET_PROP(index, month_iter, "index"); year_data.months[index->valueint] = month; } array_push(&existing_loc->insights, &year_data); } } GET_PROP(insights, json_object, "insights"); cJSON* insight_iter; cJSON_ArrayForEach(insight_iter, insights) { money_data_collection year_data; memset(&year_data, 0, sizeof(money_data_collection)); for (int i = 0; i < MONTHS_IN_YEAR; i++) { year_data.months[i].total_income = NAN; } GET_PROP(months, insight_iter, "months"); cJSON* month_iter; cJSON_ArrayForEach(month_iter, months) { money_data month; GET_PROP(total_income, month_iter, "total_income"); month.total_income = (float)total_income->valuedouble; GET_PROP(total_expenses, month_iter, "total_expenses"); month.total_expenses = (float)total_expenses->valuedouble; GET_PROP(total_profit, month_iter, "total_profit"); month.total_profit = (float)total_profit->valuedouble; GET_PROP(income_from_trips, month_iter, "income_from_trips"); month.income_from_trips = (float)income_from_trips->valuedouble; GET_PROP(expenses_from_trucks, month_iter, "expenses_from_trucks"); month.expenses_from_trucks = (float)expenses_from_trucks->valuedouble; GET_PROP(expenses_from_utility, month_iter, "expenses_from_utility"); month.expenses_from_utility = (float)expenses_from_utility->valuedouble; GET_PROP(expenses_from_loans, month_iter, "expenses_from_loans"); month.expenses_from_loans = (float)expenses_from_loans->valuedouble; GET_PROP(expenses_from_healthcare, month_iter, "expenses_from_healthcare"); month.expenses_from_healthcare = (float)expenses_from_healthcare->valuedouble; GET_PROP(expenses_from_repairs, month_iter, "expenses_from_repairs"); month.expenses_from_repairs = (float)expenses_from_repairs->valuedouble; GET_PROP(expenses_from_fuel, month_iter, "expenses_from_fuel"); month.expenses_from_fuel = (float)expenses_from_fuel->valuedouble; GET_PROP(expenses_from_employees, month_iter, "expenses_from_employees"); month.expenses_from_employees = (float)expenses_from_employees->valuedouble; GET_PROP(index, month_iter, "index"); year_data.months[index->valueint] = month; } array_push(&new_world->insights, &year_data); } GET_PROP(active_jobs, json_object, "active_jobs"); cJSON* active_job_iter; cJSON_ArrayForEach(active_job_iter, active_jobs) { active_job aj; GET_PROP(day, active_job_iter, "day"); aj.day = (s16)day->valueint; GET_PROP(timeslot, active_job_iter, "timeslot"); aj.timeslot = (s16)timeslot->valueint; GET_PROP(stay_at_destination, active_job_iter, "stay_at_destination"); aj.stay_at_destination = (bool)stay_at_destination->valueint; GET_PROP(duration_sec, active_job_iter, "duration_sec"); aj.duration_sec = (time_t)duration_sec->valuedouble; GET_PROP(left_at, active_job_iter, "left_at"); aj.left_at = (time_t)left_at->valuedouble; GET_PROP(done_at, active_job_iter, "done_at"); aj.done_at = (time_t)done_at->valuedouble; GET_PROP(reversed, active_job_iter, "reversed"); aj.reversed = (bool)reversed->valueint; GET_PROP(assignee_id, active_job_iter, "assignee_id"); u32 emp_id = (u32)assignee_id->valuedouble; aj.assignee = *get_employee_by_id_global(new_world, emp_id); GET_PROP(truck_id, active_job_iter, "truck_id"); u32 tr_id = (u32)truck_id->valuedouble; aj.assigned_truck = *get_truck_by_id(new_world, tr_id); GET_PROP(offer, active_job_iter, "offer"); { // Load job offer of existing job. job_offer new_offer; new_offer.connections = array_create(sizeof(world_location*)); GET_PROP(id, offer, "id"); new_offer.id = (u32)id->valueint; GET_PROP(expire_date, offer, "expire_date"); new_offer.expire_date = (s64)expire_date->valuedouble; GET_PROP(company_id, offer, "company_id"); new_offer.company = world_get_company_by_name(new_world, company_id->valuestring); GET_PROP(prod, offer, "product"); new_offer.product = world_get_product_by_name(new_world, prod->valuestring); GET_PROP(shipday_count, offer, "shipday_count"); new_offer.shipday_count = (s32)shipday_count->valueint; GET_PROP(reward, offer, "reward"); new_offer.reward = (u32)reward->valueint; GET_PROP(total_distance, offer, "total_distance"); new_offer.total_distance = (float)total_distance->valuedouble; GET_PROP(boat_distance, offer, "boat_distance"); new_offer.boat_distance = (float)boat_distance->valuedouble; GET_PROP(duration_sec_min, offer, "duration_sec_min"); new_offer.duration_sec_min = (time_t)duration_sec_min->valuedouble; GET_PROP(duration_sec_max, offer, "duration_sec_max"); new_offer.duration_sec_max = (time_t)duration_sec_max->valuedouble; GET_PROP(duration_sec_min_excluding_boat, offer, "duration_sec_min_excluding_boat"); new_offer.duration_sec_min_excluding_boat = (time_t)duration_sec_min_excluding_boat->valuedouble; GET_PROP(shipdays, offer, "shipdays"); cJSON* shipday_iter; int shipday_index = 0; cJSON_ArrayForEach(shipday_iter, shipdays) { new_offer.shipdays[shipday_index] = (weekday)shipday_iter->valueint; shipday_index++; } GET_PROP(connections, offer, "connections"); cJSON* connection_iter; cJSON_ArrayForEach(connection_iter, connections) { u64 id = (u32)connection_iter->valuedouble; world_location* loc = get_world_location_by_id(new_world, id); if (loc) array_push(&new_offer.connections, &loc); } aj.offer = new_offer; array_push(&new_world->active_jobs, &aj); } } world_map_set_active_world(new_world); world_set_weekly_hours_worked(new_world); //enable_insights_for_current_month(new_world); world_update_location_scores(new_world); game_set_active_scene(GAME_STATE_WORLD_MAP); } 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, "version", cJSON_CreateNumber(1)); 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)); cJSON_AddItemToObject(world, "next_id", cJSON_CreateNumber(_active_world->next_id)); cJSON_AddItemToObject(world, "simulation_speed", cJSON_CreateNumber(_active_world->simulation_speed)); cJSON_AddItemToObject(world, "days_since_last_random_event", cJSON_CreateNumber(_active_world->days_since_last_random_event)); cJSON *bank = cJSON_CreateObject(); { cJSON *loan1 = cJSON_CreateObject(); cJSON_AddItemToObject(loan1, "amount", cJSON_CreateNumber(_active_world->bank_info.loan1.amount)); cJSON_AddItemToObject(loan1, "interest", cJSON_CreateNumber(_active_world->bank_info.loan1.interest)); cJSON_AddItemToObject(loan1, "days_left", cJSON_CreateNumber(_active_world->bank_info.loan1.days_left)); cJSON_AddItemToObject(loan1, "monthly_payment", cJSON_CreateNumber(_active_world->bank_info.loan1.monthly_payment)); cJSON_AddItemToObject(loan1, "is_active", cJSON_CreateNumber(_active_world->bank_info.loan1.is_active)); cJSON_AddItemToObject(bank, "loan1", loan1); cJSON *loan2 = cJSON_CreateObject(); cJSON_AddItemToObject(loan2, "amount", cJSON_CreateNumber(_active_world->bank_info.loan2.amount)); cJSON_AddItemToObject(loan2, "interest", cJSON_CreateNumber(_active_world->bank_info.loan2.interest)); cJSON_AddItemToObject(loan2, "days_left", cJSON_CreateNumber(_active_world->bank_info.loan2.days_left)); cJSON_AddItemToObject(loan2, "monthly_payment", cJSON_CreateNumber(_active_world->bank_info.loan2.monthly_payment)); cJSON_AddItemToObject(loan2, "is_active", cJSON_CreateNumber(_active_world->bank_info.loan2.is_active)); cJSON_AddItemToObject(bank, "loan2", loan2); cJSON *loan3 = cJSON_CreateObject(); cJSON_AddItemToObject(loan3, "amount", cJSON_CreateNumber(_active_world->bank_info.loan3.amount)); cJSON_AddItemToObject(loan3, "interest", cJSON_CreateNumber(_active_world->bank_info.loan3.interest)); cJSON_AddItemToObject(loan3, "days_left", cJSON_CreateNumber(_active_world->bank_info.loan3.days_left)); cJSON_AddItemToObject(loan3, "monthly_payment", cJSON_CreateNumber(_active_world->bank_info.loan3.monthly_payment)); cJSON_AddItemToObject(loan3, "is_active", cJSON_CreateNumber(_active_world->bank_info.loan3.is_active)); cJSON_AddItemToObject(bank, "loan3", loan3); cJSON_AddItemToObject(world, "bank_info", bank); } cJSON *investments = cJSON_CreateObject(); { cJSON_AddItemToObject(investments, "marketing", cJSON_CreateNumber(_active_world->investments.marketing)); cJSON_AddItemToObject(investments, "human_resources", cJSON_CreateNumber(_active_world->investments.human_resources)); cJSON_AddItemToObject(investments, "training", cJSON_CreateNumber(_active_world->investments.training)); cJSON_AddItemToObject(investments, "legal", cJSON_CreateNumber(_active_world->investments.legal)); cJSON_AddItemToObject(world, "investments", investments); } cJSON *log = cJSON_CreateObject(); { cJSON_AddItemToObject(log, "write_cursor", cJSON_CreateNumber(_active_world->log.write_cursor)); cJSON_AddItemToObject(log, "has_unread_messages", cJSON_CreateNumber(_active_world->log.has_unread_messages)); cJSON* entries = cJSON_CreateArray(); for (int i = 0; i < _active_world->log.events.length; i++) { event* e = array_at(&_active_world->log.events, i); cJSON *event = cJSON_CreateObject(); cJSON_AddItemToObject(event, "type", cJSON_CreateNumber(e->type)); cJSON_AddItemToObject(event, "message", cJSON_CreateString(e->message)); cJSON_AddItemToArray(entries, event); } cJSON_AddItemToObject(log, "events", entries); cJSON_AddItemToObject(world, "log", log); } cJSON *locations = cJSON_CreateArray(); { for (s32 i = 0; i < _active_world->locations.length; i++) { world_location* location = array_at(&_active_world->locations, i); if (!location->is_owned) continue; cJSON *loc = cJSON_CreateObject(); cJSON_AddItemToObject(loc, "name", cJSON_CreateString(location->name)); cJSON_AddItemToObject(loc, "is_owned", cJSON_CreateNumber(location->is_owned)); cJSON_AddItemToObject(loc, "purchase_year", cJSON_CreateNumber(location->purchase_year)); cJSON_AddItemToObject(loc, "score", cJSON_CreateNumber(location->score)); cJSON *employees = cJSON_CreateArray(); { for (s32 r = 0; r < location->employees.length; r++) { employee* emp = *(employee**)array_at(&location->employees, r); cJSON *employee = cJSON_CreateObject(); cJSON_AddItemToObject(employee, "id", cJSON_CreateNumber(emp->id)); cJSON_AddItemToObject(employee, "name", cJSON_CreateString(emp->name)); cJSON_AddItemToObject(employee, "age", cJSON_CreateNumber(emp->age)); cJSON_AddItemToObject(employee, "experience", cJSON_CreateNumber(emp->experience)); cJSON_AddItemToObject(employee, "salary", cJSON_CreateNumber(emp->salary)); cJSON_AddItemToObject(employee, "happiness", cJSON_CreateNumber(emp->happiness)); cJSON_AddItemToObject(employee, "days_below_happiness_treshold", cJSON_CreateNumber(emp->days_below_happiness_treshold)); cJSON_AddItemToObject(employee, "current_location_id", cJSON_CreateNumber(emp->current_location_id)); cJSON_AddItemToObject(employee, "original_location_id", cJSON_CreateNumber(emp->original_location_id)); cJSON_AddItemToObject(employee, "active_job_id", cJSON_CreateNumber(emp->active_job_id)); cJSON_AddItemToObject(employee, "truck_id", cJSON_CreateNumber(emp->assigned_truck ? emp->assigned_truck->id :-1)); cJSON_AddItemToObject(employee, "portrait_hair_type", cJSON_CreateNumber(emp->portrait_hair_type)); cJSON *c1 = cJSON_CreateObject(); cJSON_AddItemToObject(c1, "r", cJSON_CreateNumber(emp->hair_color.r)); cJSON_AddItemToObject(c1, "g", cJSON_CreateNumber(emp->hair_color.g)); cJSON_AddItemToObject(c1, "b", cJSON_CreateNumber(emp->hair_color.b)); cJSON_AddItemToObject(c1, "a", cJSON_CreateNumber(emp->hair_color.a)); cJSON_AddItemToObject(employee, "hair_color", c1); cJSON *c2 = cJSON_CreateObject(); cJSON_AddItemToObject(c2, "r", cJSON_CreateNumber(emp->face_color.r)); cJSON_AddItemToObject(c2, "g", cJSON_CreateNumber(emp->face_color.g)); cJSON_AddItemToObject(c2, "b", cJSON_CreateNumber(emp->face_color.b)); cJSON_AddItemToObject(c2, "a", cJSON_CreateNumber(emp->face_color.a)); cJSON_AddItemToObject(employee, "face_color", c2); cJSON *c3 = cJSON_CreateObject(); cJSON_AddItemToObject(c3, "r", cJSON_CreateNumber(emp->body_color.r)); cJSON_AddItemToObject(c3, "g", cJSON_CreateNumber(emp->body_color.g)); cJSON_AddItemToObject(c3, "b", cJSON_CreateNumber(emp->body_color.b)); cJSON_AddItemToObject(c3, "a", cJSON_CreateNumber(emp->body_color.a)); cJSON_AddItemToObject(employee, "body_color", c3); cJSON_AddItemToArray(employees, employee); } cJSON_AddItemToObject(loc, "employees", employees); } cJSON *job_offers = cJSON_CreateArray(); { for (s32 x = 0; x < location->job_offers.length; x++) { job_offer* o = array_at(&location->job_offers, x); cJSON *offer = cJSON_CreateObject(); cJSON_AddItemToObject(offer, "id", cJSON_CreateNumber(o->id)); cJSON_AddItemToObject(offer, "reward", cJSON_CreateNumber(o->reward)); cJSON_AddItemToObject(offer, "expire_date", cJSON_CreateNumber(o->expire_date)); cJSON_AddItemToObject(offer, "company_id", cJSON_CreateString(o->company->name)); cJSON_AddItemToObject(offer, "product", cJSON_CreateString(o->product->name)); cJSON_AddItemToObject(offer, "shipday_count", cJSON_CreateNumber(o->shipday_count)); cJSON *shipdays = cJSON_CreateArray(); for (s32 s = 0; s < MAX_SHIPDAYS; s++) { cJSON_AddItemToArray(shipdays, cJSON_CreateNumber(o->shipdays[s])); } cJSON_AddItemToObject(offer, "shipdays", shipdays); cJSON_AddItemToObject(offer, "total_distance", cJSON_CreateNumber(o->total_distance)); cJSON_AddItemToObject(offer, "boat_distance", cJSON_CreateNumber(o->boat_distance)); cJSON_AddItemToObject(offer, "duration_sec_min", cJSON_CreateNumber(o->duration_sec_min)); cJSON_AddItemToObject(offer, "duration_sec_max", cJSON_CreateNumber(o->duration_sec_max)); cJSON_AddItemToObject(offer, "duration_sec_min_excluding_boat", cJSON_CreateNumber(o->duration_sec_min_excluding_boat)); cJSON *connections = cJSON_CreateArray(); for (s32 s = 0; s < o->connections.length; s++) { world_location* conn_loc = *(world_location**)array_at(&o->connections, s); cJSON_AddItemToArray(connections, cJSON_CreateNumber(conn_loc->id)); } cJSON_AddItemToObject(offer, "connections", connections); cJSON_AddItemToArray(job_offers, offer); } cJSON_AddItemToObject(loc, "job_offers", job_offers); } cJSON *resumes = cJSON_CreateArray(); { for (s32 r = 0; r < location->resumes.length; r++) { resume* res = array_at(&location->resumes, r); cJSON *resume = cJSON_CreateObject(); cJSON_AddItemToObject(resume, "employee_id", cJSON_CreateNumber(res->employee->id)); cJSON_AddItemToObject(resume, "expire_date", cJSON_CreateNumber(res->expire_date)); cJSON_AddItemToObject(resume, "hired", cJSON_CreateNumber(res->hired)); cJSON_AddItemToArray(resumes, resume); } cJSON_AddItemToObject(loc, "resumes", resumes); } cJSON *trucks = cJSON_CreateArray(); { for (s32 i = 0; i < location->trucks.length; i++) { truck* emp = array_at(&location->trucks, i); cJSON *truck = cJSON_CreateObject(); cJSON_AddItemToObject(truck, "id", cJSON_CreateNumber(emp->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); } cJSON_AddItemToObject(loc, "trucks", trucks); } cJSON *schedule = cJSON_CreateArray(); { for (s32 r = 0; r < location->schedule.jobs.length; r++) { 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, "reward", cJSON_CreateNumber(scheduled_job->offer.reward)); 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); } // 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, "reward", cJSON_CreateNumber(aj->offer.reward)); 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); 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) { s32 screen_center_x = area.x + (area.w/2); s32 screen_center_y = area.y + (area.h/2); float vertical_pad = 20 * scale; float horizontal_pad = vertical_pad; float spacing = 5 * scale; s32 panel_h = 280 * scale; s32 panel_item_size = (panel_h - (vertical_pad*2) - (spacing*1)) / 2; s32 panel_w = (panel_item_size * 3) + (horizontal_pad*2) + (spacing*2); s32 panel_x = screen_center_x - (panel_w/2); s32 panel_y = screen_center_y - (panel_h/2); panel_render(scale, panel_x, panel_y, panel_w, panel_h); // top row for (s32 i = 0; i < 3; i++) { 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); } } // bottom row for (s32 i = 0; i < 3; i++) { if (push_save_state_button(scale, save_entries[3 + 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(3 + i); else load_save_file(3 + i); } } // back button { s32 back_h = img_back->height * scale/2; s32 back_w = img_back->width * scale/2; s32 back_x = panel_x + (panel_item_size/3); s32 back_y = panel_y + panel_h - (back_h/2) - 1; if (push_back_button(scale, back_x, back_y, back_w, back_h)) { if (is_selecting_save_location) { game_set_active_scene(GAME_STATE_WORLD_MAP); } else { game_set_active_scene(GAME_STATE_MENU); } } } } void save_state_select_scene_render(platform_window* window) { menu_draw_background(window); save_state_draw_options(window); } void save_state_select_scene_update(platform_window* window) { if (keyboard_is_key_pressed(KEY_ESCAPE)) { if (is_selecting_save_location) { game_set_active_scene(GAME_STATE_WORLD_MAP); } else { game_set_active_scene(GAME_STATE_MENU); } } } void save_state_select_scene_destroy() { }