From 6f7374c2fa58c8692b51018864b802e6b876d305 Mon Sep 17 00:00:00 2001 From: Aldrik Ramaekers Date: Sat, 23 Nov 2024 21:52:24 +0100 Subject: A new start --- src/scenes/error_scene.c | 74 ++ src/scenes/loading_scene.c | 85 ++ src/scenes/loading_world_scene.c | 116 +++ src/scenes/menu_scene.c | 93 ++ src/scenes/place_detail.c | 1771 ++++++++++++++++++++++++++++++++++++++ src/scenes/save_state_select.c | 105 +++ src/scenes/settings_scene.c | 152 ++++ src/scenes/world_map.c | 1094 +++++++++++++++++++++++ 8 files changed, 3490 insertions(+) create mode 100644 src/scenes/error_scene.c create mode 100644 src/scenes/loading_scene.c create mode 100644 src/scenes/loading_world_scene.c create mode 100644 src/scenes/menu_scene.c create mode 100644 src/scenes/place_detail.c create mode 100644 src/scenes/save_state_select.c create mode 100644 src/scenes/settings_scene.c create mode 100644 src/scenes/world_map.c (limited to 'src/scenes') diff --git a/src/scenes/error_scene.c b/src/scenes/error_scene.c new file mode 100644 index 0000000..8d4446a --- /dev/null +++ b/src/scenes/error_scene.c @@ -0,0 +1,74 @@ + +void error_scene_init() +{ + +} + +static void error_scene_draw_info(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); + + // info text + { + { + char* title = "ERROR"; + font* font_title = FONT_REGULAR(SIZE_RD(area.w, 32)); + s32 text_w = renderer->calculate_text_width(font_title, title); + s32 text_x = screen_center_x - (text_w/2); + s32 text_y = panel_y + (vertical_pad*2); + renderer->render_text(font_title, text_x+2, text_y+2, title, COLOR_TEXT_SHADOW); + renderer->render_text(font_title, text_x, text_y, title, COLOR_TEXT); + } + { + char* text = "An error occured, please verify\nyour game files."; + font* font_info = FONT_REGULAR(SIZE_RD(area.w, 28)); + s32 text_w = panel_w - (horizontal_pad*4); + s32 text_x = screen_center_x - (text_w/2); + s32 text_y = screen_center_y - (font_info->px_h/2); + renderer->render_text_cutoff(font_info, text_x+2, text_y+2, text, COLOR_TEXT_SHADOW, 9999); + renderer->render_text_cutoff(font_info, text_x, text_y, text, COLOR_TEXT, 9999); + } + + } + + // 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)) { + game_set_active_scene(GAME_STATE_MENU); + } + } +} + +void error_scene_render(platform_window* window) +{ + menu_draw_background(window); + error_scene_draw_info(window); +} + +void error_scene_update(platform_window* window) +{ + +} + +void error_scene_destroy() +{ + +} \ No newline at end of file diff --git a/src/scenes/loading_scene.c b/src/scenes/loading_scene.c new file mode 100644 index 0000000..2240aa8 --- /dev/null +++ b/src/scenes/loading_scene.c @@ -0,0 +1,85 @@ + +#define MAX_CREDITED_NAMES 5 +#define MAX_CREDIT_NAME_LENGTH 30 +#define COMPLETE_CREDIT_LENGTH (MAX_CREDIT_NAME_LENGTH*MAX_CREDITED_NAMES)+20 +char complete_credit_text[COMPLETE_CREDIT_LENGTH]; + +void loading_scene_init() +{ + strcpy(complete_credit_text, "Music by "); + + // Load names to credit. + { + platform_set_active_directory(binary_path); + + array files = array_create(sizeof(found_file)); + array filters = string_split("AUTHOR.txt"); + bool is_cancelled = false; + platform_list_files_block(&files, "data/music/", filters, true, 0, true, &is_cancelled, 0); + log_assert(files.length <= MAX_CREDITED_NAMES, "Not enough space for credited names."); + for (s32 i = 0; i < files.length; i++) + { + found_file *file = array_at(&files, i); + + if (platform_file_exists(file->path)) + { + file_content name = platform_read_file_content(file->path, "rb"); + if (name.file_error) continue; + + string_appendn(complete_credit_text, name.content, COMPLETE_CREDIT_LENGTH); + if (i != files.length-1) { + string_appendn(complete_credit_text, ", ", COMPLETE_CREDIT_LENGTH); + } + platform_destroy_file_content(&name); + } + + mem_free(file->matched_filter); + mem_free(file->path); + } + + array_destroy(&files); + array_destroy(&filters); + } +} + +void loading_scene_render(platform_window* window) +{ + renderer->render_rectangle(area.x, area.y, area.w, area.h, COLOR_WHITE); + + font* font_reg = FONT_REGULAR(SIZE_RD(area.w, 36)); + s32 target_size = area.h/5; + s32 logo_text_pad = 20; + s32 total_height = target_size + logo_text_pad + font_reg->px_h; + + s32 screen_center_x = area.x + (area.w/2); + s32 screen_center_y = area.y + (area.h/2); + s32 logo_x = screen_center_x - (target_size/2); + s32 logo_y = screen_center_y - (total_height/2); + renderer->render_image(img_logo, logo_x, logo_y, target_size, target_size); + + s32 text_y = logo_y + target_size + logo_text_pad; + char* company_name = "Tar Software"; + s32 company_name_width = renderer->calculate_text_width(font_reg, company_name); + s32 text_x = screen_center_x - (company_name_width/2); + + renderer->render_text(font_reg, text_x, text_y, company_name, COLOR_TITLE); + + // Credits + font* font_s = FONT_REGULAR(SIZE_RD(area.w, 20)); + s32 credit_pad = 30*scale; + renderer->render_text(font_s, area.x + credit_pad, area.y+area.h-credit_pad-font_s->px_h, complete_credit_text, COLOR_TITLE); +} + +void loading_scene_update(platform_window* window) +{ + platform_set_cursor(window, CURSOR_LOADING); + + if (global_asset_collection.done_loading_assets) { + game_set_active_scene(GAME_STATE_MENU); + } +} + +void loading_scene_destroy() +{ + +} \ No newline at end of file diff --git a/src/scenes/loading_world_scene.c b/src/scenes/loading_world_scene.c new file mode 100644 index 0000000..3d15909 --- /dev/null +++ b/src/scenes/loading_world_scene.c @@ -0,0 +1,116 @@ + +void loading_world_scene_init() +{ + +} + +u64 load_start_stamp = 0; + +static void* start_loading_world_t(void* arg) +{ + load_start_stamp = platform_get_time(TIME_FULL, TIME_US); // Used for displaying info texts. + #ifdef MODE_DEBUG + // thread_sleep(1000*200); + #endif + + char* path = (char*)arg; + world* world_to_load = 0; + + if (path) { + // Load from file here + } + else { + world_to_load = world_create_new(); + } + + #ifdef MODE_DEBUG + u64 load_end_stamp = platform_get_time(TIME_FULL, TIME_US); + u64 elapsed_ns = load_end_stamp - load_start_stamp; + char info_msg[50]; + sprintf(info_msg, "Loaded world in %.2fms", elapsed_ns/1000.0f); + log_info(info_msg); + #endif + + if (!world_to_load) { + log_info("Failed to load world"); + game_set_active_scene(GAME_STATE_ERROR); + } + else { + world_map_set_active_world(world_to_load); + game_set_active_scene(GAME_STATE_WORLD_MAP); + } + + return 0; +} + +void start_loading_world(char* saved_file_path) +{ + game_set_active_scene(GAME_STATE_LOADING_WORLD); + thread_start(start_loading_world_t, saved_file_path); +} + +static void loading_world_draw_info_texts(font* font, s32 center_x, s32 y) +{ + char* texts[5] = { + "(Building cities)", + "(Connecting roads)", + "(Manufacturing trucks)", + "(Finding truck drivers)", + "(This is taking very long..)", + }; + + u64 load_end_stamp = platform_get_time(TIME_FULL, TIME_US); + u64 elapsed_ns = load_end_stamp - load_start_stamp; + float elapsed_sec = elapsed_ns / 1000.0f; + int text_index = elapsed_sec / 2000.0f; // 2 sec per text. + if (text_index >= 5) text_index = 4; + + s32 text_len = renderer->calculate_text_width(font, texts[text_index]); + renderer->render_text(font, center_x - (text_len/2), y, texts[text_index], COLOR_TEXT); +} + +static void loading_world_draw_animation(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); + + s32 carwheel_size = panel_h/4; + + static float rotation = 0.0f; + rotation -= 0.05f; + gl_render_set_rotation(rotation); + renderer->render_image(img_carwheel, screen_center_x - (carwheel_size/2), screen_center_y - (carwheel_size/2), carwheel_size, carwheel_size); + gl_render_set_rotation(0.0f); + + + font* font_info = FONT_REGULAR(SIZE_RD(area.w, 28)); + loading_world_draw_info_texts(font_info, screen_center_x, panel_y + panel_h - font_info->px_h - vertical_pad*2); +} + +void loading_world_scene_render(platform_window* window) +{ + menu_draw_background(window); + loading_world_draw_animation(window); +} + +void loading_world_scene_update(platform_window* window) +{ + +} + +void loading_world_scene_destroy() +{ + +} \ No newline at end of file diff --git a/src/scenes/menu_scene.c b/src/scenes/menu_scene.c new file mode 100644 index 0000000..73ec22d --- /dev/null +++ b/src/scenes/menu_scene.c @@ -0,0 +1,93 @@ + +void menu_scene_init() +{ + +} + +static void menu_draw_background(platform_window* window) +{ + vec4 area = camera_get_target_rectangle(window); + renderer->render_rectangle(area.x, area.y, area.w, area.h, COLOR_WORLD_MAP_BACKGROUND); + renderer->render_image(img_world_map, area.x, area.y, area.w, area.h); +} + +static void menu_draw_options(platform_window* window) +{ + s32 screen_center_x = area.x + (area.w/2); + s32 screen_center_y = area.y + (area.h/2); + + s32 panel_w = 198 * scale; + s32 panel_h = 193 * scale; + 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); + + s32 button_w = 178 * scale; + s32 button_h = 37 * scale; + s32 vertical_pad = 10 * scale; + s32 pad_x = (panel_w - button_w)/2; + float pad_y = (panel_h - (vertical_pad*2) - button_h*4)/5.0f; + + if (button_render(scale, BUTTON_ENABLED, "New Game", panel_x + pad_x, vertical_pad + panel_y + pad_y*1, button_w, button_h)) + { + start_loading_world(0); // Start new world + } + + if (button_render(scale, BUTTON_ENABLED, "Continue", panel_x + pad_x, vertical_pad + panel_y + pad_y*2 + button_h*1, button_w, button_h)) + { + game_set_active_scene(GAME_STATE_SELECT_SAVE); + } + + if (button_render(scale, BUTTON_ENABLED, "Settings", panel_x + pad_x, vertical_pad + panel_y + pad_y*3 + button_h*2, button_w, button_h)) + { + game_set_active_scene(GAME_STATE_SETTINGS); + } + + if (button_render(scale, BUTTON_ENABLED, "Quit", panel_x + pad_x, vertical_pad + panel_y + pad_y*4 + button_h*3, button_w, button_h)) + { + window->is_open = false; + } +} + +static void menu_draw_title(platform_window* window) +{ + s32 panel_w = 198 * scale; + s32 panel_h = 70 * scale; + s32 panel_pad = 50 * scale; + s32 panel_x = area.x + panel_pad; + s32 panel_y = area.y + area.h - panel_h - panel_pad; + panel_render(scale, panel_x, panel_y, panel_w, panel_h); + + font* font_reg = FONT_REGULAR(SIZE_RD(area.w, 44)); + font* font_sml = FONT_REGULAR(SIZE_RD(area.w, 20)); + s32 text_pad = 5*scale; + s32 total_text_h = font_reg->px_h + text_pad + font_sml->px_h; + s32 text_y = panel_y + (panel_h/2) - (total_text_h/2); + char* game_title = "TruckerX"; + char* game_version = "rev "GAME_VERSION; + s32 game_title_width = renderer->calculate_text_width(font_reg, game_title); + s32 text_x = panel_x + (panel_w/2) - (game_title_width/2); + + renderer->render_text(font_reg, text_x+2, text_y+2, game_title, COLOR_TEXT_SHADOW); + renderer->render_text(font_reg, text_x, text_y, game_title, COLOR_TEXT); + + renderer->render_text(font_sml, 10*scale + text_x+2,font_reg->px_h + text_pad + text_y+2, game_version, COLOR_TEXT_SHADOW); + renderer->render_text(font_sml, 10*scale + text_x, font_reg->px_h + text_pad + text_y, game_version, COLOR_TEXT); +} + +void menu_scene_render(platform_window* window) +{ + menu_draw_background(window); + menu_draw_options(window); + menu_draw_title(window); +} + +void menu_scene_update(platform_window* window) +{ + +} + +void menu_scene_destroy() +{ + +} \ No newline at end of file diff --git a/src/scenes/place_detail.c b/src/scenes/place_detail.c new file mode 100644 index 0000000..699fe0e --- /dev/null +++ b/src/scenes/place_detail.c @@ -0,0 +1,1771 @@ +typedef enum t_place_detail_state +{ + PLACE_DETAIL_SHOW_MAIN, + PLACE_DETAIL_SHOW_RESUMES, + PLACE_DETAIL_SHOW_DEALERS, + PLACE_DETAIL_SHOW_EMPLOYEE, + PLACE_DETAIL_SHOW_SCHEDULE, +} place_detail_state; + +typedef enum t_place_detail_info_state +{ + PLACE_DETAIL_EMPLOYEES = 0, + PLACE_DETAIL_JOBOFFERS = 1, + PLACE_DETAIL_SCHEDULE = 2, + PLACE_DETAIL_GARAGE = 3, +} place_detail_info_state; + +typedef enum t_schedule_state +{ + SCHEDULING_JOB, + RESCHEDULING_JOB, + VIEWING, +} schedule_state; + +// Animations +#define TAG_ANIMATION_DURATION 100 +#define EMPLOYEE_SELECTOR_ANIMATION_DURATION 100 +#define TRUCK_SWAP_ANIMATION_DURATION 200 +animation tag_animation = {0,0,0,1}; +animation employee_selector_animation = {0,0,0,1}; +animation truck_swap_animation = {0,0,0,1}; + +// States +world_location* _active_location; +employee* _active_employee; +scheduled_job _active_scheduling_job; +scheduled_job* _active_selected_scheduled_job; +schedule_state _active_schedule_state = VIEWING; +s32 _active_schedule_selected_job_index = 0; +place_detail_info_state selected_tab_index = PLACE_DETAIL_EMPLOYEES; +place_detail_state current_detail_state = PLACE_DETAIL_SHOW_MAIN; +static s16 selected_truck_index = 0; +static u8 active_dealer_index = 0; +#define INVALID_VAL -999 +static s32 index_of_truck = INVALID_VAL; + +typedef struct t_tab +{ + s32 x; + s32 y; + s32 w; + s32 h; + float scale; +} tab; + +void _goto_default_detail_state() +{ + index_of_truck = INVALID_VAL; + selected_truck_index = 0; + active_dealer_index = 0; + _active_employee = 0; + _active_schedule_state = VIEWING; + current_detail_state = PLACE_DETAIL_SHOW_MAIN; + _active_schedule_selected_job_index = 0; + _active_selected_scheduled_job = 0; + keyboard_set_input_text(""); + _global_keyboard.take_input = false; +} + +void place_detail_set_active_location(world_location* location) +{ + _active_location = location; + selected_tab_index = PLACE_DETAIL_EMPLOYEES; + current_detail_state = PLACE_DETAIL_SHOW_MAIN; +} + +void place_detail_scene_init() +{ + +} + +static char* get_shipday_list_string(job_offer* offer, char* buf, s32 len) +{ + memset(buf, 0, len); + bool first = true; + if (job_offer_has_ship_day(offer, MONDAY)) { string_appendn(buf, "Mon", len); first = false; } + if (job_offer_has_ship_day(offer, TUESDAY)) { if (!first) string_appendn(buf, ", ", len); string_appendn(buf, "Tue", len); first = false; } + if (job_offer_has_ship_day(offer, WEDNESDAY)) { if (!first) string_appendn(buf, ", ", len); string_appendn(buf, "Wed", len); first = false; } + if (job_offer_has_ship_day(offer, THURSDAY)) { if (!first) string_appendn(buf, ", ", len); string_appendn(buf, "Thu", len); first = false; } + if (job_offer_has_ship_day(offer, FRIDAY)) { if (!first) string_appendn(buf, ", ", len); string_appendn(buf, "Fri", len); first = false; } + if (job_offer_has_ship_day(offer, SATURDAY)) { if (!first) string_appendn(buf, ", ", len); string_appendn(buf, "Sat", len); first = false; } + if (job_offer_has_ship_day(offer, SUNDAY)) { if (!first) string_appendn(buf, ", ", len); string_appendn(buf, "Sun", len); first = false; } + + return buf; +} + +static scheduled_job* get_scheduled_job_at_time(s32 day, s32 timeslot) +{ + for (s32 i = 0; i < _active_location->schedule.jobs.length; i++) { + scheduled_job* slot = array_at(&_active_location->schedule.jobs, i); + for (s32 x = 0; x < slot->offer.shipday_count; x++) { + if (slot->timeslots[x].day == day && slot->timeslots[x].timeslot == timeslot) return slot; + } + } + return 0; +} + +static s32 find_empty_timeslot_for_day(s32 day) +{ + log_assert(day >= 0 && day < NUM_DAYS, "Invalid day"); + for (s32 i = 0; i < TIME_SLOTS_PER_DAY; i++) { + scheduled_job* job = get_scheduled_job_at_time(day, i); + if (!job || (_active_schedule_state == RESCHEDULING_JOB && job == _active_selected_scheduled_job)) return i; + } + return -1; +} + +static s32 find_empty_timeslot_for_day_right_to_left(s32 day) +{ + log_assert(day >= 0 && day < NUM_DAYS, "Invalid day"); + for (s32 i = TIME_SLOTS_PER_DAY-1; i >= 0; i--) { + scheduled_job* job = get_scheduled_job_at_time(day, i); + if (!job || (_active_schedule_state == RESCHEDULING_JOB && job == _active_selected_scheduled_job)) return i; + } + return -1; +} + + +static scheduled_job create_empty_job_schedule(job_offer* job) +{ + scheduled_job new_job; + new_job.location = _active_location; + new_job.offer = *job; + new_job.trust = 1.0f; + for (s32 i = 0; i < MAX_SHIPDAYS; i++) { + new_job.timeslots[i] = (scheduled_job_time){-1, -1, 0, 0}; + } + for (s32 i = 0; i < job->shipday_count; i++) { + new_job.timeslots[i] = (scheduled_job_time){job->shipdays[i], find_empty_timeslot_for_day(job->shipdays[i]), 0, 0}; + } + return new_job; +} + +#define scroll_speed 15 +#define HANDLE_TAB_SCROLL\ + bool hovering_tab = (_global_mouse.y >= orig_y && _global_mouse.y <= orig_y + h \ + && _global_mouse.x >= x && _global_mouse.x <= x + w);\ + if (scroll_h > 0) {\ + if (hovering_tab) {\ + if (_global_mouse.scroll_state < 0) current_scroll += scroll_speed;\ + if (_global_mouse.scroll_state > 0) current_scroll -= scroll_speed;\ + }\ + if (current_scroll > scroll_h) current_scroll = scroll_h;\ + if (current_scroll < 0) current_scroll = 0;\ + y -= current_scroll;\ + } + +#define HANDLE_TAB_START(_count, _parts)\ + s32 item_h = 34 * tab.scale;\ + s32 total_h = (item_h+1) * (_count) + (10 * tab.scale);\ + s32 scroll_h = total_h - h;\ + s32 orig_y = y;\ + static s32 current_scroll = 0;\ + font* fnt = fnt_rd16;\ + s32 info_text_h = fnt->px_h*4;\ + scroll_h += info_text_h;\ + s32 item_part_w = (w-item_h) / _parts;\ + (void)item_part_w; + +#define HANDLE_TAB_INFO(_info, _btn_name, _next_state)\ + s32 btn_h = info_text_h/1.3;\ + s32 btn_w = btn_h*4;\ + total_h += info_text_h;\ + s32 info_text_y = y + (btn_h/2)-(fnt->px_h/2);\ + renderer->render_text(fnt, x+1, info_text_y+1, _info, COLOR_TEXT_SHADOW);\ + renderer->render_text(fnt, x, info_text_y, _info, COLOR_TEXT);\ + if (_btn_name && button_render(tab.scale, true, _btn_name, x+w-btn_w-1, y, btn_w, btn_h)) {\ + current_detail_state = _next_state;\ + }\ + y += info_text_h; + +#define HANDLE_TAB_ITEM_INTERACTION(_take) HANDLE_TAB_ITEM_INTERACTIONX(_take, i); +#define HANDLE_TAB_ITEM_INTERACTIONX(_take, _count)\ + s32 item_y = y + ((item_h+1) * _count);\ + bool hovered = (_global_mouse.x >= x && _global_mouse.x <= x + w\ + && _global_mouse.y >= item_y && _global_mouse.y <= item_y + item_h\ + && hovering_tab);\ + color tint = COLOR_LIST_ENTRY_BACKGROUND;\ + if (_take && hovered) {\ + tint = COLOR_LIST_ENTRY_BACKGROUND_ACTIVE;\ + platform_set_cursor(window, CURSOR_POINTER);\ + }\ + renderer->render_rectangle(x, item_y, w, item_h, tint); + +#define TAB_ITEM_PUSH_TEXT(_buf,_width,_total)\ + s32 text_w = renderer->calculate_text_width(fnt, _buf);\ + s32 x_start = (x + item_h*2 - text_pad) + _total;\ + s32 text_x = x_start + (_width/2)-(text_w/2);\ + s32 text_y = item_y + (item_h/2) - (fnt->px_h/2);\ + renderer->render_text(fnt, text_x+1, text_y+1, _buf, COLOR_TEXT_SHADOW);\ + renderer->render_text(fnt, text_x, text_y,_buf, COLOR_TEXT);\ + renderer->render_rectangle(x_start + _width, item_y+(5*tab.scale), 1, item_h-+(10*tab.scale), COLOR_BUTTON);\ + _total += _width; + +#define PUSH_NOT_AVAILABLE_TEXT(_cond, _text)\ + if (_cond) {\ + font* fnt = fnt_rd20;\ + char* txt = _text;\ + s32 textw = renderer->calculate_text_width(fnt, txt);\ + renderer->render_text(fnt, x+(w/2)-(textw/2), y+(tab.h/2)-(fnt->px_h/2)-(30*scale), txt, COLOR_TEXT);\ + } + +static void place_detail_draw_job_offers(platform_window* window, tab tab, float x, float y, float w, float h) +{ + HANDLE_TAB_START(_active_location->job_offers.length, 2); + HANDLE_TAB_SCROLL; + + char info_buf[50]; + sprintf(info_buf, "%d Job offers open.", _active_location->job_offers.length); + HANDLE_TAB_INFO(info_buf, 0, 0); + + PUSH_NOT_AVAILABLE_TEXT(!_active_location->job_offers.length, "No job offers available."); + + for (s32 i = 0; i < _active_location->job_offers.length; i++) { + job_offer* offer = array_at(&_active_location->job_offers, i); + HANDLE_TAB_ITEM_INTERACTION(true); + + if (hovered && is_left_clicked()) { + current_detail_state = PLACE_DETAIL_SHOW_SCHEDULE; + _active_schedule_state = SCHEDULING_JOB; + _active_schedule_selected_job_index = 0; + _active_selected_scheduled_job = 0; + _active_scheduling_job = create_empty_job_schedule(offer); + + tag_animation = animation_create(TAG_ANIMATION_DURATION); + tag_animation.started = true; + audio_play_sound(snd_click, AUDIO_CHANNEL_SFX_1); + } + + float icon_pad = item_h * 0.15; + float text_pad = icon_pad*2; + float icon_s = item_h - (icon_pad*2); + // icon + { + renderer->render_image(offer->company->logo, x + icon_pad, item_y + icon_pad, icon_s*2, icon_s); + renderer->render_rectangle(x + icon_pad + icon_s*2 + icon_pad, item_y+(5*tab.scale), 1, item_h-+(10*tab.scale), COLOR_BUTTON); + } + + float width_of_piece = 80 * tab.scale; + float price_text_w = 0; + + // price + { + char pricebuf[25]; + sprintf(pricebuf, "$%d/trip", offer->reward); + TAB_ITEM_PUSH_TEXT(pricebuf, width_of_piece, price_text_w); + } + + // distance + { + char pricebuf[25]; + sprintf(pricebuf, "%.0fkm", offer->total_distance); + TAB_ITEM_PUSH_TEXT(pricebuf, width_of_piece, price_text_w); + } + + // dur + { + char pricebuf[25]; + sprintf(pricebuf, "%.0fh-%.0fh", offer->duration_sec_min/3600.0f, offer->duration_sec_max/3600.0f); + TAB_ITEM_PUSH_TEXT(pricebuf, width_of_piece, price_text_w); + } + + // Name + { + char daybuf[50]; + char buf[200]; + sprintf(buf, "%s wants you to ship %s to %s every %s", offer->company->name, offer->product->name, + (*(world_location**)array_at(&offer->connections, offer->connections.length-1))->name, get_shipday_list_string(offer, daybuf, 50)); + s32 text_x = x + item_h*2 + price_text_w + icon_pad; + s32 text_y = item_y + (item_h/2) - (fnt->px_h/2); + renderer->render_text(fnt, text_x+1, text_y+1, buf, COLOR_TEXT_SHADOW); + renderer->render_text(fnt, text_x, text_y, buf, COLOR_TEXT); + } + } +} + + +static void place_detail_draw_trucks(platform_window* window, tab tab, float x, float y, float w, float h) +{ + HANDLE_TAB_START(_active_location->trucks.length, 5); + HANDLE_TAB_SCROLL; + + char info_buf[50]; + sprintf(info_buf, "%d Trucks in garage.", _active_location->trucks.length); + HANDLE_TAB_INFO(info_buf, "Purchase", PLACE_DETAIL_SHOW_DEALERS); + + PUSH_NOT_AVAILABLE_TEXT(!_active_location->trucks.length, "No trucks in garage."); + + for (s32 i = 0; i < _active_location->trucks.length; i++) { + truck* emp = array_at(&_active_location->trucks, i); + HANDLE_TAB_ITEM_INTERACTION(false); + + float icon_pad = item_h * 0.15; + float text_pad = icon_pad*2; + float icon_s = item_h - (icon_pad*2); + // icon + { + renderer->render_image(emp->logo, x + icon_pad, item_y + icon_pad, icon_s, icon_s); + renderer->render_rectangle(x + icon_pad + icon_s + icon_pad, item_y+(5*tab.scale), 1, item_h-+(10*tab.scale), COLOR_BUTTON); + } + + // Name + { + char buffer[MAX_WORLD_LOCATION_NAME_LENGTH + 20]; + sprintf(buffer, "%s, ID: #%d", emp->name, emp->id); + + s32 text_x = x + item_h + text_pad; + s32 text_y = item_y + (item_h/2) - (fnt->px_h/2); + renderer->render_text(fnt, text_x+1, text_y+1, buffer, COLOR_TEXT_SHADOW); + renderer->render_text(fnt, text_x, text_y, buffer, COLOR_TEXT); + renderer->render_rectangle(x + item_h + item_part_w, item_y+(5*tab.scale), 1, item_h-+(10*tab.scale), COLOR_BUTTON); + } + + // current location + { + char buffer[MAX_EMPLOYEE_NAME_LENGTH + 20]; + if (emp->assigned_employee) sprintf(buffer, "Assigned to %s", emp->assigned_employee->name); + else strcpy(buffer, "No assignee"); + s32 text_x = x + item_h + item_part_w*1 + text_pad; + s32 text_y = item_y + (item_h/2) - (fnt->px_h/2); + renderer->render_text(fnt, text_x+1, text_y+1, buffer, COLOR_TEXT_SHADOW); + renderer->render_text(fnt, text_x, text_y, buffer, COLOR_TEXT); + } + } +} + +static void place_detail_draw_employees(platform_window* window, tab tab, float x, float y, float w, float h) +{ + HANDLE_TAB_START(_active_location->employees.length+2, 4); + HANDLE_TAB_SCROLL; + + char info_buf[50]; + sprintf(info_buf, "%d Employees currently on site.", _active_location->employees.length); + HANDLE_TAB_INFO(info_buf, "Hire", PLACE_DETAIL_SHOW_RESUMES); + + s32 current_index = 0; + bool show_internal = true; + do_again:; + + for (s32 i = 0; i <= _active_location->employees.length; i++) { + employee* emp = 0; + if (i > 0) { + emp = *(employee**)array_at(&_active_location->employees, i-1); + bool is_internal = (emp->original_location_id == _active_location->id); + if (show_internal != is_internal) continue; + } + + HANDLE_TAB_ITEM_INTERACTIONX(i != 0, current_index); + if (i != 0 && hovered && is_left_clicked()) { + place_detail_show_employee_detail(emp); + audio_play_sound(snd_click, AUDIO_CHANNEL_SFX_1); + } + current_index++; + + if (i == 0) { + char* buffer = show_internal ? "Internal" : "External"; + s32 text_x = x + item_h; + s32 text_y = item_y + (item_h/2) - (fnt->px_h/2); + renderer->render_text(fnt, text_x+1, text_y+1, buffer, COLOR_TEXT_SHADOW); + renderer->render_text(fnt, text_x, text_y, buffer, COLOR_TEXT); + continue; + } + + float icon_pad = item_h * 0.15; + float text_pad = icon_pad*2; + float icon_s = item_h - (icon_pad*2); + // icon + { + draw_employee_portrait(emp, x + icon_pad, item_y + icon_pad, icon_s, icon_s); + renderer->render_rectangle(x + icon_pad + icon_s + icon_pad, item_y+(5*tab.scale), 1, item_h-+(10*tab.scale), COLOR_BUTTON); + } + + // Name + { + char buffer[MAX_EMPLOYEE_NAME_LENGTH + 20]; + sprintf(buffer, "%s, ID: #%d", emp->name, emp->id); + s32 text_x = x + item_h + text_pad; + s32 text_y = item_y + (item_h/2) - (fnt->px_h/2); + renderer->render_text(fnt, text_x+1, text_y+1, buffer, COLOR_TEXT_SHADOW); + renderer->render_text(fnt, text_x, text_y, buffer, COLOR_TEXT); + renderer->render_rectangle(x + item_h + item_part_w, item_y+(5*tab.scale), 1, item_h-(10*tab.scale), COLOR_BUTTON); + } + + // Name + { + char buffer[MAX_EMPLOYEE_NAME_LENGTH + 20]; + if (emp->assigned_truck) sprintf(buffer, "Truck: %s, #%d", emp->assigned_truck->name, emp->assigned_truck->id); + else strcpy(buffer, "No assigned truck"); + s32 text_x = x + item_h + item_part_w*1 + text_pad; + s32 text_y = item_y + (item_h/2) - (fnt->px_h/2); + renderer->render_text(fnt, text_x+1, text_y+1, buffer, COLOR_TEXT_SHADOW); + renderer->render_text(fnt, text_x, text_y, buffer, COLOR_TEXT); + renderer->render_rectangle(x + item_h + item_part_w*2, item_y+(5*tab.scale), 1, item_h-(10*tab.scale), COLOR_BUTTON); + } + + // current location + { + char buffer[MAX_WORLD_LOCATION_NAME_LENGTH + 20]; + if (emp->active_job_id != INVALID_ID) { + active_job* j = get_active_job_by_id(_active_world, emp->active_job_id); + job_endpoints endpoints = job_offer_get_endpoints(&j->offer); + sprintf(buffer, "Driving to %s", endpoints.dest->name); + } + else if (emp->current_location_id != _active_location->id) { // employee located at other location + sprintf(buffer, "Located at %s", get_world_location_by_id(_active_world, emp->current_location_id)->name); + } + else { // At original location + sprintf(buffer, "%s", "Idling"); + } + + s32 text_x = x + item_h + item_part_w*2 + text_pad; + s32 text_y = item_y + (item_h/2) - (fnt->px_h/2); + renderer->render_text(fnt, text_x+1, text_y+1, buffer, COLOR_TEXT_SHADOW); + renderer->render_text(fnt, text_x, text_y, buffer, COLOR_TEXT); + renderer->render_rectangle(x + item_h + item_part_w*3, item_y+(5*tab.scale), 1, item_h-+(10*tab.scale), COLOR_BUTTON); + } + + // Happiness + { + s32 text_x = x + item_h + item_part_w*3 + text_pad; + s32 text_y = item_y + (item_h/2) - (fnt->px_h/2); + + s32 stars = employee_calculate_happiness_stars(emp); + s32 textw = renderer->render_text(fnt, text_x, text_y, "Happiness: ", COLOR_TEXT); + text_x += (scale*5); + for (s32 i = 0; i < 5; i++) + { + if (i < stars) renderer->render_image(img_star, text_x + textw + (i*(fnt->px_h+2)), text_y, fnt->px_h, fnt->px_h); + else renderer->render_image_tint(img_star, text_x + textw + (i*(fnt->px_h+2)), text_y, fnt->px_h, fnt->px_h, rgba(255,255,255,30)); + } + + //renderer->render_rectangle(x + item_h + item_part_w*4, item_y+(5*tab.scale), 1, item_h-+(10*tab.scale), COLOR_BUTTON); + } + } + + if (show_internal) { + show_internal = false; + goto do_again; + } +} + +static void render_logo_at(image* img, s32 pad, s32 x, s32 y, s32 w, s32 h) +{ + if (img->width >= img->height) + { + float s_scale = w/(float)img->width; + float new_w = w - (pad*2); + float new_h = (img->height * s_scale) - (pad*2); + float new_x = x + w/2 - new_w/2; + float new_y = y + h/2 - new_h/2; + renderer->render_image(img,new_x,new_y,new_w,new_h); + } +} + +static void place_detail_draw_active_dealer(platform_window* window, truck_dealer* dealer, float scale, s32 x, s32 y, s32 w, s32 h) +{ + button_render(scale, false, 0, x, y, w, h); + static truck_dealer* prev_dealer = 0; + if (prev_dealer != dealer) { + prev_dealer = dealer; + selected_truck_index = 0; + } + + if (selected_truck_index < 0) selected_truck_index = 0; + if (selected_truck_index >= dealer->trucks.length) selected_truck_index = dealer->trucks.length-1; + truck* tr_curr = (selected_truck_index >= 0 && selected_truck_index <= dealer->trucks.length-1) ? array_at(&dealer->trucks, selected_truck_index) : 0; + truck* tr_prev = (selected_truck_index-1 >= 0) ? array_at(&dealer->trucks, selected_truck_index-1) : 0; + truck* tr_next = (selected_truck_index+1 <= dealer->trucks.length-1) ? array_at(&dealer->trucks, selected_truck_index+1) : 0; + if (!tr_curr) return; + s32 total_img_space = (w)/3 * 2; + s32 img_w_center = total_img_space/10*5; + s32 img_w_side = total_img_space/10*2.5; + s32 pad = 40 * scale; + s32 img_x = x; + s32 img_y = y; + + static animation swap_animation = {0,0,0,0}; + static animation purchase_animation = {0,0,0,0}; + if (swap_animation.started) animation_update(&swap_animation); + if (purchase_animation.started) animation_update(&purchase_animation); + static s32 target_truck_index = 0; + + truck* tn_prev = (target_truck_index-1 >= 0) ? array_at(&dealer->trucks, target_truck_index-1) : 0; + truck* tn_next = (target_truck_index+1 <= dealer->trucks.length-1) ? array_at(&dealer->trucks, target_truck_index+1) : 0; + + // Truck images + { + s32 overlap = 50 * scale; + + s32 y_side = img_y + (h / 2) - (img_w_side/2); + s32 x_left = img_x + overlap; + s32 y_left = y_side; + s32 w_left = img_w_side; + s32 h_left = img_w_side; + + s32 x_right = img_x+img_w_side+img_w_center - overlap; + s32 y_right = y_side; + s32 w_right = img_w_side; + s32 h_right = img_w_side; + + s32 x_center = img_x+img_w_side; + s32 y_center = img_y + (h / 2) - (img_w_center/2); + s32 w_center = img_w_center; + s32 h_center = img_w_center; + + #define LI(_cx,_dx) (_cx + (_dx-_cx)*swap_animation.percentage) + #define LIP(_cx,_dx) (_cx + (_dx-_cx)*purchase_animation.percentage) + #define LI_TINT(_c,_d) rgba(255,255,255,_c + (_d-_c)*swap_animation.percentage) + #define LI_TINTP(_c,_d) rgba(255,255,255,_c + (_d-_c)*purchase_animation.percentage) + + if (tr_prev) { + if (target_truck_index < selected_truck_index) { + color tint = LI_TINT(50, 255); + renderer->render_image_tint(tr_prev->logo,LI(x_left,x_center),LI(y_left,y_center),LI(w_left,w_center),LI(h_left,h_center), tint); + + if (swap_animation.started && tn_prev) { + color tint = LI_TINT(0, 50); + renderer->render_image_tint(tn_prev->logo,x_left,y_left,w_left,h_left, tint); + } + } + else { + color tint = LI_TINT(50, 0); + renderer->render_image_tint(tr_prev->logo,x_left,y_left,w_left,h_left, tint); + } + } + if (tr_next) { + if (target_truck_index < selected_truck_index) { + color tint = LI_TINT(50, 0); + renderer->render_image_tint(tr_next->logo,x_right,y_right,w_right,h_right, tint); + } + else { + color tint = LI_TINT(50, 255); + renderer->render_image_tint(tr_next->logo,LI(x_right,x_center),LI(y_right,y_center),LI(w_right,w_center),LI(h_right,h_center), tint); + + if (swap_animation.started && tn_next) { + color tint = LI_TINT(0, 50); + renderer->render_image_tint(tn_next->logo,x_right,y_right,w_right,h_right, tint); + } + } + } + if (!purchase_animation.started) + { + color tint = LI_TINT(255, 50); + if (target_truck_index < selected_truck_index) + renderer->render_image_tint(tr_curr->logo,LI(x_center,x_right),LI(y_center,y_right),LI(w_center,w_right),LI(h_center,h_right), tint); + else + renderer->render_image_tint(tr_curr->logo,LI(x_center,x_left),LI(y_center,y_left),LI(w_center,w_left),LI(h_center,h_left), tint); + } + else { + vec4 area = camera_get_target_rectangle(window); + s32 x_off = area.x; + s32 h_off = area.h/2; + s32 y_off = area.y+(area.h/2)-(h_off/2); + s32 w_off = h_off; + + color tint = LI_TINTP(255, 0); + renderer->render_image_tint(tr_curr->logo,LIP(x_center,x_off),LIP(y_center,y_off),LIP(w_center,w_off),LIP(h_center,h_off), tint); + } + } + + font* fnt = fnt_rd24; + s32 text_y = img_y + pad; + s32 text_x = x + total_img_space; + + #define PUSH_TEXT(_str, _data)\ + {char buf[50]; snprintf(buf,50,_str,_data);\ + renderer->render_text(fnt, text_x+1, text_y+1, buf, COLOR_TEXT_SHADOW);\ + renderer->render_text(fnt, text_x, text_y, buf, COLOR_TEXT);}\ + text_y+=fnt->px_h+(10*scale); + + PUSH_TEXT("Name: %s", tr_curr->name); + PUSH_TEXT("Power: %dhp", tr_curr->hp); + PUSH_TEXT("Price: $%d", tr_curr->price); + PUSH_TEXT("Fuel Capacity: %dL", tr_curr->fuelcapacity); + PUSH_TEXT("Torque %drpm", tr_curr->torque); + PUSH_TEXT("Fuel Usage %.1fL per 100Km", tr_curr->fuelusage); + + #define TRUCK_SWAP_DELAY 200 + #define TRUCK_PURCHASE_DELAY 800 + s32 btn_size = 30*scale; + s32 btn_x = text_x; + + button_type type1 = purchase_animation.started || swap_animation.started ? BUTTON_STATIC : (selected_truck_index > 0) ? BUTTON_ENABLED : BUTTON_DISABLED; + if (button_render(scale, type1, "<", btn_x, text_y, btn_size, btn_size)) { + if (!swap_animation.started) { + swap_animation = animation_create(TRUCK_SWAP_DELAY); + swap_animation.started = true; + target_truck_index = selected_truck_index-1; + } + } + btn_x += btn_size; + s32 center_btn_w = 100*scale; + button_type type2 = purchase_animation.started || swap_animation.started ? BUTTON_STATIC : BUTTON_ENABLED; + if (button_render(scale, type2, "purchase", btn_x, text_y, center_btn_w, btn_size)) { + ADD_EXPENSE(_active_world, _active_location, expenses_from_trucks, tr_curr->price) + add_truck_to_world_location(_active_world, _active_location, tr_curr); + purchase_animation = animation_create(TRUCK_PURCHASE_DELAY); + purchase_animation.started = true; + } + btn_x += center_btn_w; + button_type type3 = purchase_animation.started || swap_animation.started ? BUTTON_STATIC : (selected_truck_index < dealer->trucks.length-1) ? BUTTON_ENABLED : BUTTON_DISABLED; + if (button_render(scale, type3, ">", btn_x, text_y, btn_size, btn_size)) { + if (!swap_animation.started) { + swap_animation = animation_create(TRUCK_SWAP_DELAY); + swap_animation.started = true; + target_truck_index = selected_truck_index+1; + } + } + if (swap_animation.percentage == 1.0f) { + swap_animation = (animation){0,0,0,0}; + selected_truck_index = target_truck_index; + } + if (purchase_animation.percentage == 1.0f) { + purchase_animation = (animation){0,0,0,0}; + current_detail_state = PLACE_DETAIL_SHOW_MAIN; + selected_truck_index = 0; + active_dealer_index = 0; + } +} + +void place_detail_show_schedule_with_highlighted_job(world_location* loc, scheduled_job* job, scheduled_job_time job_time) { + _goto_default_detail_state(); + place_detail_set_active_location(loc); + game_set_active_scene(GAME_STATE_PLACE_DETAIL); + + if (job) { + for (s32 i = 0; i < MAX_SHIPDAYS; i++) { + if (job_time.day == job->timeslots[i].day) { + _active_schedule_selected_job_index = i; + break; + } + } + } + + _active_schedule_state = VIEWING; + _active_selected_scheduled_job = job; + selected_tab_index = PLACE_DETAIL_SHOW_MAIN; + current_detail_state = PLACE_DETAIL_SHOW_SCHEDULE; + tag_animation = animation_create(TAG_ANIMATION_DURATION); + tag_animation.started = true; +} + +void place_detail_show_employee_detail(employee* emp) { + _goto_default_detail_state(); + world_location* original_loc = get_world_location_by_id(_active_world, emp->original_location_id); + place_detail_set_active_location(original_loc); + game_set_active_scene(GAME_STATE_PLACE_DETAIL); + current_detail_state = PLACE_DETAIL_SHOW_EMPLOYEE; + _active_employee = emp; + tag_animation = animation_create(TAG_ANIMATION_DURATION); + tag_animation.started = true; +} + +static s32 place_detail_draw_selected_employee_truck_selector(platform_window* window, s32 x, s32 y, s32 w, s32 h) +{ + s32 btn_w = 40*scale; + s32 pad = 30*scale; + s32 truck_h = h / 2; + s32 truck_w = truck_h; + s32 truck_x = x + (pad*2) + btn_w; + s32 truck_y = y + pad; + + world_location* original_location = get_world_location_by_id(_active_world, _active_employee->original_location_id); + + if (index_of_truck == INVALID_VAL) { + index_of_truck = _active_employee->assigned_truck ? ((void*)_active_employee->assigned_truck - (void*)original_location->trucks.data) / sizeof(truck) : -1; + } + + s32 bg_h = h; + s32 bg_w = truck_w + (btn_w*2) + (pad*4); + button_draw_background(scale, x,y,bg_w,bg_h, COLOR_WHITE, COLOR_BUTTON); + + static bool animation_going_left = false; + + // Draw truck info + { + #define TRUCK_ANIMATION_OFFSET (animation_going_left ? -60*scale : 60*scale) + s32 truck_offset_x = TRUCK_ANIMATION_OFFSET - (TRUCK_ANIMATION_OFFSET*truck_swap_animation.percentage); + color truck_tint = AN_LI_TINT(COLOR_WHITE, truck_swap_animation); + + truck* selected_truck = 0; + if (index_of_truck != -1) selected_truck = array_at(&original_location->trucks, index_of_truck); + renderer->render_image_tint(selected_truck ? selected_truck->logo : img_truck_unknown, truck_x+truck_offset_x, truck_y, truck_w, truck_h, truck_tint); + if (selected_truck) { + font* fnt = fnt_rd24; + char buf[40]; + sprintf(buf, "%s, ID: #%d", selected_truck->name, selected_truck->id); + s32 text_w = renderer->calculate_text_width(fnt, buf); + s32 text_x = truck_x + (truck_w/2)-(text_w/2); + renderer->render_text(fnt, text_x+1+truck_offset_x, truck_y+truck_h+1, buf, COLOR_TEXT_SHADOW); + renderer->render_text(fnt, text_x+truck_offset_x,truck_y+truck_h, buf, COLOR_TEXT); + } + } + + // Change truck + { + if (button_render(scale, BUTTON_ENABLED, "<", truck_x-btn_w-pad,truck_y, btn_w, truck_h)) { + s32 prev_index = index_of_truck; + index_of_truck = -1; + + try_again: + for (s32 i = prev_index-1; i >= 0; i--) { + truck* emp = array_at(&original_location->trucks, i); + if (emp->assigned_employee == 0 || emp->assigned_employee == _active_employee) { + index_of_truck = i; + break; + } + } + + if (prev_index == -1) { + prev_index = original_location->trucks.length; + goto try_again; + } + + truck_swap_animation = animation_create(TRUCK_SWAP_ANIMATION_DURATION); + truck_swap_animation.started = true; + animation_going_left = true; + } + if (button_render(scale, BUTTON_ENABLED, ">", truck_x+truck_w+pad,truck_y, btn_w, truck_h)) { + s32 prev_index = index_of_truck; + index_of_truck = -1; + for (s32 i = prev_index+1; i < original_location->trucks.length; i++) { + truck* emp = array_at(&original_location->trucks, i); + if (emp->assigned_employee == 0 || emp->assigned_employee == _active_employee) { + index_of_truck = i; + break; + } + } + + truck_swap_animation = animation_create(TRUCK_SWAP_ANIMATION_DURATION); + truck_swap_animation.started = true; + animation_going_left = false; + } + } + + + // Save changes button + { + s32 btn_h = 30*scale; + btn_w = 160*scale; + s32 btn_y = y + h - pad - btn_h; + s32 btn_x = x + (bg_w/2) - (btn_w/2); + if (button_render(scale, BUTTON_ENABLED, "Save Changes", btn_x, btn_y, btn_w, btn_h)) { + if (index_of_truck != -1) { + if (_active_employee->assigned_truck) _active_employee->assigned_truck->assigned_employee = 0; + _active_employee->assigned_truck = array_at(&_active_location->trucks, index_of_truck); + log_assert(_active_employee->assigned_truck->assigned_employee == 0 && + _active_employee->assigned_truck->assigned_employee != _active_employee, "Truck already has assignee"); + + _active_employee->assigned_truck->assigned_employee = _active_employee; + } + else { + if (_active_employee->assigned_truck) _active_employee->assigned_truck->assigned_employee = 0; + _active_employee->assigned_truck = 0; + } + + index_of_truck = INVALID_VAL; + current_detail_state = PLACE_DETAIL_SHOW_MAIN; + } + } + + animation_update(&truck_swap_animation); + return bg_w; +} + +static void place_detail_draw_selected_employee(platform_window* window) +{ + float offset = scale * 40.0; + s32 item_h = 82 * 0.4 * scale; + s32 h = (area.h * 0.6 - (offset*2)) + item_h; + s32 w = (area.w * 0.9 - (offset*2)); + s32 x = offset + area.x + (area.w*0.05); + s32 pos_y = area.y + (area.w*0.12); + s32 y = pos_y; + + s32 truck_selector_w = place_detail_draw_selected_employee_truck_selector(window, x, y, w, h); + + { + font* fnt = fnt_rd24; + + s32 info_panel_x = x + truck_selector_w+offset; + s32 info_panel_w = w - offset - truck_selector_w; + s32 pad = 30*scale; + + s32 text_y = y + pad; + s32 text_x = info_panel_x + pad; + s32 text_offset = fnt->px_h + (10*scale); + s32 info_h = (h/2)-(pad/2); + button_draw_background(scale, info_panel_x, y, info_panel_w, info_h, COLOR_WHITE, COLOR_BUTTON); + + s32 portrait_h = (info_h)-(pad*2); + draw_employee_portrait(_active_employee, text_x, text_y, portrait_h, portrait_h); + + text_x += portrait_h + pad; + + #define PUSH_INFO_TEXT(_fmt, _data)\ + {\ + char buf[100];\ + sprintf(buf, _fmt, _data);\ + renderer->render_text(fnt, text_x, text_y, buf, COLOR_TEXT);\ + text_y += text_offset;\ + } + + PUSH_INFO_TEXT("Name: %s", _active_employee->name); + PUSH_INFO_TEXT("Age: %d", _active_employee->age); + PUSH_INFO_TEXT("Experience: %d years", _active_employee->experience); + PUSH_INFO_TEXT("Salary: $%.2f/month", _active_employee->salary); + + // Happiness + { + s32 stars = employee_calculate_happiness_stars(_active_employee); + s32 textw = renderer->render_text(fnt, text_x, text_y, "Happiness: ", COLOR_TEXT); + for (s32 i = 0; i < 5; i++) + { + if (i < stars) renderer->render_image(img_star, text_x + textw + (i*(fnt->px_h+2)), text_y, fnt->px_h, fnt->px_h); + else renderer->render_image_tint(img_star, text_x + textw + (i*(fnt->px_h+2)), text_y, fnt->px_h, fnt->px_h, rgba(255,255,255,30)); + } + text_y += text_offset; + } + + #define SALARY_RAISE_INCREASE 70 + // Buttons + { + s32 pad_between_items = 10 * scale; + s32 button_w = 164 * scale; + s32 button_h = 37 * scale; + s32 btn_y = y+info_h+pad+info_h-button_h; + s32 btn_x = x + w - button_w; + if (button_render(scale, BUTTON_ENABLED, "End Contract", btn_x, btn_y, button_w, button_h)) { + audio_play_sound(snd_click3, AUDIO_CHANNEL_SFX_1); + end_contract_with_employee(_active_world, _active_employee); + _active_employee = 0; + _goto_default_detail_state(); + } + btn_y -= (button_h+pad_between_items); + + if (button_render(scale, BUTTON_ENABLED, "Give Raise", btn_x, btn_y, button_w, button_h)) { + audio_play_sound(snd_click2, AUDIO_CHANNEL_SFX_1); + _active_employee->salary += SALARY_RAISE_INCREASE; + } + btn_y -= (button_h+pad_between_items); + } + } +} + +static void place_detail_draw_schedule_employee_options(platform_window* window, s32 pad, float scale, s32 x, s32 y, s32 w, s32 h) +{ + bool viewing = (_active_schedule_state == VIEWING && !_active_selected_scheduled_job); + if (viewing) return; + + job_endpoints endpoints = job_offer_get_endpoints(&_active_scheduling_job.offer); + bool enabled = endpoints.dest->is_owned; + + if (enabled) button_draw_background(scale, x,y,w,h, COLOR_WHITE, COLOR_BUTTON); + else button_draw_background(scale, x,y,w,h, COLOR_BUTTON_DISABLED_TINT, COLOR_BUTTON_DISABLED); + + s32 checkbox_s = h/3; + scheduled_job_time selected_timeslot = _active_scheduling_job.timeslots[_active_schedule_selected_job_index]; + if (_active_schedule_state == VIEWING) selected_timeslot = _active_selected_scheduled_job->timeslots[_active_schedule_selected_job_index]; + + char* check_txt = selected_timeslot.stay_at_destination ? "X" : ""; + if (button_render(scale, enabled && _active_schedule_state != VIEWING ? BUTTON_ENABLED : BUTTON_DISABLED, check_txt, x+pad,y+(h/2)-(checkbox_s/2),checkbox_s,checkbox_s)) { + _active_scheduling_job.timeslots[_active_schedule_selected_job_index].stay_at_destination = !selected_timeslot.stay_at_destination; + } + + font* fnt = fnt_rd20; + s32 textx = x + pad + checkbox_s + pad/2; + s32 texty = y+(h/2)-(fnt->px_h/2); + renderer->render_text(fnt, textx, texty, "Stay at destination", COLOR_TEXT); + + if (!enabled) { + s32 icon_s = h/2; + renderer->render_image(img_lock, x + (w/2)-(icon_s/2), y + (h/2)-(icon_s/2), icon_s, icon_s); + } +} + +static void place_detail_draw_schedule_employee_selector(platform_window* window, s32 pad, float scale, s32 x, s32 y, s32 w, s32 h) +{ + if (_active_schedule_state == VIEWING && !_active_selected_scheduled_job) { + button_render(scale, BUTTON_STATIC, 0, x,y,w,h); + font* fnt = fnt_rd20; + s32 texty = y + (h/2)-(fnt->px_h/2); + s32 textx = x + pad; + renderer->render_text(fnt, textx, texty, "Select a timeslot for details.", COLOR_TEXT); + } + else { + scheduled_job_time selected_timeslot = _active_scheduling_job.timeslots[_active_schedule_selected_job_index]; + + employee* selected_employee = selected_timeslot.assignee; + bool is_viewing_existing_timeslot = _active_selected_scheduled_job && _active_schedule_state == VIEWING; + if (is_viewing_existing_timeslot) { + selected_employee = _active_selected_scheduled_job->timeslots[_active_schedule_selected_job_index].assignee; + } + + // When timeslot changes, set selected employee id as keyboard input text. + static s32 prev_selected_job_index = 0; + if (is_viewing_existing_timeslot) prev_selected_job_index = -1; + if (prev_selected_job_index != _active_schedule_selected_job_index) { + prev_selected_job_index = _active_schedule_selected_job_index; + if (selected_employee) { + char idbuf[20]; + sprintf(idbuf, "%d", selected_employee->id); + keyboard_set_input_text(idbuf); + } + else { + keyboard_set_input_text(""); + } + } + + _active_scheduling_job.timeslots[_active_schedule_selected_job_index].assignee = + employee_selector_render(window, scale, !is_viewing_existing_timeslot, selected_employee,x,y,w,h, employee_selector_animation, &_active_scheduling_job); + animation_update(&employee_selector_animation); + } +} + +static bool scheduling_job_is_correct() +{ + for (s32 i = 0; i < _active_scheduling_job.offer.shipday_count; i++) { + scheduled_job_time scheduled_time = _active_scheduling_job.timeslots[i]; + if (!scheduled_time.assignee) { return false; } + if (scheduled_time.timeslot == -1) { return false; } + } + return true; +} + +static void draw_duration_of_scheduled_job_entry(scheduled_job_time scheduled_time, s32 hour_w, s32 tx, s32 ty, s32 timeslot_w, s32 x, s32 y, s32 timeslot_h, color tile_color) +{ + #define TILE_X(_x) (x+hour_w+(timeslot_w*_x)+1) + #define TILE_Y(_y) (y+(timeslot_h*(CDAYTORDAY(_y)))+1) + + s32 duration_of_job_measured_in_timeslots_max = ceil((_active_scheduling_job.offer.duration_sec_max/60.0f) / (60.0f/TIME_SLOTS_PER_HOUR)); + if (!scheduled_time.stay_at_destination) duration_of_job_measured_in_timeslots_max*=2; + log_assert(duration_of_job_measured_in_timeslots_max < 36*TIME_SLOTS_PER_HOUR, "Multi-day trip not supported yet in schedule preview."); + + s32 slots_outside_of_schedule = (24-(WORK_HOUR_END-WORK_HOUR_START))*TIME_SLOTS_PER_HOUR; + s32 overflow_to_next_day = -(((TIME_SLOTS_PER_DAY-scheduled_time.timeslot) - + (duration_of_job_measured_in_timeslots_max)) + + slots_outside_of_schedule); + color tile_color_bg = tile_color; + tile_color_bg.a = 50; + + s32 highlight_w = timeslot_w*duration_of_job_measured_in_timeslots_max; + if (tx+highlight_w > x+(timeslot_w*(TIME_SLOTS_PER_DAY+TIME_SLOTS_PER_HOUR))) + highlight_w -= (tx+highlight_w) - (x+(timeslot_w*(TIME_SLOTS_PER_DAY+TIME_SLOTS_PER_HOUR))); + renderer->render_rectangle(tx, ty, highlight_w, timeslot_h, tile_color_bg); + s32 highlight_y = ty+timeslot_h; + if (scheduled_time.day == SUNDAY) highlight_y = TILE_Y(MONDAY); + if (overflow_to_next_day > 0) renderer->render_rectangle(TILE_X(0), highlight_y, timeslot_w*overflow_to_next_day, timeslot_h, tile_color_bg); +} + +static void place_detail_draw_schedule(platform_window* window) +{ + float offset = scale * 40.0; + s32 item_h = 82 * 0.4 * scale; + s32 h = (area.h * 0.6 - (offset*2)) + item_h; + s32 pad_between_items = 10 * scale; + s32 employee_select_h = (h/4); + h -= employee_select_h + pad_between_items; + s32 w = (area.w * 0.9 - (offset*2)); + s32 x = offset + area.x + (area.w*0.05); + s32 pos_y = area.y + (area.w*0.12); + s32 y = pos_y; + s32 btn_pad = pad_between_items/2; + + s32 button_w = 124 * scale; + s32 button_h = (employee_select_h-btn_pad)/2; + s32 schedule_pad = 20*scale; + + #define COLS (TIME_SLOTS_PER_DAY+TIME_SLOTS_PER_HOUR) + #define ROWS 8 + s32 timeslot_w = (w-schedule_pad*2)/COLS; + s32 timeslot_h = (h-schedule_pad*2)/ROWS; + s32 hour_w = timeslot_w*4; + s32 new_w = (timeslot_w)*COLS+schedule_pad*2; + s32 new_h = (timeslot_h)*ROWS+schedule_pad*2; + s32 off_x = w - new_w; + s32 off_y = h - new_h; + w = new_w; + h = new_h; + x += off_x/2; + y += off_y/2; + + s32 employee_select_y = y + h + pad_between_items; + s32 btn_y = y+h+pad_between_items; + if (_active_schedule_state == SCHEDULING_JOB || _active_schedule_state == RESCHEDULING_JOB) + { + button_type type = scheduling_job_is_correct() ? BUTTON_ENABLED : BUTTON_DISABLED; + if (button_render(scale, type, "Accept", x-button_w+w, btn_y, button_w, button_h)) { + audio_play_sound(snd_click2, AUDIO_CHANNEL_SFX_1); + if (_active_schedule_state == SCHEDULING_JOB) + array_push(&_active_location->schedule.jobs, &_active_scheduling_job); + else if (_active_schedule_state == RESCHEDULING_JOB) + *_active_selected_scheduled_job = _active_scheduling_job; + + job_offer* offer = get_job_offer_by_id(_active_location, _active_scheduling_job.offer.id); + if (offer) array_remove_by(&_active_location->job_offers, offer); + + _goto_default_detail_state(); + } + btn_y += btn_pad + button_h; + if (button_render(scale, BUTTON_ENABLED, "Cancel", x-button_w+w, btn_y, button_w, button_h)) { + _goto_default_detail_state(); + } + } + else if (_active_schedule_state == VIEWING && _active_selected_scheduled_job) { + if (button_render(scale, BUTTON_ENABLED, "Reschedule", x-button_w+w, btn_y, button_w, button_h)) { + _active_schedule_state = RESCHEDULING_JOB; + _active_scheduling_job = *_active_selected_scheduled_job; + } + } + + s32 options_w = 230*scale; + s32 selector_w = w-options_w-(pad_between_items*2)-button_w; + + button_render(scale, BUTTON_STATIC, 0, x,y,w,h); + + x+=schedule_pad; + y+=schedule_pad; + w-=schedule_pad*2; + h-=schedule_pad*2; + + #define TILE_X(_x) (x+hour_w+(timeslot_w*_x)+1) + #define TILE_Y(_y) (y+(timeslot_h*(CDAYTORDAY(_y)))+1) + + + vec2f hovered_tile = (vec2f){(s32)((_global_mouse.x-x)/timeslot_w)-TIME_SLOTS_PER_HOUR, (s32)((_global_mouse.y-y)/timeslot_h)}; + s32 hovered_day = RDAYTOCDAY((s32)hovered_tile.y); + scheduled_job* existing_job_at_hovered_tile = get_scheduled_job_at_time(hovered_day, (s32)hovered_tile.x); + font* fnt = fnt_rd24; + + static bool dragging_tile = false; + + // newly scheduling job + if (_active_schedule_state == SCHEDULING_JOB || _active_schedule_state == RESCHEDULING_JOB) + { + for (s32 i = 0; i < _active_scheduling_job.offer.shipday_count; i++) { + scheduled_job_time scheduled_time = _active_scheduling_job.timeslots[i]; + s32 dday = CDAYTORDAY(scheduled_time.day); + renderer->render_rectangle(x+hour_w, y+(dday*timeslot_h), w-hour_w, timeslot_h, COLOR_SCHEDULE_ROW_ACTIVE); + + bool being_hovered = false; + bool valid_tile = (!existing_job_at_hovered_tile || (_active_schedule_state == RESCHEDULING_JOB && existing_job_at_hovered_tile == _active_selected_scheduled_job)); + if (valid_tile && hovered_tile.x >= 0 && hovered_tile.x < TIME_SLOTS_PER_DAY && hovered_tile.y == dday) { // If in current row. + + // Interaction. + if (_active_scheduling_job.timeslots[i].timeslot == hovered_tile.x) { + platform_set_cursor(window, CURSOR_POINTER); + being_hovered = true; + + // Check if player started to drag. + if (is_left_clicked_peak()) { // is_left_clicked_peak instead of is_left_clicked so employee selector can reset input. + dragging_tile = true; + _active_schedule_selected_job_index = i; + audio_play_sound(snd_click, AUDIO_CHANNEL_SFX_1); + } + } + } + + s32 tx = TILE_X(scheduled_time.timeslot); + s32 ty = TILE_Y(scheduled_time.day); + + bool is_dragging_current_tile = (dragging_tile && _active_schedule_selected_job_index == i); + color tile_color = (_active_schedule_selected_job_index == i) ? COLOR_SCHEDULE_TILE_HOVERED : COLOR_SCHEDULE_TILE_SELECTED; + + // Handle dragging. + if (being_hovered || is_dragging_current_tile) { + + // Get index of new timeslot. + s32 index_to_set = hovered_tile.x; + s32 possible_tile_left = find_empty_timeslot_for_day(scheduled_time.day); + s32 possible_tile_right = find_empty_timeslot_for_day_right_to_left(scheduled_time.day); + if (index_to_set < 0) index_to_set = possible_tile_left; + if (index_to_set >= TIME_SLOTS_PER_DAY) index_to_set = possible_tile_right; + + // Set new timeslot. + if (is_dragging_current_tile) { + platform_set_cursor(window, CURSOR_DRAG); + + scheduled_job* existing_job_at_tile = get_scheduled_job_at_time(scheduled_time.day, index_to_set); + if (existing_job_at_tile == _active_selected_scheduled_job) existing_job_at_tile = 0; // Make sure reschedule can be set at current location. + if (index_to_set != -1 && !existing_job_at_tile) _active_scheduling_job.timeslots[i].timeslot = index_to_set; + + if (!is_left_down()) { + dragging_tile = false; + } + + tx = TILE_X(_active_scheduling_job.timeslots[i].timeslot); + ty = TILE_Y(scheduled_time.day); + } + + // Draw duration of job. + draw_duration_of_scheduled_job_entry(scheduled_time, hour_w, tx, ty, timeslot_w, x, y, timeslot_h, tile_color); + + // Draw left and right arrow. + s32 icon_s = timeslot_w*0.7f; + s32 icon_y = ty + (timeslot_h-icon_s)/2; + s32 icon_pad = (3*scale); + if (possible_tile_left != -1 && possible_tile_left != index_to_set) { + renderer->render_image_tint(img_arrow_left_rounded, tx - icon_s - icon_pad-1, icon_y, icon_s+2, icon_s,COLOR_SCHEDULE_BORDER_THIN); + renderer->render_image_tint(img_arrow_left_rounded, tx - icon_s - icon_pad, icon_y, icon_s, icon_s,tile_color); + } + if (possible_tile_right != -1 && possible_tile_right != index_to_set) { + gl_render_set_rotation(M_PI); + renderer->render_image_tint(img_arrow_left_rounded, tx + timeslot_w + icon_pad-1, icon_y, icon_s+2, icon_s,COLOR_SCHEDULE_BORDER_THIN); + renderer->render_image_tint(img_arrow_left_rounded, tx + timeslot_w + icon_pad, icon_y, icon_s, icon_s,tile_color); + gl_render_set_rotation(0.0f); + } + } + + // Draw tile. + renderer->render_rectangle(tx, ty, timeslot_w, timeslot_h, tile_color); + + // Status icon if tile being scheduled. + s32 icon_size = timeslot_w*0.6f; + if (scheduled_time.assignee == 0) { + renderer->render_image(img_questionmark, tx+(timeslot_w/2)-(icon_size/2),ty+(timeslot_h/5),icon_size,icon_size); + } + else { + renderer->render_image(img_checkmark, tx+(timeslot_w/2)-(icon_size/2),ty+(timeslot_h/5),icon_size,icon_size); + } + } + } + + // existing jobs + { + for (s32 i = 0; i < _active_location->schedule.jobs.length; i++) { + scheduled_job* job = array_at(&_active_location->schedule.jobs, i); + + // if rescheduling, dont show the timeslots for the job being rescheduled. + if (_active_schedule_state == RESCHEDULING_JOB && _active_selected_scheduled_job == job) { + continue; + } + + for (s32 t = 0; t < job->offer.shipday_count; t++) { + scheduled_job_time scheduled_time = job->timeslots[t]; + s32 tx = TILE_X(scheduled_time.timeslot); + s32 ty = TILE_Y(scheduled_time.day); + bool is_selected_job = (_active_selected_scheduled_job == job); + bool is_selected = (is_selected_job && _active_schedule_selected_job_index == t); + bool is_inspecting_employee = false; + + if (_active_selected_scheduled_job != 0 && scheduled_time.assignee == _active_selected_scheduled_job->timeslots[_active_schedule_selected_job_index].assignee && !is_selected) { + is_inspecting_employee = true; + } + + color tc = COLOR_SCHEDULE_TILE_FIXED; + if (is_inspecting_employee) { + tc = COLOR_SCHEDULE_TILE_HIGHLIGHTED; + } + else if (is_selected) { + tc = COLOR_SCHEDULE_TILE_HOVERED; + } + if (scheduled_time.assignee == 0) tc = is_selected ? COLOR_SCHEDULE_TILE_HOVERED : COLOR_SCHEDULE_TILE_INVALID; + if (is_inspecting_employee || is_selected) { + // Draw duration of job. + draw_duration_of_scheduled_job_entry(scheduled_time, hour_w, tx, ty, timeslot_w, x, y, timeslot_h, tc); + } + + renderer->render_rectangle(tx, ty, timeslot_w, timeslot_h, tc); + if (is_selected_job && !is_selected) { + s32 dotsize = timeslot_w/3; + s32 dotoffsetx = (timeslot_w/2)-(dotsize/2); + s32 dotoffsety = timeslot_h-(timeslot_h/4)-(dotsize/2); + renderer->render_image_tint(img_dot, tx+dotoffsetx,ty+dotoffsety,dotsize,dotsize,COLOR_SCHEDULE_TILE_HOVERED); + } + + // Status icon + s32 icon_size = timeslot_w*0.6f; + if (scheduled_time.assignee == 0) { + renderer->render_image(img_questionmark, tx+(timeslot_w/2)-(icon_size/2),ty+(timeslot_h/5),icon_size,icon_size); + } + + #if 0 + color col = COLOR_SCHEDULE_TILE_FIXED; + if (is_selected_job) col = COLOR_SCHEDULE_TILE_HIGHLIGHTED; + if (is_selected) col = COLOR_SCHEDULE_TILE_HOVERED; + renderer->render_rectangle(tx, ty, timeslot_w, timeslot_h, col); + #endif + + #if 0 + // Viewing job info box. + if (is_selected) { + s32 tooltip_offset_from_tile = 15*scale; + s32 tooltip_pad = 15*scale; + s32 tooltip_h = fnt_s->px_h + (tooltip_pad*2); + s32 tooltip_x = tx + timeslot_w + tooltip_offset_from_tile; + s32 tooltip_y = ty + (timeslot_h/2)-(tooltip_h/2); + // if (tx > area.x + (area.w/2)) + + char* tooltip_text = "Maastricht -> Machester"; + s32 tooltip_text_w = renderer->calculate_text_width(fnt_s, tooltip_text); + s32 tooltip_w = tooltip_text_w + (tooltip_pad*2); + + renderer->set_render_depth(5); + button_draw_background(scale, tooltip_x,tooltip_y,tooltip_w,tooltip_h, COLOR_WHITE, COLOR_BUTTON); + renderer->render_text(fnt_s, tooltip_x+tooltip_pad, tooltip_y+tooltip_pad, tooltip_text, COLOR_TEXT); + renderer->set_render_depth(4); + } + #endif + + if (_active_schedule_state == VIEWING && mouse_interacts(tx,ty,timeslot_w,timeslot_h)) { + platform_set_cursor(window, CURSOR_POINTER); + + if (is_left_clicked()) { + scheduled_job* prev_selected_scheduled_job = _active_selected_scheduled_job; + _active_schedule_state = VIEWING; + _active_selected_scheduled_job = job; + _active_schedule_selected_job_index = t; + audio_play_sound(snd_click, AUDIO_CHANNEL_SFX_1); + + if (_active_selected_scheduled_job != prev_selected_scheduled_job) { + tag_animation = animation_create(TAG_ANIMATION_DURATION); + tag_animation.started = true; + } + + employee_selector_animation = animation_create(EMPLOYEE_SELECTOR_ANIMATION_DURATION); + employee_selector_animation.started = true; + } + } + } + } + } + + // rows + for (s32 ty = 0; ty < ROWS; ty++) + { + s32 row_y = y + (ty*timeslot_h); + + if (ty==0) { + // COLOR_SCHEDULE_ROW_ACTIVE + renderer->render_rectangle(x, y, timeslot_w*4, h, COLOR_SCHEDULE_BG); + renderer->render_rectangle(x, y, w, timeslot_h, COLOR_SCHEDULE_BG); + } + else { + // Days + { + char buf[20]; + switch(ty) { + case 1: strcpy(buf, "Mon"); break; + case 2: strcpy(buf, "Tue"); break; + case 3: strcpy(buf, "Wed"); break; + case 4: strcpy(buf, "Thu"); break; + case 5: strcpy(buf, "Fri"); break; + case 6: strcpy(buf, "Sat"); break; + case 7: strcpy(buf, "Sun"); break; + } + + s32 tw = renderer->calculate_text_width(fnt, buf); + s32 textx = x + (hour_w/2)-(tw/2); + s32 texty = row_y + (timeslot_h/2)-(fnt->px_h/2); + renderer->render_text(fnt, textx, texty, buf, COLOR_TEXT); + } + } + renderer->render_rectangle(x, row_y, w, 1, COLOR_SCHEDULE_BORDER); + } + + // cols + for (s32 tx = 0; tx < COLS; tx++) + { + s32 row_x = x + (tx*timeslot_w); + bool is_big_line = tx % 4 == 0; + if (tx>=4 || is_big_line) { + s32 liney = y; + s32 lineh = h; + if (!is_big_line) { + liney = y+timeslot_h; + lineh = h-timeslot_h; + } + renderer->render_rectangle(row_x, liney, 1, lineh, (is_big_line) ? COLOR_SCHEDULE_BORDER : COLOR_SCHEDULE_BORDER_THIN); + } + if (tx!=0 && is_big_line) { + + char buf[20]; + sprintf(buf, "%d:00", WORK_HOUR_START+((tx-1)/TIME_SLOTS_PER_HOUR)); + s32 tw = renderer->calculate_text_width(fnt, buf); + s32 textx = row_x + (hour_w/2)-(tw/2); + s32 texty = y + (timeslot_h/2)-(fnt->px_h/2); + renderer->render_text(fnt, textx, texty, buf, COLOR_TEXT); + } + } + + // outline + renderer->render_rectangle_outline(x,y,w+1,h+1,1,COLOR_SCHEDULE_BORDER); + + x -= schedule_pad; + place_detail_draw_schedule_employee_options(window, schedule_pad, scale, x + selector_w + pad_between_items, employee_select_y, options_w, employee_select_h); + place_detail_draw_schedule_employee_selector(window, schedule_pad, scale, x, employee_select_y, selector_w, employee_select_h); + + // Deselect selected job. + if (mouse_interacts(x+schedule_pad,y,w+1,h+1) && is_left_clicked()) { + if (_active_schedule_state == VIEWING) { + _active_selected_scheduled_job = 0; + } + } +} + +static void place_detail_draw_dealers(platform_window* window) +{ + float offset = scale * 40.0; + s32 item_h = 82 * 0.4 * scale; + s32 h = (area.h * 0.6 - (offset*2)) + item_h; + s32 w = (area.w * 0.9 - (offset*2)); + s32 x = offset + area.x + (area.w*0.05); + s32 pos_y = area.y + (area.w*0.12); + s32 y = pos_y; + + s32 item_count = _active_world->truck_dealers.length; + + s32 spacing = 6; + s32 logo_space_w = w / 7.0f; + s32 logo_space_h = (h-(spacing*(item_count-1))) / 3.0f; + + for (s32 i = 0; i < item_count; i++) + { + truck_dealer* d = array_at(&_active_world->truck_dealers, i); + if(button_render(scale, true, 0, x, y + (logo_space_h*i)+spacing*i, logo_space_w, logo_space_h)) active_dealer_index = i; + render_logo_at(d->logo, 40 * scale, x, y + (logo_space_h*i)+spacing*i, logo_space_w, logo_space_h); + } + + if (active_dealer_index < _active_world->truck_dealers.length) { + truck_dealer* d = array_at(&_active_world->truck_dealers, active_dealer_index); + + s32 panel_x = x + spacing + logo_space_w; + s32 panel_w = (x + w) - panel_x; + place_detail_draw_active_dealer(window, d, scale, panel_x, y, panel_w, h); + } +} + +static void place_detail_draw_resumes(platform_window* window) +{ + s32 screen_center_x = area.x + (area.w/2); + s32 screen_center_y = area.y + (area.h/2); + s32 panel_w = area.w*0.75; + s32 panel_h = area.h*0.75; + s32 panel_x = screen_center_x - (panel_w/2); + s32 panel_y = screen_center_y - (panel_h/2); + + bool resume_available = _active_location->resumes.length; + + font* fntb = fnt_rd32; + font* fnt = fnt_rd16; + + if (!resume_available) { + char* message = "No resumes available."; + s32 text_w = renderer->calculate_text_width(fnt, message); + s32 text_x = screen_center_x - (text_w/2); + s32 text_y = screen_center_y - (fnt->px_h/2); + renderer->render_text(fnt, text_x+1, text_y+1, message, COLOR_TEXT_SHADOW); + renderer->render_text(fnt, text_x, text_y, message, COLOR_TEXT); + } + else { + s32 panel_w = area.w*0.2; + s32 panel_h = area.h*0.3; + panel_x = screen_center_x - (panel_w/2); + panel_y = screen_center_y - (panel_h/2); + resume* resume = array_at(&_active_location->resumes, 0); + + float opacity = 1.0f - resume->animation.percentage; + if (opacity < 0.0f) opacity = 0.0f; + + color color_bg = COLOR_WHITE; + color_bg.a = opacity*255; + + renderer->render_image_tint(img_resume, panel_x, panel_y, panel_w, panel_h, color_bg); + + char buffer[100]; + sprintf(buffer, "Resume"); + s32 text_x = panel_x + (panel_w/2) - (renderer->calculate_text_width(fntb, buffer)/2); + s32 text_y = panel_y + (20*scale); + + color t_shadow = COLOR_TEXT_SHADOW; + t_shadow.a = opacity*255; + + renderer->render_text(fntb, text_x, text_y, buffer, t_shadow); + + text_x = panel_x + (20*scale); + text_y += fntb->px_h+(15*scale); + + sprintf(buffer, "Name: %s", resume->employee->name); + renderer->render_text(fnt, text_x, text_y, buffer, t_shadow); + + text_y += fnt->px_h + 8*scale; + sprintf(buffer, "Age: %d", resume->employee->age); + renderer->render_text(fnt, text_x, text_y, buffer, t_shadow); + + text_y += fnt->px_h + 8*scale; + sprintf(buffer, "Experience: %d years", resume->employee->experience); + renderer->render_text(fnt, text_x, text_y, buffer, t_shadow); + + text_y += fnt->px_h + 8*scale; + sprintf(buffer, "Salary: $%.2f/month", resume->employee->salary); + renderer->render_text(fnt, text_x, text_y, buffer, t_shadow); + + struct tm* curr_time = gmtime(&resume->expire_date); + text_y += fnt->px_h + 8*scale; + strftime(buffer, 50, "Offer open untill %d/%m/%Y", curr_time); + renderer->render_text(fnt, text_x, text_y, buffer, t_shadow); + text_y += fnt->px_h + 8*scale; + + s32 signature_h = ((panel_y+panel_h)-text_y)*0.6; + text_y += (10 * scale); + renderer->render_image(img_signature, text_x, text_y, signature_h*2, signature_h); + + if (!resume->animation.started) { + s32 button_w = 124 * scale; + s32 button_h = 37 * scale; + s32 pad = 20 * scale; + s32 btn_y = panel_y + panel_h + pad; + s32 btn_x = panel_x; + if (button_render(scale, true, "Hire", btn_x, btn_y, button_w, button_h)) { + audio_play_sound(snd_click2, AUDIO_CHANNEL_SFX_1); + resume->animation.started = true; + resume->hired = true; + resume->employee->id = _active_world->next_id++; + resume->employee->hire_date = _active_world->current_time; + add_employee_to_world_location(_active_location, resume->employee); + } + btn_x = panel_x + panel_w - button_w; + if (button_render(scale, true, "Decline", btn_x, btn_y, button_w, button_h)) { + audio_play_sound(snd_click3, AUDIO_CHANNEL_SFX_1); + resume->animation.started = true; + resume->hired = false; + } + } + else { + s32 pad = scale * 50; + s32 stamp_x = panel_x + pad; + s32 stamp_y = panel_y + pad; + s32 stamp_w = panel_w - (pad*2); + s32 stamp_h = panel_h - (pad*2); + if (resume->hired) { + renderer->render_image_tint(img_hired, stamp_x, stamp_y, stamp_w, stamp_h, color_bg); + } + else { + renderer->render_image_tint(img_denied, stamp_x, stamp_y, stamp_w, stamp_h, color_bg); + } + + animation_update(&resume->animation); + if (resume->animation.percentage == 1.0f) { + array_remove_at(&_active_location->resumes, 0); + } + } + } +} + +static void place_detail_draw_info(platform_window* window, tab tab) +{ + float pad = tab.scale * 10; + float x = tab.x+pad; + float y = tab.y+pad; + float w = tab.w-(pad*2); + float h = tab.h-(pad*2); + + renderer->render_set_scissor(window, x, y, w, h); + if (selected_tab_index == PLACE_DETAIL_EMPLOYEES) { + place_detail_draw_employees(window, tab, x, y, w, h); + } + else if (selected_tab_index == PLACE_DETAIL_JOBOFFERS) { + place_detail_draw_job_offers(window, tab, x, y, w, h); + } + else if (selected_tab_index == PLACE_DETAIL_GARAGE) { + place_detail_draw_trucks(window, tab, x, y, w, h); + } + + renderer->render_reset_scissor(window); +} + +static tab place_detail_draw_tab_bg(platform_window* window) +{ + float offset = scale * 40.0; + s32 cornor_size = img_button_topleft->width*(scale/2); + s32 item_h = 82 * 0.4 * scale; + s32 h = (area.h * 0.6 - (offset*2)); + s32 w = (area.w * 0.9 - (offset*2)); + s32 top_width = w - (cornor_size*2); + s32 size_height = h - (cornor_size*2); + s32 x = offset + area.x + (area.w*0.05); + s32 pos_y = area.y + (area.w*0.12); + s32 y = pos_y + item_h; + + color fill = COLOR_BUTTON_ACTIVE_TINT; + + // button_render(scale, BUTTON_STATIC, 0, panel_x + pad_x, vertical_pad + panel_y + pad_y*2 + button_h*1, button_w, button_h); + + // left + renderer->render_image_tint(img_button_left, x, y-1, cornor_size, size_height+2, fill); + + // right + renderer->render_image_tint(img_button_right, x + cornor_size + top_width, y-1, cornor_size, size_height+2, fill); + + // bottom + renderer->render_image_tint(img_button_bottomleft, x, y + size_height, cornor_size, cornor_size, fill); + renderer->render_image_tint(img_button_bottom, x + cornor_size, y + size_height, top_width, cornor_size, fill); + renderer->render_image_tint(img_button_bottomright, x + cornor_size + top_width, y + size_height, cornor_size, cornor_size, fill); + + // fill + s32 pad = cornor_size-1; + fill = COLOR_BUTTON_ACTIVE; + renderer->render_rectangle(x+pad, y-1, w-(pad*2), h-(pad*2), fill); + + return (tab){x,y,w,h, scale}; +} + +static void place_detail_push_tab(place_detail_info_state index, platform_window* window, char* text) +{ + float offset = scale * 40.0; + s32 pos_y = area.y + (area.w*0.12); + s32 item_w = 426 * 0.4 * scale; + s32 item_h = 82 * 0.4 * scale; + s32 pos_x = offset + area.x + (area.w*0.05) + (item_w*index); + bool hovered = mouse_interacts(pos_x,pos_y,item_w,item_h); + + color tint = COLOR_WHITE; + if (selected_tab_index == index) { + tint = COLOR_BUTTON_ACTIVE_TINT; + } + if (hovered) { + platform_set_cursor(window, CURSOR_POINTER); + if (is_left_clicked()) { + if (selected_tab_index != index) audio_play_sound(snd_click, AUDIO_CHANNEL_SFX_1); + selected_tab_index = index; + } + } + + renderer->render_image_tint(img_tabitem, pos_x, pos_y, item_w, item_h, tint); + + { + font* fnt = fnt_rd24; + s32 text_x = pos_x + (item_w/2) - (renderer->calculate_text_width(fnt, text)/2); + s32 text_y = pos_y + (item_h/2) - (fnt->px_h/2); + + renderer->render_text(fnt, text_x+1, text_y+1, text, COLOR_TEXT_SHADOW); + renderer->render_text(fnt, text_x, text_y, text, COLOR_TEXT); + } +} + +static tab place_detail_draw_tabs(platform_window* window) +{ + place_detail_push_tab(PLACE_DETAIL_EMPLOYEES, window, "Employees"); + place_detail_push_tab(PLACE_DETAIL_JOBOFFERS, window, "Job Offers"); + place_detail_push_tab(PLACE_DETAIL_SCHEDULE, window, "Schedule"); + place_detail_push_tab(PLACE_DETAIL_GARAGE, window, "Garage"); + return place_detail_draw_tab_bg(window); +} + +static void place_detail_draw_title(platform_window* window) +{ + char buf[200]; + if (current_detail_state == PLACE_DETAIL_SHOW_MAIN) { + strcpy(buf, _active_location->name); + } + else if (current_detail_state == PLACE_DETAIL_SHOW_RESUMES) { + strcpy(buf, "Hire new employees"); + } + else if (current_detail_state == PLACE_DETAIL_SHOW_DEALERS) { + strcpy(buf, "Dealers"); + } + else if (current_detail_state == PLACE_DETAIL_SHOW_EMPLOYEE) { + sprintf(buf, "%s, #%d", _active_employee->name, _active_employee->id); + } + else if (current_detail_state == PLACE_DETAIL_SHOW_SCHEDULE) { + if (_active_schedule_state == VIEWING) + sprintf(buf, "Schedule for %s", _active_location->name); + else { + sprintf(buf, "%s wants you to ship %s to %s", + _active_scheduling_job.offer.company->name, + _active_scheduling_job.offer.product->name, + (*(world_location**)array_at(&_active_scheduling_job.offer.connections, + _active_scheduling_job.offer.connections.length-1))->name); + } + } + + font* fnt = fnt_rd36; + float text_pad = scale * 40.0; + s32 text_x = text_pad + area.x + (area.w*0.05); + s32 text_y = text_pad + area.y + (area.w*0.05); + + // Title + { + renderer->render_text(fnt, text_x+2, text_y+2, buf, COLOR_TEXT_SHADOW); + renderer->render_text(fnt, text_x, text_y, buf, COLOR_TEXT); + } + + color tags_text_color = AN_LI_TINT(COLOR_TEXT, tag_animation); + color tag_icon_color = AN_LI_TINT(COLOR_WHITE, tag_animation); + + // Tags + #define PUSH_TAG(_text, _icon){\ + char* text = _text;\ + s32 textw = renderer->calculate_text_width(fnt, text);\ + text_x -= textw;\ + renderer->render_text(fnt, text_x, text_y, text, tags_text_color);\ + text_x -= icon_s + pad_between_icon_and_text;\ + renderer->render_image_tint(_icon, text_x, text_y,icon_s,icon_s, tag_icon_color);\ + text_x -= pad_between_tags;} + + text_y += (fnt->px_h/2); + fnt = fnt_rd20; + text_y -= (fnt->px_h/2); + s32 icon_s = fnt->px_h; + s32 pad_between_icon_and_text = 5*scale; + s32 pad_between_tags = 20*scale; + + #define ANIMATION_OFFSET (30*scale) + text_x = area.x + (area.w*0.05) + (area.w*0.9) - text_pad + (ANIMATION_OFFSET-(ANIMATION_OFFSET*tag_animation.percentage)); + #undef ANIMATION_OFFSET + + job_offer* job_to_inspect = 0; + if (current_detail_state == PLACE_DETAIL_SHOW_SCHEDULE && _active_schedule_state != VIEWING) + job_to_inspect = &_active_scheduling_job.offer; + if (current_detail_state == PLACE_DETAIL_SHOW_SCHEDULE && _active_schedule_state == VIEWING && _active_selected_scheduled_job != 0) + job_to_inspect = &_active_selected_scheduled_job->offer; + + if (current_detail_state == PLACE_DETAIL_SHOW_SCHEDULE && job_to_inspect) { + // price + { + char pricebuf[25]; + sprintf(pricebuf, "$%d/trip", job_to_inspect->reward); + PUSH_TAG(pricebuf, img_coins); + } + + // distance + { + char pricebuf[25]; + sprintf(pricebuf, "%.0fkm", job_to_inspect->total_distance); + PUSH_TAG(pricebuf, img_road); + } + + // duration + { + char pricebuf[25]; + sprintf(pricebuf, "%.0fh-%.0fh", job_to_inspect->duration_sec_min/3600.0f, job_to_inspect->duration_sec_max/3600.0f); + PUSH_TAG(pricebuf, img_timer); + } + + if (_active_schedule_state == VIEWING) { + char namebuf[MAX_WORLD_LOCATION_NAME_LENGTH*3]; + job_endpoints endpoints = job_offer_get_endpoints(job_to_inspect); + sprintf(namebuf, "%s to %s", endpoints.source->name, endpoints.dest->name); + PUSH_TAG(namebuf, img_globe); + } + } + else if (current_detail_state == PLACE_DETAIL_SHOW_EMPLOYEE) { + // Original location + { + char pricebuf[100]; + sprintf(pricebuf, "From: %s", get_world_location_by_id(_active_world, _active_employee->original_location_id)->name); + PUSH_TAG(pricebuf, img_city); + } + + // Current location or active trip destination + { + char pricebuf[100]; + if (_active_employee->current_location_id != INVALID_ID) { + sprintf(pricebuf, "Currently at: %s", get_world_location_by_id(_active_world, _active_employee->current_location_id)->name); + } + else if (_active_employee->active_job_id != INVALID_ID) { + active_job* j = get_active_job_by_id(_active_world, _active_employee->active_job_id); + job_endpoints endpoints = job_offer_get_endpoints(&j->offer); + sprintf(pricebuf, "Driving to: %s", endpoints.dest->name); + } + PUSH_TAG(pricebuf, img_location_pin); + } + } + + animation_update(&tag_animation); +} + +static void place_detail_draw_panel(platform_window* window) +{ + s32 panel_w = area.w*0.9; + s32 panel_h = area.h*0.7; + s32 panel_x = area.x + area.w*0.05; + s32 panel_y = area.y + area.w*0.05; + panel_render(scale, panel_x, panel_y, panel_w, panel_h); + + // back button + { + s32 back_h = img_back->height * scale/2; + s32 back_w = img_back->width * scale/2; + s32 back_x = panel_x + (panel_w/10.0f); + 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 (current_detail_state == PLACE_DETAIL_SHOW_MAIN) { + game_set_active_scene(GAME_STATE_WORLD_MAP); + } + else { + current_detail_state = PLACE_DETAIL_SHOW_MAIN; + } + _goto_default_detail_state(); + } + } + + if (mouse_interacts(panel_x,panel_y,panel_w,panel_h)) { + reset_left_click(); + } + else if (is_left_clicked()) { + _goto_default_detail_state(); + audio_play_sound(snd_click, AUDIO_CHANNEL_SFX_1); + game_set_active_scene(GAME_STATE_WORLD_MAP); + } +} + +void place_detail_scene_render(platform_window* window) +{ + renderer->set_render_depth(5); + world_map_draw_info_panel(window, false); + + renderer->set_render_depth(4); + if (current_detail_state == PLACE_DETAIL_SHOW_MAIN) { + tab t = place_detail_draw_tabs(window); + place_detail_draw_info(window, t); + + if (selected_tab_index == PLACE_DETAIL_SCHEDULE) { + selected_tab_index = PLACE_DETAIL_SHOW_MAIN; + current_detail_state = PLACE_DETAIL_SHOW_SCHEDULE; + } + } + if (current_detail_state == PLACE_DETAIL_SHOW_RESUMES) { + place_detail_draw_resumes(window); + } + if (current_detail_state == PLACE_DETAIL_SHOW_DEALERS) { + place_detail_draw_dealers(window); + } + if (current_detail_state == PLACE_DETAIL_SHOW_EMPLOYEE) { + place_detail_draw_selected_employee(window); + } + if (current_detail_state == PLACE_DETAIL_SHOW_SCHEDULE) { + place_detail_draw_schedule(window); + } + + renderer->set_render_depth(3); + place_detail_draw_title(window); + + renderer->set_render_depth(2); + place_detail_draw_panel(window); + + renderer->set_render_depth(1); + menu_draw_background(window); +} + +void place_detail_scene_update(platform_window* window) +{ + // world_update(window, _active_world, false); + + if (keyboard_is_key_pressed(KEY_ESCAPE)) { + if (current_detail_state == PLACE_DETAIL_SHOW_MAIN) { + game_set_active_scene(GAME_STATE_WORLD_MAP); + } + else { + current_detail_state = PLACE_DETAIL_SHOW_MAIN; + } + _goto_default_detail_state(); + } +} + +void place_detail_scene_destroy() +{ + +} \ No newline at end of file diff --git a/src/scenes/save_state_select.c b/src/scenes/save_state_select.c new file mode 100644 index 0000000..1db5d11 --- /dev/null +++ b/src/scenes/save_state_select.c @@ -0,0 +1,105 @@ + +void save_state_select_scene_init() +{ + +} + +static bool push_save_state_button(float scale, bool enabled, s32 x, s32 y, s32 size) +{ + if (enabled) { + button_render(scale, true, 0, x, y, size, size); + } + else { + 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 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; +} + +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++) + push_save_state_button(scale, 0, panel_x + horizontal_pad + (panel_item_size*i) + + (spacing*i), panel_y + horizontal_pad, panel_item_size); + + // bottom row + for (s32 i = 0; i < 3; i++) + 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); + + // 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)) { + 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)) { + game_set_active_scene(GAME_STATE_MENU); + } +} + +void save_state_select_scene_destroy() +{ + +} \ No newline at end of file diff --git a/src/scenes/settings_scene.c b/src/scenes/settings_scene.c new file mode 100644 index 0000000..23655d3 --- /dev/null +++ b/src/scenes/settings_scene.c @@ -0,0 +1,152 @@ +enum settings_state +{ + SETTINGS_AUDIO, + SETTINGS_DISPLAY, + SETTINGS_KEYBINDINGS, +}; + +enum settings_state settings_state = SETTINGS_AUDIO; + +void settings_scene_init() { + +} + +static void draw_display_settings(s32 x, s32 y, s32 w, s32 h) +{ + s32 option_spacing = 20*scale; + s32 checkbox_s = 30*scale; + s32 checkbox_offset = ((checkbox_s-fnt_rd24->px_h)/2); + s32 text_y = y + option_spacing; + s32 text_x = x + checkbox_s + option_spacing; + + #define PUSH_DISPLAY_OPTION(_str, _opt)\ + renderer->render_text(fnt_rd24, text_x, text_y, _str, COLOR_TEXT);\ + if (button_draw_background(scale, x,text_y-checkbox_offset, checkbox_s, checkbox_s, COLOR_WHITE, COLOR_BUTTON)) {\ + if (is_left_clicked()) { \ + _opt = !_opt;\ + if (&_opt == &option_vsync) platform_toggle_vsync(main_window, option_vsync);\ + if (&_opt == &option_fullscreen) platform_toggle_fullscreen(main_window, option_fullscreen);\ + audio_play_sound(snd_click, AUDIO_CHANNEL_SFX_1);\ + }\ + }\ + if (_opt) {\ + s32 tw = renderer->calculate_text_width(fnt_rd24, "X");\ + renderer->render_text(fnt_rd24, x + checkbox_s/2 - tw/2,text_y-checkbox_offset + checkbox_s/2 - fnt_rd24->px_h/2, "X", COLOR_TEXT);\ + }\ + text_y += (checkbox_s + option_spacing); + + PUSH_DISPLAY_OPTION("Vsync", option_vsync); + PUSH_DISPLAY_OPTION("Fullscreen", option_fullscreen); +} + +static void draw_audio_settings(s32 x, s32 y, s32 w, s32 h) +{ + s32 option_spacing = 20*scale; + s32 slider_h = 30*scale; + s32 slider_offset = ((slider_h-fnt_rd24->px_h)/2); + s32 slider_w = w/2; + s32 slider_x = x + slider_w; + s32 text_y = y + option_spacing; + + // Music + #define PUSH_VOLUME_OPTION(_text, _opt)\ + {\ + static bool is_editing = false;\ + renderer->render_text(fnt_rd24, x, text_y, _text, COLOR_TEXT);\ + s32 slider_y = text_y - slider_offset;\ + float percentage = is_editing ? ((_global_mouse.x - (slider_x+10*scale)) / (float)(slider_w-20*scale)) : _opt;\ + if (percentage < 0.0f) percentage = 0.0f;\ + if (percentage > 1.0f) percentage = 1.0f;\ + button_draw_background_percentage(scale, slider_x,slider_y,slider_w,slider_h, COLOR_WHITE, COLOR_BUTTON, percentage, COLOR_WHITE);\ + bool hovered = mouse_interacts(slider_x,slider_y,slider_w,slider_h);\ + if (hovered && is_left_clicked()) is_editing = true;\ + if (is_editing) {\ + audio_set_mixer_volume(AUDIO_CHANNEL_SFX_1, volume_sfx*volume_global);\ + audio_set_mixer_volume(AUDIO_CHANNEL_SFX_2, volume_sfx*volume_global);\ + audio_set_music_volume(volume_music*volume_global);\ + }\ + if (is_editing) {\ + if (!is_left_down()) { is_editing = false; audio_play_sound(snd_click, AUDIO_CHANNEL_SFX_1); }\ + _opt = percentage;\ + }\ + text_y += (slider_h + option_spacing);\ + } + + PUSH_VOLUME_OPTION("Global", volume_global); + PUSH_VOLUME_OPTION("Music", volume_music); + PUSH_VOLUME_OPTION("Interface", volume_sfx); +} + +static void settings_draw_options(platform_window* window) +{ + s32 screen_center_x = area.x + (area.w/2); + s32 screen_center_y = area.y + (area.h/2); + + s32 panel_w = 400 * scale; + s32 panel_h = 500 * scale; + 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); + + // Buttons + s32 button_pad = 10*scale; + s32 button_w = (panel_w-(button_pad*4))/3; + s32 button_h = 37*scale; + s32 button_y = panel_y + button_pad*1.3f; + s32 button_x = panel_x + button_pad; + + if (button_render(scale, BUTTON_ENABLED, "Audio", button_x, button_y, button_w, button_h)) + { + settings_state = SETTINGS_AUDIO; + } + + if (button_render(scale, BUTTON_ENABLED, "Display", button_x + (button_w + button_pad), button_y, button_w, button_h)) + { + settings_state = SETTINGS_DISPLAY; + } + + if (button_render(scale, BUTTON_ENABLED, "Keybindings", button_x + (button_w + button_pad)*2, button_y, button_w, button_h)) + { + settings_state = SETTINGS_KEYBINDINGS; + } + + s32 detail_pad = 25*scale; + s32 detail_w = panel_w - (detail_pad*2)-(button_pad*2); + s32 detail_h = panel_h - (detail_pad*2)-(button_pad*3)-button_h; + s32 detail_x = panel_x+detail_pad+button_pad; + s32 detail_y = button_y + button_h + detail_pad+button_pad; + + if (settings_state == SETTINGS_AUDIO) { + draw_audio_settings(detail_x, detail_y, detail_w, detail_h); + } + else if (settings_state == SETTINGS_DISPLAY) { + draw_display_settings(detail_x, detail_y, detail_w, detail_h); + } + + // back button + { + s32 back_h = img_back->height * scale/2; + s32 back_w = img_back->width * scale/2; + s32 back_x = panel_x + (panel_w/10); + s32 back_y = panel_y + panel_h - (back_h/2) - 1; + + if (push_back_button(scale, back_x, back_y, back_w, back_h)) { + game_set_active_scene(GAME_STATE_MENU); + } + } +} + +void settings_scene_render(platform_window* window) { + menu_draw_background(window); + settings_draw_options(window); +} + +void settings_scene_update(platform_window* window) { + if (keyboard_is_key_pressed(KEY_ESCAPE)) { + game_set_active_scene(GAME_STATE_MENU); + } +} + +void settings_scene_destroy() { + +} \ No newline at end of file diff --git a/src/scenes/world_map.c b/src/scenes/world_map.c new file mode 100644 index 0000000..eb106cb --- /dev/null +++ b/src/scenes/world_map.c @@ -0,0 +1,1094 @@ +typedef enum t_world_map_scene_state +{ + WORLD_SCENE_STATE_IDLE, + WORLD_SCENE_STATE_PURCHASE_LOCATION, + WORLD_SCENE_STATE_LOG, + WORLD_SCENE_STATE_INSIGHTS, + WORLD_SCENE_STATE_INVEST, +} world_map_scene_state; + +typedef union t_world_map_scene_data +{ + world_location* location_to_purchase; +} world_map_scene_data; + +s32 insights_selected_year_index = 0; // years since first year. +world* _active_world = 0; +world_map_scene_state scene_state = WORLD_SCENE_STATE_IDLE; +world_map_scene_data scene_data = {0}; +active_job_ref currently_viewing_active_job = {0,0,0}; + +animation log_button_flash_animation = {0,0,0,1}; + +void place_detail_show_employee_detail(employee* emp); +void place_detail_show_schedule_with_highlighted_job(world_location* loc, scheduled_job* job, scheduled_job_time job_time); + +void world_map_set_active_world(world* world) +{ + _active_world = world; +} + +void world_map_scene_init() +{ + +} + +static bool push_info_panel_button(float scale, image* img, s32 x, s32 y, s32 size, bool enabled, bool flashing) +{ + button_type type = (enabled ? ((flashing && log_button_flash_animation.percentage >= 0.5f) ? BUTTON_HIGHLIGHTED : BUTTON_ENABLED) : BUTTON_DISABLED); + bool result = button_render(scale, type, 0, x, y, size, size); + + float img_size = size / 2; + renderer->render_image(img, x+(img_size/2), y + (img_size/2), img_size, img_size); + return result; +} + +static void world_map_draw_speed_panel(platform_window* window, bool enabled) +{ + s32 screen_center_x = area.x + (area.w/2); + + s32 panel_h = 60 * scale; + s32 panel_w = 180 * scale; + + float btn_size = 23 * scale; + float pad_top = 6 * scale; + s32 panel_x = screen_center_x - (panel_w/2); + s32 panel_y = area.y + area.h - panel_h - (100 * scale); + s32 text_y = panel_y + pad_top; + float side_btn_offset = pad_top; + + // Background panel + panel_render(scale, panel_x, panel_y, panel_w, panel_h); + + // Button decrease simulation speed + if (push_info_panel_button(scale, img_arrow_left, screen_center_x - (panel_w/2) + side_btn_offset, text_y, btn_size, enabled, false)) { + _active_world->simulation_speed/=2; + } + // Button increase simulation speed + if (push_info_panel_button(scale, img_arrow_right, screen_center_x + (panel_w/2) - btn_size - side_btn_offset, text_y, btn_size, enabled, false)) { + _active_world->simulation_speed*=2; + if (_active_world->simulation_speed == 0) _active_world->simulation_speed = 1; + } + + // Validate new simulation speed + if (_active_world->simulation_speed < MIN_SIMULATION_SPEED) _active_world->simulation_speed = MIN_SIMULATION_SPEED; + if (_active_world->simulation_speed > MAX_SIMULATION_SPEED) _active_world->simulation_speed = MAX_SIMULATION_SPEED; + + // Draw the current speed, or pause icon when paused + if (_active_world->simulation_speed != 0) { + font* fnt = fnt_rd20; + + char buf[10]; + sprintf(buf, "%dx", _active_world->simulation_speed); + + s32 tw = renderer->calculate_text_width(fnt, buf); + s32 textx = panel_x + (panel_w/2)-(tw/2); + renderer->render_text(fnt, textx, text_y+(5*scale), buf, COLOR_TEXT); + } + else { + s32 icon_pad = 4*scale; + s32 icon_s = btn_size-(icon_pad*2); + renderer->render_image(img_pause, panel_x+(panel_w/2)-(icon_s/2), text_y+icon_pad, icon_s, icon_s); + } + + if (mouse_interacts(panel_x,panel_y,panel_w,panel_h)) { + reset_left_click(); + } +} + +static void world_map_draw_info_panel(platform_window* window, bool enabled) +{ + world_map_draw_speed_panel(window, enabled); + + s32 screen_center_x = area.x + (area.w/2); + + float vertical_pad = 20 * scale; + + s32 panel_h = 120 * scale; + s32 panel_w = 280 * scale; + s32 panel_x = screen_center_x - (panel_w/2); + s32 panel_y = area.y + area.h - panel_h - (10 * scale); + + // Draw background panel + panel_render(scale, panel_x, panel_y, panel_w, panel_h); + + if (enabled) { + char txt_buf[50]; + char* text = txt_buf; + s32 text_y; + s32 text_x; + font* font_big = fnt_rd32; + s32 game_title_width; + + // Draw current date and time + { + strftime(txt_buf, 50, "%H:%M %d/%m/%Y", &_active_world->current_time); + game_title_width = renderer->calculate_text_width(font_big, text); + text_y = panel_y + vertical_pad; + text_x = screen_center_x - (game_title_width/2); + + renderer->render_text(font_big, text_x+1, text_y+1, text, COLOR_TEXT_SHADOW); + renderer->render_text(font_big, text_x, text_y, text, COLOR_TEXT); + } + + // Draw money + { + sprintf(txt_buf, "$%.0f", _active_world->money); + text = txt_buf; + font* font_medium = fnt_rd24; + game_title_width = renderer->calculate_text_width(font_medium, text); + text_y = text_y + font_big->px_h + (10 * scale); + text_x = screen_center_x - (game_title_width/2); + + renderer->render_text(font_medium, text_x+1, text_y+1, text, COLOR_TEXT_SHADOW); + renderer->render_text(font_medium, text_x, text_y, text, COLOR_TEXT); + } + + float btn_size = 35 * scale; + float btn_spacing = 2 * scale; + float btn_y = panel_y + panel_h - btn_size - vertical_pad/2; + float total_btn_w = (btn_size+btn_spacing)*4-btn_spacing; + + if (log_button_flash_animation.percentage == 1.0f) log_button_flash_animation = animation_create(1000); + log_button_flash_animation.started = true; + animation_update(&log_button_flash_animation); + + // Graph button + if (push_info_panel_button(scale, img_graph, screen_center_x - (total_btn_w/2) + (btn_size+btn_spacing)*0, btn_y, btn_size, enabled, false)) { + scene_state = (scene_state == WORLD_SCENE_STATE_INSIGHTS) ? WORLD_SCENE_STATE_IDLE : WORLD_SCENE_STATE_INSIGHTS; + } + + // Event log button + if (push_info_panel_button(scale, img_list, screen_center_x - (total_btn_w/2) + (btn_size+btn_spacing)*1, btn_y, btn_size, enabled, _active_world->log.has_unread_messages)) { + scene_state = (scene_state == WORLD_SCENE_STATE_LOG) ? WORLD_SCENE_STATE_IDLE : WORLD_SCENE_STATE_LOG; + } + + // Bank button + push_info_panel_button(scale, img_bank, screen_center_x - (total_btn_w/2) + (btn_size+btn_spacing)*2, btn_y, btn_size, enabled, false); + + // Event log button + if (push_info_panel_button(scale, img_list, screen_center_x - (total_btn_w/2) + (btn_size+btn_spacing)*3, btn_y, btn_size, enabled, _active_world->log.has_unread_messages)) { + scene_state = (scene_state == WORLD_SCENE_STATE_INVEST) ? WORLD_SCENE_STATE_IDLE : WORLD_SCENE_STATE_INVEST; + } + } + else { + s32 icon_s = panel_h/2; + renderer->render_image(img_pause, panel_x+(panel_w/2)-(icon_s/2), panel_y+(panel_h/2)-(icon_s/2), icon_s, icon_s); + } + + if (mouse_interacts(panel_x,panel_y,panel_w,panel_h)) { + reset_left_click(); + } +} + +static void _insights_draw_grid(float scale, bool invalid_location, money_data_collection* collection, s32 grid_rows, s32 grid_cols, s32 textpad, s32 x, s32 y, s32 w, s32 h, s32 width_per_item, s32 height_per_item) +{ + font* fnt = fnt_rd16; + font* fnt_title = fnt_rd24; + + for (s32 i = 0; i < MONTHS_IN_YEAR; i++) { + if (!collection) break; + money_data data = collection->months[i]; + if (isnan(data.total_income)) continue; + + char textbuf[50]; + #define PUSH_VAL(_index, _val, _neg)\ + sprintf(textbuf, "$%.0f", fabs(_val));\ + renderer->render_text(fnt, x + ((i+3)*width_per_item) + (textpad/2), y + (_index*height_per_item)+(height_per_item/2)-(fnt->px_h/2), textbuf, (_neg) ? COLOR_TEXT_NEGATIVE : COLOR_TEXT); + + PUSH_VAL(1, data.income_from_trips, false); + PUSH_VAL(2, data.expenses_from_utility, true); + PUSH_VAL(3, data.expenses_from_healthcare, true); + PUSH_VAL(4, data.expenses_from_repairs, true); + PUSH_VAL(5, data.expenses_from_fuel, true); + PUSH_VAL(6, data.expenses_from_employees, true); + PUSH_VAL(7, data.expenses_from_trucks, true); + // ADD NEW ENTRY HERE + PUSH_VAL(9, data.total_income, false); + PUSH_VAL(10, data.total_expenses, true); + PUSH_VAL(11, data.total_profit, data.total_profit < 0.0f); + } + + renderer->render_rectangle(x, y, width_per_item*3, h, COLOR_SCHEDULE_BG); + renderer->render_rectangle(x, y, w, height_per_item, COLOR_SCHEDULE_BG); + + for (s32 tx = 3; tx < grid_cols; tx++) { + s32 posx = x + tx * width_per_item; + renderer->render_rectangle(posx,y,1,h, COLOR_SCHEDULE_BORDER_THIN); + + char buf[50]; + buf[0] = 0; + + switch(tx-2) { + case 1: strcpy(buf, "Jan"); break; + case 2: strcpy(buf, "Feb"); break; + case 3: strcpy(buf, "Mar"); break; + case 4: strcpy(buf, "Apr"); break; + case 5: strcpy(buf, "May"); break; + case 6: strcpy(buf, "Jun"); break; + case 7: strcpy(buf, "Jul"); break; + case 8: strcpy(buf, "Aug"); break; + case 9: strcpy(buf, "Sep"); break; + case 10: strcpy(buf, "Oct"); break; + case 11: strcpy(buf, "Nov"); break; + case 12: strcpy(buf, "Dec"); break; + default: break; + } + + s32 textw = renderer->calculate_text_width(fnt_title, buf); + renderer->render_text(fnt_title, posx + (width_per_item/2)-(textw/2), y+(height_per_item/2)-(fnt_title->px_h/2), buf, COLOR_TEXT); + } + + for (s32 ty = 0; ty < grid_rows; ty++) { + s32 posy = y + ty * height_per_item; + renderer->render_rectangle(x,posy,w,1, COLOR_SCHEDULE_BORDER_THIN); + + char buf[50]; + buf[0] = 0; + + switch(ty) { + case 1: strcpy(buf, "Income"); break; + case 2: strcpy(buf, "Utility"); break; + case 3: strcpy(buf, "Employee Benefits"); break; + case 4: strcpy(buf, "Repairs"); break; + case 5: strcpy(buf, "Fuel"); break; + case 6: strcpy(buf, "Salaries"); break; + case 7: strcpy(buf, "Trucks"); break; + // ADD NEW ENTRY HERE + case 9: strcpy(buf, "Total Income"); break; + case 10: strcpy(buf, "Total Expenses"); break; + case 11: strcpy(buf, "Total Profit"); break; + default: break; + } + + renderer->render_text(fnt_title, x + textpad, posy+(height_per_item/2)-(fnt_title->px_h/2), buf, COLOR_TEXT); + } + + if (!collection) { + char* txtbuf = invalid_location ? "Invalid location" : "No data for time period"; + s32 textw = renderer->calculate_text_width(fnt_title, txtbuf); + s32 textx = x + (width_per_item*9) - (textw/2); + s32 texty = y + (h/2)-(fnt_title->px_h/2); + renderer->render_text(fnt_title, textx, texty, txtbuf, COLOR_TEXT); + } + + renderer->render_rectangle_outline(x,y,w,h, 1, COLOR_SCHEDULE_BORDER); +} + +static void _insights_draw_chart(platform_window*window, bool invalid_location, float scale, money_data_collection* collection, s32 grid_rows, s32 grid_cols, s32 textpad, s32 x, s32 y, s32 w, s32 h, s32 width_per_item, s32 height_per_item) +{ + font* fnt = fnt_rd16; + font* fnt_big = fnt_rd24; + + s32 xaxis_height = fnt_big->px_h; + h -= xaxis_height; + + s32 max_val = 0; + s32 min_val = INT_MAX; + for (s32 i = 0; i < MONTHS_IN_YEAR; i++) { + if (!collection) break; + money_data data = collection->months[i]; + + #define CHECK_MIN_MAX(_val)\ + if (_val > max_val) max_val = _val;\ + if (_val < min_val) min_val = _val; + + CHECK_MIN_MAX(data.income_from_trips); + CHECK_MIN_MAX(data.expenses_from_utility); + CHECK_MIN_MAX(data.expenses_from_healthcare); + CHECK_MIN_MAX(data.expenses_from_repairs); + CHECK_MIN_MAX(data.expenses_from_fuel); + CHECK_MIN_MAX(data.expenses_from_employees); + CHECK_MIN_MAX(data.expenses_from_trucks); + // ADD NEW ENTRY HERE + CHECK_MIN_MAX(data.total_income); + CHECK_MIN_MAX(data.total_expenses); + CHECK_MIN_MAX(data.total_profit); + } + + if (min_val == INT_MAX) min_val = 0; + + s32 steps = 10; + s32 step_size = 0; + + // Round min and max to nearest 10k + { + s32 max = max_val >= abs(min_val) ? max_val : abs(min_val); + s32 round_to = 10000; + if (max > 10000) round_to = 10000; + if (max > 100000) round_to = 100000; + if (max > 1000000) round_to = 1000000; + if (max > 10000000) round_to = 10000000; + if (max > 100000000) round_to = 100000000; + + max_val = max_val + round_to - max_val % round_to; + if (min_val > round_to) min_val = min_val - min_val % round_to; + else min_val = min_val - round_to - (min_val%round_to); + + // make sure negative and positive y-axis is symmetrical + if (max_val > abs(min_val)) { + min_val = -max_val; + } + else if (max_val < abs(min_val)) { + max_val = abs(min_val); + } + + step_size = (max_val-min_val)/steps; + } + + static bool enabled_categories[10] = {1,1,1,1,1,1,1,1,1,1}; + + color colors[] = { + rgb(87, 82, 208), + rgb(3, 118, 247), + rgb(54, 166, 214), + rgb(50, 140, 250), + rgb(90, 196, 248), + rgb(78, 213, 95), + rgb(252, 200, 3), + rgb(249, 146, 5), + rgb(249, 59, 47), + rgb(249, 45, 82), + }; + + char* legenda_items[] = { + "Income", + "Utility", + "Employee Benefits", + "Repairs", + "Fuel", + "Salaries", + "Trucks", + // ADD NEW ENTRY HERE + "Total Income", + "Total Expenses", + "Total Profit", + }; + + // Legenda + { + s32 text_spacing = 10*scale; + s32 outline_spacing = text_spacing/2; + s32 _index = 0; + + #define PUSH_LEGENDA_ITEM(_text){\ + s32 yy = y+((fnt_big->px_h+text_spacing)*_index);\ + s32 totalw = width_per_item*3;\ + bool hovered = (_global_mouse.x >= x-outline_spacing && _global_mouse.x <= x+totalw+outline_spacing\ + && _global_mouse.y >= yy-outline_spacing && _global_mouse.y <= yy + fnt_big->px_h+outline_spacing);\ + if (hovered) {platform_set_cursor(window, CURSOR_POINTER); if (is_left_clicked())\ + { enabled_categories[_index] = !enabled_categories[_index]; audio_play_sound(snd_click, AUDIO_CHANNEL_SFX_1); }}\ + renderer->render_rectangle(x,yy,fnt_big->px_h, fnt_big->px_h, enabled_categories[_index] ? colors[_index] : LEGENDA_COLOR_DISABLED);\ + if (hovered) renderer->render_rectangle(x-outline_spacing,yy-outline_spacing,totalw+(text_spacing),fnt_big->px_h+(text_spacing), LEGENDA_HOVER_BACKGROUND_COLOR);\ + renderer->render_text(fnt_big,x+fnt_big->px_h+text_spacing,yy,_text,COLOR_TEXT);_index++;} + + for (s32 i = 0; i < sizeof(legenda_items)/sizeof(char*); i++) { + PUSH_LEGENDA_ITEM(legenda_items[i]); + } + } + + // Graph y axis + { + s32 linex = x + 4*width_per_item; + + float step_h = (h)/(float)steps; + for (s32 i = 0; i < steps+1; i++) { + char buf[20]; + s32 val = max_val - (step_size*i); + sprintf(buf, "%d", val); + s32 textw = renderer->calculate_text_width(fnt, buf); + renderer->render_text(fnt, x+(width_per_item*4)-textw-(10*scale), y+(step_h*i)-(fnt->px_h/2), buf, COLOR_TEXT); + renderer->render_rectangle(linex, y+h*(i/(float)steps), width_per_item*(MONTHS_IN_YEAR-1), 1, LEGENDA_SUB_COLOR_DISABLED); + } + + { + renderer->render_rectangle(linex, y + h - (h*1.0f), width_per_item*(MONTHS_IN_YEAR-1), 1, LEGENDA_COLOR_DISABLED); + renderer->render_rectangle(linex, y + h - (h*0.5f), width_per_item*(MONTHS_IN_YEAR-1), 1, LEGENDA_COLOR_DISABLED); + renderer->render_rectangle(linex, y + h - (h*0.0f), width_per_item*(MONTHS_IN_YEAR-1), 1, LEGENDA_COLOR_DISABLED); + } + } + + // Graph x axis + for (s32 i = 0; i < MONTHS_IN_YEAR; i++) { + char buf[50]; + buf[0] = 0; + + switch(i+1) { + case 1: strcpy(buf, "Jan"); break; + case 2: strcpy(buf, "Feb"); break; + case 3: strcpy(buf, "Mar"); break; + case 4: strcpy(buf, "Apr"); break; + case 5: strcpy(buf, "May"); break; + case 6: strcpy(buf, "Jun"); break; + case 7: strcpy(buf, "Jul"); break; + case 8: strcpy(buf, "Aug"); break; + case 9: strcpy(buf, "Sep"); break; + case 10: strcpy(buf, "Oct"); break; + case 11: strcpy(buf, "Nov"); break; + case 12: strcpy(buf, "Dec"); break; + default: break; + } + s32 textw = renderer->calculate_text_width(fnt,buf); + + renderer->render_text(fnt, x+(width_per_item*(i+4))-(textw/2), y+h+(10*scale), buf, COLOR_TEXT); + } + + // Graph data + { + s32 total_diff = max_val-min_val; + if (total_diff == 0) total_diff = 1; + + s32 dot_size = 4*scale; + s32 dot_offset =dot_size/2; + s32 _index = 0; + //if (min_val < 0) min_val = 0; + + #define DRAW_LINES_FOR_DATA(_var){\ + s32 last_dot_x = 0;\ + s32 last_dot_y = 0;\ + for (s32 i = 0; i < MONTHS_IN_YEAR; i++) {\ + if (!collection) break;\ + if (!enabled_categories[_index]) break;\ + money_data data = collection->months[i];\ + if (isnan(data.total_income)) continue;\ + s32 val_diff = (_var) - min_val;\ + s32 dot_x = x + (i+4)*width_per_item;\ + s32 dot_y = y + h - (h*(val_diff/(float)total_diff));\ + renderer->render_image_tint(img_dot, dot_x-dot_offset, dot_y-dot_offset, dot_size, dot_size, colors[_index]);\ + if (mouse_interacts(dot_x-dot_offset, dot_y-dot_offset, dot_size, dot_size)) {\ + reset_left_click();\ + s32 info_x = dot_x;\ + s32 info_y = dot_y+(dotsize/2);\ + char info_txt[50];\ + sprintf(info_txt, "%s: $%.0f", legenda_items[_index], _var);\ + show_tooltip(info_x, info_y, info_txt);\ + }\ + if (last_dot_x != 0) {\ + renderer->render_line(dot_x, dot_y, last_dot_x, last_dot_y, 1, colors[_index]);\ + }\ + last_dot_x = dot_x;\ + last_dot_y = dot_y;\ + }_index++;\ + } + + DRAW_LINES_FOR_DATA(data.income_from_trips); + DRAW_LINES_FOR_DATA(data.expenses_from_utility); + DRAW_LINES_FOR_DATA(data.expenses_from_healthcare); + DRAW_LINES_FOR_DATA(data.expenses_from_repairs); + DRAW_LINES_FOR_DATA(data.expenses_from_fuel); + DRAW_LINES_FOR_DATA(data.expenses_from_employees); + DRAW_LINES_FOR_DATA(data.expenses_from_trucks); + // ADD NEW ENTRY HERE + DRAW_LINES_FOR_DATA(data.total_income); + DRAW_LINES_FOR_DATA(data.total_expenses); + DRAW_LINES_FOR_DATA(data.total_profit); + } + + if (!collection) { + char* txtbuf = invalid_location ? "Invalid location" : "No data for time period"; + s32 textw = renderer->calculate_text_width(fnt_big, txtbuf); + s32 textx = x + (width_per_item*9) - (textw/2); + s32 texty = y + (h/2)-(fnt_big->px_h/2); + renderer->render_text(fnt_big, textx, texty, txtbuf, COLOR_TEXT); + } +} + +static void world_map_draw_insights(platform_window* window) +{ + s32 w = area.w*0.9; + s32 h = area.h*0.7; + s32 x = area.x + area.w*0.05; + s32 y = area.y + area.w*0.05; + s32 w_orig = w; + s32 h_orig = h; + s32 x_orig = x; + s32 y_orig = y; + panel_render(scale, x, y, w, h); + + #define GRID_ROWS ((sizeof(money_data)/sizeof(float))+2) + #define GRID_COLS (MONTHS_IN_YEAR+3) + + s32 button_row_h = 44*scale; + s32 btn_size = 34*scale; + s32 spacing = 5*scale; + + s32 pad = 40*scale; + s32 halfpad = pad/2; + s32 textpad = 20*scale; + + w -= pad*2; + h -= pad*2; + + h -= button_row_h+spacing; + + s32 width_per_item = (w/GRID_COLS); + s32 height_per_item = (h/GRID_ROWS); + s32 new_w = width_per_item*GRID_COLS; + s32 new_h = height_per_item*GRID_ROWS; + s32 off_x = w - new_w; + s32 off_y = h - new_h; + w = new_w; + h = new_h; + + s32 panel_w = w+(halfpad*2); + + enum insights_panel_format + { + CHART, + GRID, + }; + static enum insights_panel_format current_format = GRID; + static world_location* active_world_location_filter = 0; + + { + s32 btn_row_x = x + off_x/2+halfpad; + s32 btn_row_y = y + off_y/2+halfpad; + + s32 button_pad = (button_row_h - btn_size)/2; + s32 btn_start_x = btn_row_x + panel_w - (4*scale); + s32 btn_left_start_x = btn_row_x+(4*scale)+button_pad; + s32 year_w = (80*scale); + s32 year_text_w = year_w+(button_pad*2); + s32 btn_y = btn_row_y + button_pad; + + // background + button_render(scale, BUTTON_STATIC, 0, btn_row_x, btn_row_y,panel_w, button_row_h); + + // Buttons right + if (push_info_panel_button(scale, img_graph, btn_start_x - ((btn_size + button_pad)*1), btn_y, btn_size, current_format!=CHART, false)) (current_format = CHART); + if (push_info_panel_button(scale, img_grid, btn_start_x - ((btn_size + button_pad)*2), btn_y, btn_size, current_format!=GRID, false)) (current_format = GRID); + + font* fnt = fnt_rd24; + s32 current_year = 1900+_active_world->start_year+insights_selected_year_index; + + // Buttons left + button_render(scale, BUTTON_STATIC, 0, btn_left_start_x+btn_size+button_pad, btn_y,year_w, btn_size); + char buf[10]; + sprintf(buf, "%d", current_year); + s32 textw = renderer->calculate_text_width(fnt,buf); + renderer->render_text(fnt,btn_left_start_x+(btn_size)+(year_text_w/2)-(textw/2), btn_y+(btn_size/2)-(fnt->px_h/2), buf, COLOR_TEXT); + + s32 btn_right_x = btn_left_start_x + (btn_size) + year_text_w; + if (push_info_panel_button(scale, img_arrow_left, btn_left_start_x, btn_y, btn_size, insights_selected_year_index>=1, false)) (insights_selected_year_index--); + if (push_info_panel_button(scale, img_arrow_right, btn_right_x, btn_y, + btn_size, insights_selected_year_index<_active_world->insights.length-1, false)) (insights_selected_year_index++); + + // Location selector + s32 tb_filter_x = btn_right_x + btn_size + button_pad; + s32 tb_width = 520*scale; + active_world_location_filter = location_selector_render(window, scale, true, active_world_location_filter, tb_filter_x, btn_y, tb_width, btn_size); + + // Clear button + s32 clear_btn_x = tb_filter_x + tb_width + button_pad; + if (push_info_panel_button(scale, img_globe, clear_btn_x, btn_y, btn_size, _global_keyboard.input_text_len, false)) { + active_world_location_filter = 0; + keyboard_set_input_text(""); + } + } + + x+=off_x/2+pad; + y+=off_y/2+pad+button_row_h+spacing; + + button_render(scale, BUTTON_STATIC, 0, x-halfpad,y-halfpad,panel_w,h+(halfpad*2)); + + static money_data_collection* prev_collection = 0; + money_data_collection* collection = get_current_insights_data(_active_world); // Get current year page, also a hack to make sure a new page exists at year switch. + if (!prev_collection || collection != prev_collection) { + prev_collection = collection; + insights_selected_year_index = _active_world->insights.length-1; + } + + money_data_collection* collection_to_use = array_at(&_active_world->insights, insights_selected_year_index); // Get page to use + if (active_world_location_filter) { + if (active_world_location_filter->is_owned) { + s32 index_of_filter_page = insights_selected_year_index; + s32 purchase_year_diff = active_world_location_filter->purchase_year - _active_world->start_year; + index_of_filter_page -= purchase_year_diff; + if (index_of_filter_page < 0) collection_to_use = 0; + else if (index_of_filter_page > active_world_location_filter->insights.length-1) collection_to_use = 0; + else collection_to_use = array_at(&active_world_location_filter->insights, index_of_filter_page); + } + else { + collection_to_use = 0; + } + } + + bool is_typing_in_filter_tb = (!active_world_location_filter && _global_keyboard.input_text_len); + if (is_typing_in_filter_tb) collection_to_use = 0; + + if (current_format == GRID) _insights_draw_grid(scale, is_typing_in_filter_tb, collection_to_use, GRID_ROWS, GRID_COLS, textpad, x, y, w, h, width_per_item, height_per_item); + if (current_format == CHART) _insights_draw_chart(window, is_typing_in_filter_tb, scale, collection_to_use, GRID_ROWS, GRID_COLS, textpad, x, y, w, h, width_per_item, height_per_item); + + if (mouse_interacts(x_orig, y_orig, w_orig, h_orig)) { + reset_left_click(); + } else if (is_left_clicked()) { + scene_state = WORLD_SCENE_STATE_IDLE; + } +} + +static void world_map_draw_event_log(platform_window* window) +{ + _active_world->log.has_unread_messages = false; + + font* fnt = fnt_rd16; + + s32 panel_h = area.h*0.9f; + s32 panel_pad = area.h*0.05f; + + s32 panel_w = area.w/3.5f; + + s32 panel_x = area.x + panel_pad; + s32 panel_y = area.y + panel_pad; + panel_render(scale, panel_x, panel_y, panel_w, panel_h); + + float text_pad = panel_w*0.05f; + + s32 text_y = panel_y + text_pad; + s32 text_x = panel_x + text_pad; + s32 text_w = panel_w - (text_pad*2); + + renderer->render_set_scissor(window, text_x, text_y, panel_w, panel_h-(text_pad*2)); + + if (_active_world->log.events.length) { + s32 read_cursor = _active_world->log.write_cursor; + for (s32 i = 0; i < _active_world->log.events.length; i++) { + read_cursor--; + if (read_cursor < 0) read_cursor = _active_world->log.events.length-1; + + event* e = array_at(&_active_world->log.events, read_cursor); + + float inner_pad = 5*scale; + + + s32 texth = renderer->render_text_cutoff(fnt, text_x+inner_pad, text_y+inner_pad, e->message, COLOR_TEXT, text_w-(inner_pad*2)); + s32 highlight_w = text_w; + s32 highlight_h = texth+(inner_pad); + + if (mouse_interacts(text_x, text_y, highlight_w, highlight_h)) { + platform_set_cursor(window, CURSOR_POINTER); + renderer->render_rectangle(text_x, text_y, text_w, highlight_h, rgba(255,255,255,20)); + + if (is_left_clicked()) { + switch (e->type) + { + case EVENT_TYPE_MISSED_SHIPMENT_NO_TRUCK: + place_detail_show_employee_detail((employee*)e->data); + break; + case EVENT_TYPE_MISSED_SHIPMENT_NO_ASSIGNEE: + case EVENT_TYPE_MISSED_SHIPMENT_NOT_AT_LOCATION: { + scheduled_job* job = (scheduled_job*)e->data; + place_detail_show_schedule_with_highlighted_job(job->location, job, e->job_time); + } break; + case EVENT_TYPE_EMPLOYEE_QUIT: + place_detail_show_schedule_with_highlighted_job((world_location*)e->data, 0, e->job_time); + break; + + default: + log_assert(0, "Invalid event type."); + break; + } + } + } + + text_y += highlight_h; + if (text_y > panel_y + panel_h) break; + } + } + else { + char* buf = "No events."; + s32 tw = renderer->calculate_text_width(fnt, buf); + renderer->render_text_cutoff(fnt, text_x + (text_w/2)-(tw/2), text_y, "No events.", COLOR_TEXT, text_w); + } + + renderer->render_reset_scissor(window); + + if (mouse_interacts(panel_x, panel_y, panel_w, panel_h)) { + reset_left_click(); + } else if (is_left_clicked()) { + scene_state = WORLD_SCENE_STATE_IDLE; + } +} + +s32 hovered_investment_panel_item_index = -1; +static s32 world_map_push_invest_panel_item(s32 x, s32 y, s32 w, s32 index, char* text, s32* fval) +{ + s32 pad = 40*scale; + s32 halfpad = pad/2; + s32 panel_w = w-(pad); + float item_pad = 30*scale; + float item_spacing = 5*scale; + float item_halfpad = item_pad/2; + + font* fnt = fnt_rd24; + char value_str[30]; + sprintf(value_str, "$%d", *fval); + float item_h = fnt->px_h+item_pad; + float item_x = x+halfpad; + float item_y = y+halfpad+(index*(item_h+item_spacing)); + float item_w = panel_w; + float content_x = item_x+item_halfpad; + float content_y = item_y+item_halfpad; + float total_val_editor_w = item_w*0.3f; + float button_left_x = item_x + item_w - total_val_editor_w - item_halfpad; + float btn_size = fnt->px_h*2; + float btn_y = item_y + ((item_h-btn_size)/2); + button_render(scale, BUTTON_STATIC, 0, item_x, item_y,item_w,item_h); + renderer->render_text(fnt, content_x, content_y, text, COLOR_TEXT); + // Button decrease simulation speed + if (push_info_panel_button(scale, img_arrow_left, button_left_x, btn_y, btn_size, *fval > 0, false)) { + *fval -= 100.0f; + } + // Button increase simulation speed + if (push_info_panel_button(scale, img_arrow_right, button_left_x+total_val_editor_w-btn_size, btn_y, btn_size, true, false)) { + *fval += 100.0f; + } + s32 item_text_w = renderer->calculate_text_width(fnt, value_str); + s32 item_text_x = button_left_x + (total_val_editor_w/2) - (item_text_w/2); + renderer->render_text(fnt, item_text_x, content_y, value_str, COLOR_TEXT); + + if (mouse_interacts_peak(item_x, item_y, item_w, item_h)) { + hovered_investment_panel_item_index = index; + } + + return item_y + item_h; +} + +static void world_map_draw_invest_panel(platform_window* window) +{ + s32 w = area.w*0.5; + s32 h = area.h*0.7; + s32 x = area.x + area.w*0.25; + s32 y = area.y + area.w*0.05; + panel_render(scale, x, y, w, h); + + s32 pad = 40*scale; + s32 halfpad = pad/2; + s32 panel_w = w-(pad); + + #define TOTAL_INVEST_ITEM_COUNT (5) + + hovered_investment_panel_item_index = -1; + + world_map_push_invest_panel_item(x, y, w, 0, "Workspace safety", (s32*)(&_active_world->investments.safety)); + world_map_push_invest_panel_item(x, y, w, 1, "Marketing", (s32*)(&_active_world->investments.marketing)); + world_map_push_invest_panel_item(x, y, w, 2, "Human resources", (s32*)(&_active_world->investments.human_resources)); + world_map_push_invest_panel_item(x, y, w, 3, "Employee training", (s32*)(&_active_world->investments.training)); + s32 item_bottom = world_map_push_invest_panel_item(x, y, w, 4, "Legal department", (s32*)(&_active_world->investments.legal)); + + float detail_panel_y = item_bottom + halfpad; + float detail_panel_h = h - (detail_panel_y-y)-halfpad; + + button_render(scale, BUTTON_STATIC, 0, x+halfpad, detail_panel_y,panel_w,detail_panel_h); + + s32 info_text_pad = 20*scale; + + char* info_text = 0; + switch (hovered_investment_panel_item_index) + { + case 0: info_text = "Increasing the budget for workspace safety will ensure employees receive the proper workspace safety training."; + break; + case 1: info_text = "Increasing the marketing budget will give your business more publicity and will secure more work proposals."; + break; + case 2: info_text = "Investing in human resources will guarantee more responses to vacancies."; + break; + case 3: info_text = "Providing training for your employees will increase happiness for your employees and make them work more efficiently."; + break; + case 4: info_text = "The legal department is essential for settling legal disputes."; + break; + } + + if (info_text) + renderer->render_text_cutoff(fnt_rd24, x+halfpad+info_text_pad, detail_panel_y+info_text_pad, info_text, COLOR_TEXT, panel_w - (info_text_pad*2)); + + if (mouse_interacts(x,y,w,h)) { + reset_left_click(); + } else if (is_left_clicked()) { + scene_state = WORLD_SCENE_STATE_IDLE; + } +} + +static void world_map_draw_purchase_location_panel(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; + + s32 panel_h = 160 * scale; + s32 panel_w = 280 * scale; + + 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); + + // info text + { + font* font_title = FONT_REGULAR(SIZE_RD(area.w, 32)); + { + char* title = scene_data.location_to_purchase->name; + s32 text_w = renderer->calculate_text_width(font_title, title); + s32 text_x = screen_center_x - (text_w/2); + s32 text_y = panel_y + (vertical_pad); + renderer->render_text(font_title, text_x+2, text_y+2, title, COLOR_TEXT_SHADOW); + renderer->render_text(font_title, text_x, text_y, title, COLOR_TEXT); + } + { + char buf[100]; + sprintf(buf, "Purchase a garage for $%.0f?", world_location_get_price(scene_data.location_to_purchase)); + char* text = buf; + font* font_info = FONT_REGULAR(SIZE_RD(area.w, 20)); + s32 text_w = renderer->calculate_text_width(font_info, text); + s32 text_x = screen_center_x - (text_w/2); + s32 text_y = panel_y + vertical_pad*1.5+font_title->px_h; + renderer->render_text(font_info, text_x+2, text_y+2, text, COLOR_TEXT_SHADOW); + renderer->render_text(font_info, text_x, text_y, text, COLOR_TEXT); + } + } + + s32 button_w = 178 * scale; + s32 button_h = 37 * scale; + if (button_render(scale, true, "Purchase", screen_center_x - (button_w/2), panel_y + panel_h - button_h - vertical_pad, button_w, button_h)) + { + // Mark as owned. + scene_data.location_to_purchase->is_owned = true; + scene_data.location_to_purchase->purchase_year = _active_world->current_time.tm_year; + + // Make sure insights are ready to display. + money_data_collection* collection = get_current_insights_data_for_location(_active_world, scene_data.location_to_purchase); + collection->months[_active_world->current_time.tm_mon].total_income = 0; + + _active_world->money -= world_location_get_price(scene_data.location_to_purchase); + + scene_state = WORLD_SCENE_STATE_IDLE; + } + + button_w = 30 * scale; + if (button_render(scale, true, "X", panel_x+panel_w-button_w, panel_y+2, button_w, button_w)) + { + scene_state = WORLD_SCENE_STATE_IDLE; + } + + if (mouse_interacts(panel_x,panel_y,panel_w,panel_h)) { + reset_left_click(); + } else if (is_left_clicked()) { + scene_state = WORLD_SCENE_STATE_IDLE; + } +} + +static void world_map_draw_viewing_job(platform_window* window) +{ + active_job* job = get_active_job_by_ref(_active_world, currently_viewing_active_job); + if (!job) { + currently_viewing_active_job.offerid = INVALID_ID; + return; + } + + s32 panel_offset_from_job = area.h*0.1f; + s32 panel_h = area.h*0.32f; + s32 panel_w = area.w*0.15f; + + s32 panel_x = job->px_pos.x; + s32 panel_y = job->px_pos.y; + + if (job->px_pos.y > area.y + (area.h/2)) { + panel_y -= panel_h - panel_offset_from_job; + + renderer->render_line(job->px_pos.x+(dotsize/2), job->px_pos.y+(dotsize/2), panel_x + (panel_w/3), panel_y+panel_h-5, 3, COLOR_INSPECT_ACTIVE_JOB_LINE_CONNECTION); + renderer->render_line(job->px_pos.x+(dotsize/2), job->px_pos.y+(dotsize/2), panel_x + (panel_w/3*2), panel_y+panel_h-5, 3, COLOR_INSPECT_ACTIVE_JOB_LINE_CONNECTION); + } + else { + panel_y += panel_offset_from_job; + + renderer->render_line(job->px_pos.x+(dotsize/2), job->px_pos.y+(dotsize/2), panel_x + (panel_w/3), panel_y+5, 3, COLOR_INSPECT_ACTIVE_JOB_LINE_CONNECTION); + renderer->render_line(job->px_pos.x+(dotsize/2), job->px_pos.y+(dotsize/2), panel_x + (panel_w/3*2), panel_y+5, 3, COLOR_INSPECT_ACTIVE_JOB_LINE_CONNECTION); + } + + panel_render(scale, panel_x, panel_y, panel_w, panel_h); + + s32 panel_pad = 10*scale; + s32 truck_img_x = panel_x + panel_pad; + s32 truck_img_y = panel_y + panel_pad; + s32 truck_img_s = panel_w * 0.6f; + + s32 portrait_s = truck_img_s / 2; + + renderer->render_image(job->assigned_truck.logo, truck_img_x+(panel_pad), truck_img_y, truck_img_s, truck_img_s); + draw_employee_portrait(&job->assignee, truck_img_x+(panel_pad) + truck_img_s - (portrait_s/3), truck_img_y + (truck_img_s / 3), portrait_s, portrait_s); + + { + s32 text_x = truck_img_x; + float text_pad = (5*scale); + s32 text_y = truck_img_y + truck_img_s + panel_pad; + font* fnt = fnt_rd16; + font* fnt_s = fnt_rd12; + renderer->render_text(fnt,text_x,text_y,job->assignee.name,COLOR_TEXT); + text_y += fnt->px_h + text_pad; + + // Name + origin + { + world_location* origin = get_world_location_by_id(_active_world, job->assignee.original_location_id); + char buf[100]; + sprintf(buf, "Works at %s", origin->name); + renderer->render_text(fnt_s,text_x,text_y,buf,COLOR_TEXT); + text_y += fnt_s->px_h + text_pad; + } + + // Job info + { + text_y += (20*scale); + + char daybuf[10]; + switch(job->day) { + case 1: strcpy(daybuf, "Mon"); break; + case 2: strcpy(daybuf, "Tue"); break; + case 3: strcpy(daybuf, "Wed"); break; + case 4: strcpy(daybuf, "Thu"); break; + case 5: strcpy(daybuf, "Fri"); break; + case 6: strcpy(daybuf, "Sat"); break; + case 0: strcpy(daybuf, "Sun"); break; + } + + char buf[100]; + sprintf(buf, "Left on %s %02d:%02d", daybuf, WORK_HOUR_START + (job->timeslot/TIME_SLOTS_PER_HOUR), job->timeslot%TIME_SLOTS_PER_HOUR); + renderer->render_text(fnt_s,text_x,text_y,buf,COLOR_TEXT); + text_y += fnt_s->px_h + text_pad; + } + + // Job status + if (!job->reversed) + { + job_endpoints endpoints = job_offer_get_endpoints(&job->offer); + char buf[100]; + sprintf(buf, "Shipping %s to %s", job->offer.product->name, endpoints.dest->name); + text_y += renderer->render_text_cutoff(fnt_s,text_x,text_y,buf,COLOR_TEXT, panel_w - (panel_pad*2)); + text_y += text_pad; + } + else { + world_location* source = *(world_location**)array_at(&job->offer.connections, 0); // Get source location + char buf[100]; + sprintf(buf, "Returning to %s", source->name); + renderer->render_text_cutoff(fnt_s,text_x,text_y,buf,COLOR_TEXT, panel_w - (panel_pad*2)); + text_y += fnt_s->px_h + text_pad; + } + } + + if (mouse_interacts(panel_x,panel_y,panel_w,panel_h)) { + reset_left_click(); + } else if (is_left_clicked()) { + currently_viewing_active_job.offerid = INVALID_ID; + } +} + +static void world_handle_scroll(platform_window* window) +{ + static float target_zoom = 1.0f; + if (global_ui_context.mouse->scroll_state == SCROLL_UP) + { + if (target_zoom < zoom) target_zoom = zoom; + target_zoom+=0.1f; + } + if (global_ui_context.mouse->scroll_state == SCROLL_DOWN) + { + if (target_zoom > zoom) target_zoom = zoom; + target_zoom-=0.1f; + } + + // Keep camera position + if (target_zoom != zoom) { + vec4 area = camera_get_target_rectangle(window); + int orig_w = area.w * zoom; + int orig_h = area.h * zoom; + int new_w = area.w * target_zoom; + int new_h = area.h * target_zoom; + + float errorw = new_w - orig_w; + float errorh = new_h - orig_h; + camera_x -= errorw / 25.0f; + camera_y -= errorh / 40.0f; + } + + // Smooth scrolling + if (target_zoom != zoom) + { + float error = target_zoom - zoom; + zoom += error / 10; + } + + if (zoom < 1.0f) zoom = 1.0f; + if (zoom > 5.0f) zoom = 5.0f; + + if (camera_x > 0.0f) camera_x = 0.0f; + if (camera_y > 0.0f) camera_y = 0.0f; + + //if (is_left_down_peak()) + { + + } +} + +void world_map_scene_render(platform_window* window) +{ + renderer->set_render_depth(5); + + world_handle_scroll(window); + + world_map_draw_info_panel(window, true); + + renderer->set_render_depth(4); + switch (scene_state) + { + case WORLD_SCENE_STATE_INSIGHTS: + world_map_draw_insights(window); + break; + case WORLD_SCENE_STATE_LOG: + world_map_draw_event_log(window); + break; + case WORLD_SCENE_STATE_IDLE: break; + case WORLD_SCENE_STATE_PURCHASE_LOCATION: + world_map_draw_purchase_location_panel(window); + break; + case WORLD_SCENE_STATE_INVEST: + world_map_draw_invest_panel(window); + break; + } + + renderer->set_render_depth(3); + if (currently_viewing_active_job.offerid != INVALID_ID) world_map_draw_viewing_job(window); + + if (_active_world) { + world_update_result click_result = world_render(window, _active_world); + + if (click_result.clicked_location) { + if (click_result.clicked_location->is_owned) { + place_detail_set_active_location(click_result.clicked_location); + game_set_active_scene(GAME_STATE_PLACE_DETAIL); + } + else { + scene_data.location_to_purchase = click_result.clicked_location; + scene_state = WORLD_SCENE_STATE_PURCHASE_LOCATION; + } + } + else if (click_result.clicked_job) { + currently_viewing_active_job = (active_job_ref){click_result.clicked_job->day, click_result.clicked_job->timeslot, click_result.clicked_job->offer.id}; + } + } + + renderer->set_render_depth(0); + + vec4 area = camera_get_target_rectangle(window); + renderer->render_rectangle(area.x, area.y, area.w, area.h, COLOR_WORLD_MAP_BACKGROUND); + renderer->render_image(img_world_map, area.x + camera_x, area.y + camera_y, area.w*zoom, area.h*zoom); +} + +void world_map_scene_update(platform_window* window) +{ + world_update(window, _active_world); + if (keyboard_is_key_pressed(KEY_ESCAPE)) { + scene_state = WORLD_SCENE_STATE_IDLE; + } +} + +void world_map_scene_destroy() +{ + +} \ No newline at end of file -- cgit v1.2.3-70-g09d2