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_SCENE_MENU, WORLD_SCENE_BANK, } 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 if (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)) { scene_state = (scene_state == WORLD_SCENE_BANK) ? WORLD_SCENE_STATE_IDLE : WORLD_SCENE_BANK; } // Event log button if (push_info_panel_button(scale, img_invest, screen_center_x - (total_btn_w/2) + (btn_size+btn_spacing)*3, btn_y, btn_size, enabled, false)) { 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 (e->type != EVENT_TYPE_INFO && 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_FINED: 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; case EVENT_TYPE_INFO: 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 -= INVESTMENT_INCREMENT; } // 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 += INVESTMENT_INCREMENT; } 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, 0, "Marketing", (s32*)(&_active_world->investments.marketing)); world_map_push_invest_panel_item(x, y, w, 1, "Human resources", (s32*)(&_active_world->investments.human_resources)); world_map_push_invest_panel_item(x, y, w, 2, "Employee training", (s32*)(&_active_world->investments.training)); s32 item_bottom = world_map_push_invest_panel_item(x, y, w, 3, "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 marketing budget will give your business more publicity and will secure more work proposals."; break; case 1: info_text = "Investing in human resources will guarantee more responses to vacancies."; break; case 2: info_text = "Providing training for your employees will increase happiness for your employees and make them work more efficiently."; break; case 3: 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; s32 state = (_active_world->money < world_location_get_price(scene_data.location_to_purchase)) ? BUTTON_DISABLED : BUTTON_ENABLED; if (button_render(scale, state, "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) { float prev_zoom = zoom; bool changed = false; if (global_ui_context.mouse->scroll_state == SCROLL_UP) { zoom+=0.1f; changed = true; } if (global_ui_context.mouse->scroll_state == SCROLL_DOWN) { zoom-=0.1f; changed = true; } if (zoom < 1.0f) zoom = 1.0f; else if (zoom > 3.0f) zoom = 3.0f; float center_y = -((area.h * zoom) - area.h) / 8; float center_x = -((area.w * zoom) - area.w) / 2; if (changed && prev_zoom != zoom) { float cursor_offset_x = (((_global_mouse.x) - (float)area.w / 2) / area.w); float cursor_offset_y = (((_global_mouse.y) - (float)area.h / 2) / area.h); if (zoom < prev_zoom) { int space_lost_x = ( (area.w * prev_zoom) - (area.w * zoom) ); camera_x += space_lost_x/2; int space_lost_y = ( (area.h * prev_zoom) - (area.h * zoom) ); camera_y += space_lost_y/2; } else { int space_lost_x = ( (area.w * zoom) - (area.w * prev_zoom) ); camera_x -= space_lost_x/2 + (cursor_offset_x*150); int space_lost_y = ( (area.h * zoom) - (area.h * prev_zoom) ); camera_y -= space_lost_y/2 + (cursor_offset_y*150); } } static bool is_dragging = false; static s32 drag_startx = 0; static s32 drag_starty = 0; static s32 camera_start_x = 0; static s32 camera_start_y = 0; if (is_right_down() && window->has_focus) { if (!is_dragging) { is_dragging = true; drag_startx = _global_mouse.x; drag_starty = _global_mouse.y; camera_start_x = camera_x; camera_start_y = camera_y; } s32 diffx = _global_mouse.x - drag_startx; s32 diffy = _global_mouse.y - drag_starty; camera_x = camera_start_x + diffx; camera_y = camera_start_y + diffy; } else { is_dragging = false; } if (camera_x > 0.0f) camera_x = 0.0f; if (camera_y > 0.0f) camera_y = 0.0f; float max_camera_offset_y = -((area.h * zoom) - area.h); float max_camera_offset_x = -((area.w * zoom) - area.w); if (camera_y < max_camera_offset_y) camera_y = max_camera_offset_y; if (camera_x < max_camera_offset_x) camera_x = max_camera_offset_x; } static vec4 world_map_get_daytime_rect() { int hour = _active_world->current_time.tm_hour; int minutes = _active_world->current_time.tm_min; int offset = area.w*zoom - ((((hour*60.0f) + minutes) / (24*60.0f)) * area.w*zoom); vec4 rec = (vec4){0,0,0,0}; return rec; } static color img_get_px(image* img, s32 x, s32 y) { if (x < 0 || y < 0) return rgba(0,0,0,0); if (x > img->width || y > img->height) return rgba(0,0,0,0); u8* bytes = (((u8*)img->data) + (img->width * img->channels * y) + (img->channels * x)); color c = *(color*)(bytes); return c; } static s32 shadow_offset = 0; static s32 shadow2_offset = 0; bool world_map_location_is_in_sun(vec2f px_pos) { //px_pos.x *= (float)area.w/img_shadow->width; return !(img_get_px(img_shadow, (px_pos.x - shadow_offset)/((float)area.w/img_shadow->width*zoom), (px_pos.y - camera_y - area.y)/((float)area.h/img_shadow->height*zoom)).a == 255 || img_get_px(img_shadow, (px_pos.x - shadow2_offset)/((float)area.w/img_shadow->width*zoom), (px_pos.y - camera_y - area.y)/((float)area.h/img_shadow->height*zoom)).a == 255); } static void world_map_draw_night(platform_window* window) { int hour = _active_world->current_time.tm_hour; int minutes = _active_world->current_time.tm_min; int offset = area.w*zoom - ((((hour*60.0f) + minutes) / (24*60.0f)) * area.w*zoom); shadow_offset = area.x + camera_x + offset; shadow2_offset = area.x + camera_x + offset - area.w*zoom; renderer->render_image_tint(img_shadow, shadow_offset, area.y + camera_y, area.w*zoom, area.h*zoom, rgba(255,255,255,100)); renderer->render_image_tint(img_shadow, shadow2_offset, area.y + camera_y, area.w*zoom, area.h*zoom, rgba(255,255,255,100)); } static void world_map_draw_bank(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 = 350 * 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); } static void world_map_draw_menu(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, "Continue", panel_x + pad_x, vertical_pad + panel_y + pad_y*1, button_w, button_h)) { scene_state = WORLD_SCENE_STATE_IDLE; } if (button_render(scale, BUTTON_ENABLED, "Save", 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); scene_state = WORLD_SCENE_STATE_IDLE; is_selecting_save_location = true; } 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); scene_state = WORLD_SCENE_STATE_IDLE; is_editing_settings_from_ingame = true; } 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)) { game_set_active_scene(GAME_STATE_MENU); scene_state = WORLD_SCENE_STATE_IDLE; } } void world_map_scene_render(platform_window* window) { renderer->set_render_depth(5); world_handle_scroll(window); world_map_draw_info_panel(window, scene_state != WORLD_SCENE_MENU); 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; case WORLD_SCENE_MENU: world_map_draw_menu(window); break; case WORLD_SCENE_BANK: world_map_draw_bank(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 + camera_x, area.y + camera_y, area.w*zoom, area.h*zoom, COLOR_WORLD_MAP_BACKGROUND); renderer->render_image(img_world_map, area.x + camera_x, area.y + camera_y, area.w*zoom, area.h*zoom); world_map_draw_night(window); } void world_map_scene_update(platform_window* window) { if (scene_state != WORLD_SCENE_MENU) world_update(window, _active_world); if (keyboard_is_key_pressed(KEY_ESCAPE)) { if (scene_state == WORLD_SCENE_STATE_IDLE) { scene_state = WORLD_SCENE_MENU; } else scene_state = WORLD_SCENE_STATE_IDLE; } } void world_map_scene_destroy() { }