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 = JOB_STARTING_TRUST; 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 35 #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 ($%.2f/km)", offer->reward, offer->reward / (float)offer->total_distance); TAB_ITEM_PUSH_TEXT(pricebuf, width_of_piece*1.5f, 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 s32 place_get_nr_of_internal_employees() { s32 count = 0; for (s32 i = 0; i < _active_location->employees.length; i++) { employee* emp = *(employee**)array_at(&_active_location->employees, i); bool is_internal = (emp->original_location_id == _active_location->id); if (is_internal) count++; } return count; } 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, 5); HANDLE_TAB_SCROLL; char info_buf[50]; sprintf(info_buf, "%d Employees on payroll.", place_get_nr_of_internal_employees()); 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); } // Hours { char buffer[50]; float total_hours = get_worked_hours_per_week_for_employee(_active_world, emp, 0); sprintf(buffer, "Scheduled: %.0f hours", total_hours); 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); } // Truck { 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*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*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*3 + 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*4 + 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 (_active_world->money < tr_curr->price) type2 = BUTTON_DISABLED; if (_active_location->trucks.length == MAX_TRUCK_COUNT-1) type2 = BUTTON_DISABLED; 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; audio_play_sound(snd_bell, AUDIO_CHANNEL_SFX_2); } 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_scheduling_job = *job; } _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(); s32 location_id_to_inspect; if (_active_location == 0 || (_active_location->id != emp->current_location_id && _active_location->id != emp->original_location_id)) { s32 location_id_to_inspect = emp->current_location_id; if (location_id_to_inspect == INVALID_ID) { location_id_to_inspect = emp->original_location_id; } } else { location_id_to_inspect = _active_location->id; } world_location* original_loc = get_world_location_by_id(_active_world, location_id_to_inspect); 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: %.0f 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 scheduled_job_time draw_duration_of_scheduled_job_entry(scheduled_job job, 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, bool do_draw) { scheduled_job_time result = scheduled_time; #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((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))); if (do_draw) 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) { int extra_days = ceil(overflow_to_next_day / (float)TIME_SLOTS_PER_DAY); int curr_day = scheduled_time.day; //printf("%d %d %d\n", extra_days, duration_of_job_measured_in_timeslots_max, overflow_to_next_day); for (int xx = 0; xx < extra_days; xx++) { s32 overflow = overflow_to_next_day % TIME_SLOTS_PER_DAY; if (xx != extra_days-1) overflow = TIME_SLOTS_PER_DAY; else if (overflow == 0) { overflow = TIME_SLOTS_PER_DAY; } if (curr_day == SUNDAY) { curr_day = MONDAY; highlight_y = TILE_Y(MONDAY); } //printf("%d %d %d\n", overflow_to_next_day, extra_days, overflow); if (do_draw) renderer->render_rectangle(TILE_X(0), highlight_y, timeslot_w*overflow, timeslot_h, tile_color_bg); overflow_to_next_day -= TIME_SLOTS_PER_DAY; curr_day++; if (curr_day == 7) curr_day = 0; if (overflow_to_next_day <= 0) { if (scheduled_time.stay_at_destination) { if (do_draw) { renderer->render_rectangle(TILE_X(0) + timeslot_w*overflow - timeslot_w, highlight_y, timeslot_w, timeslot_h, COLOR_SCHEDULE_TILE_EXIT); renderer->set_render_depth(5); renderer->render_image(img_exit, TILE_X(0) + timeslot_w*overflow - timeslot_w + timeslot_w*1.5f, highlight_y+(timeslot_h/4), timeslot_h/2, timeslot_h/2); renderer->set_render_depth(4); } } } highlight_y += timeslot_h; result.day = curr_day; result.timeslot = overflow; } } else { result.timeslot += duration_of_job_measured_in_timeslots_max; if (scheduled_time.stay_at_destination) { if (do_draw) { renderer->render_rectangle(tx + highlight_w - timeslot_w, ty, timeslot_w, timeslot_h, COLOR_SCHEDULE_TILE_EXIT); renderer->set_render_depth(5); renderer->render_image(img_exit, tx + highlight_w - timeslot_w + timeslot_w*1.5f, ty+(timeslot_h/4), timeslot_h/2, timeslot_h/2); renderer->set_render_depth(4); } } } return result; } static s32 scheduled_job_times_where_employee_return_to_location(world_location* location_to, employee* emp, scheduled_job_time* buffer, s32 buffer_size) { s32 count = 0; for (s32 i = 0; i < _active_world->locations.length; i++) { world_location* location = array_at(&_active_world->locations, i); if (!location->is_owned) continue; for (s32 r = 0; r < location->schedule.jobs.length; r++) { scheduled_job* scheduled_job = array_at(&location->schedule.jobs, r); job_endpoints endpoints = job_offer_get_endpoints(&scheduled_job->offer); if (endpoints.dest != location_to) continue; for (s32 t = 0; t < scheduled_job->offer.shipday_count; t++) { scheduled_job_time scheduled_time = scheduled_job->timeslots[t]; if (scheduled_time.assignee == emp && scheduled_time.stay_at_destination) { scheduled_job_time end_timeslot = draw_duration_of_scheduled_job_entry(*scheduled_job, scheduled_time, 0, 0, 0, 0, 0, 0, 0, rgb(0,0,0), false); //printf("%d %d\n", ); buffer[count] = end_timeslot; count++; if (count >= buffer_size) return count; } } } } return count; } static bool termination_popup_active = false; 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); if (_active_schedule_state == RESCHEDULING_JOB) { _global_keyboard.take_input = false; _active_schedule_state = VIEWING; } else _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)) { if (_active_schedule_state == RESCHEDULING_JOB) { _global_keyboard.take_input = false; _active_schedule_state = VIEWING; } else _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; } btn_y += btn_pad + button_h; char* terminate_txt = termination_popup_active ? "Confirm?" : "Cancel Job"; s32 terminate_type = termination_popup_active ? BUTTON_HIGHLIGHTED : BUTTON_ENABLED; if (button_render(scale, terminate_type, terminate_txt, x-button_w+w, btn_y, button_w, button_h)) { if (termination_popup_active) { array_remove_by(&_active_location->schedule.jobs, _active_selected_scheduled_job); audio_play_sound(snd_click3, AUDIO_CHANNEL_SFX_1); _active_selected_scheduled_job = 0; termination_popup_active = false; } else { termination_popup_active = true; } } } 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 = _active_scheduling_job.offer.shipday_count-1; i >= 0; 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); bool is_selected = _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 if dragging. draw_duration_of_scheduled_job_entry(_active_scheduling_job, scheduled_time, hour_w, tx, ty, timeslot_w, x, y, timeslot_h, tile_color, true); // 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); } } // Highlight duration if selected. else if (is_selected) { draw_duration_of_scheduled_job_entry(_active_scheduling_job, scheduled_time, hour_w, tx, ty, timeslot_w, x, y, timeslot_h, tile_color, true); } // Highlight duration if sharing same employee. else if (scheduled_time.assignee != 0 && scheduled_time.assignee == _active_scheduling_job.timeslots[_active_schedule_selected_job_index].assignee) { draw_duration_of_scheduled_job_entry(_active_scheduling_job, scheduled_time, hour_w, tx, ty, timeslot_w, x, y, timeslot_h, tile_color, true); } // 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_schedule_state == VIEWING && _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; } if ((_active_schedule_state == SCHEDULING_JOB || _active_schedule_state == RESCHEDULING_JOB) && scheduled_time.assignee == _active_scheduling_job.timeslots[_active_schedule_selected_job_index].assignee) { 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(*job, scheduled_time, hour_w, tx, ty, timeslot_w, x, y, timeslot_h, tc, true); } 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_scheduling_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; } } } } } // external job re-entries if (_active_schedule_state == SCHEDULING_JOB || _active_schedule_state == RESCHEDULING_JOB) { for (s32 i = _active_scheduling_job.offer.shipday_count-1; i >= 0; i--) { scheduled_job_time scheduled_time = _active_scheduling_job.timeslots[i]; bool is_selected = _active_schedule_selected_job_index == i; if (!is_selected) continue; s32 tx = TILE_X(scheduled_time.timeslot); s32 ty = TILE_Y(scheduled_time.day); scheduled_job_time sj = scheduled_time; employee* emp = sj.assignee; if (emp) { // find all jobs of employee where they return to current location. scheduled_job_time buffer[10]; s32 count = scheduled_job_times_where_employee_return_to_location(_active_location, emp, buffer, 10); for (int c = 0; c < count; c++) { scheduled_job_time time = buffer[c]; s32 tx = TILE_X(time.timeslot); s32 ty = TILE_Y(time.day); renderer->render_rectangle(tx, ty, timeslot_w, timeslot_h, COLOR_SCHEDULE_TILE_ENTRY); renderer->set_render_depth(5); renderer->render_image(img_entry, tx - timeslot_w*2, ty+(timeslot_h/4), timeslot_h/2, timeslot_h/2); renderer->set_render_depth(4); } } } } for (s32 i = 0; i < _active_location->schedule.jobs.length; i++) { scheduled_job* job = array_at(&_active_location->schedule.jobs, i); 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); // highlight timeslot where employee returns from external location. if (is_selected_job || is_selected) { scheduled_job_time sj = scheduled_time; employee* emp = sj.assignee; if (emp) { // find all jobs of employee where they return to current location. scheduled_job_time buffer[10]; s32 count = scheduled_job_times_where_employee_return_to_location(_active_location, emp, buffer, 10); for (int c = 0; c < count; c++) { scheduled_job_time time = buffer[c]; s32 tx = TILE_X(time.timeslot); s32 ty = TILE_Y(time.day); renderer->render_rectangle(tx, ty, timeslot_w, timeslot_h, COLOR_SCHEDULE_TILE_ENTRY); renderer->set_render_depth(5); renderer->render_image(img_entry, tx - timeslot_w*2, ty+(timeslot_h/4), timeslot_h/2, timeslot_h/2); renderer->set_render_depth(4); } } } } } // 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_rd16, buf); s32 textx = x + (hour_w/2)-(tw/2); s32 texty = row_y + (timeslot_h/2)-(fnt_rd16->px_h/2); renderer->render_text(fnt_rd16, 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_rd16, buf); s32 textx = row_x + (hour_w/2)-(tw/2); s32 texty = y + (timeslot_h/2)-(fnt_rd16->px_h/2); renderer->render_text(fnt_rd16, 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: %.0f 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, _active_location->employees.length == MAX_EMPLOYEE_COUNT-1 ? BUTTON_DISABLED : BUTTON_ENABLED, "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); } #define DRAW_STARS(_x, _y, _txt, _count){\ s32 textw = renderer->render_text(fnt, _x, _y, _txt, COLOR_TEXT);\ _x += (scale*5);\ for (s32 i = 0; i < 5; i++)\ {\ if (i < _count) renderer->render_image(img_star, _x + textw + (i*(fnt->px_h+2)), _y, fnt->px_h, fnt->px_h);\ else renderer->render_image_tint(img_star, _x + textw + (i*(fnt->px_h+2)), _y, fnt->px_h, fnt->px_h, rgba(255,255,255,30));\ }} static void place_detail_draw_location_stats(platform_window* window) { font* fnt = fnt_rd20; float text_pad = scale * 40.0; s32 text_x = area.x + text_pad + (area.w*0.75); s32 text_y = area.y + text_pad + (area.w*0.05); DRAW_STARS(text_x, text_y, "Score: ", (int)round(_active_location->score * 5)); } 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); place_detail_draw_location_stats(window); } 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 = area.x + text_pad + (area.w*0.05); s32 text_y = area.y + text_pad + (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 ($%.2f/km)", job_to_inspect->reward, job_to_inspect->reward / (float)job_to_inspect->total_distance); 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)) { termination_popup_active = false; 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)) { termination_popup_active = false; 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() { }