diff --git a/dataobj/schedule.cc b/dataobj/schedule.cc index 017daa13b..517cc1805 100644 --- a/dataobj/schedule.cc +++ b/dataobj/schedule.cc @@ -24,7 +24,7 @@ #include "../tpl/slist_tpl.h" -schedule_entry_t schedule_t::dummy_entry(koord3d::invalid, 0, 0); +schedule_entry_t schedule_t::dummy_entry(koord3d::invalid, 0, 0, 0, false, 0); // copy all entries from schedule src to this and adjusts current_stop @@ -40,6 +40,7 @@ void schedule_t::copy_from(const schedule_t *src) entries.append(i); } set_current_stop( src->get_current_stop() ); + set_spacing( src->get_spacing() ); editing_finished = src->is_editing_finished(); } @@ -110,7 +111,7 @@ halthandle_t schedule_t::get_prev_halt( player_t *player ) const } -bool schedule_t::insert(const grund_t* gr, uint8 minimum_loading, uint8 waiting_time_shift ) +bool schedule_t::insert(const grund_t* gr, uint8 minimum_loading, uint8 waiting_time_shift, sint16 spacing_shift, bool wait_for_time, uint16 delay_tolerance) { // stored in minivec, so we have to avoid adding too many if( entries.get_count()>=254 ) { @@ -119,7 +120,7 @@ bool schedule_t::insert(const grund_t* gr, uint8 minimum_loading, uint8 waiting_ } if( is_stop_allowed(gr) ) { - entries.insert_at(current_stop, schedule_entry_t(gr->get_pos(), minimum_loading, waiting_time_shift)); + entries.insert_at(current_stop, schedule_entry_t(gr->get_pos(), minimum_loading, waiting_time_shift, spacing_shift, wait_for_time, delay_tolerance)); current_stop ++; make_current_stop_valid(); return true; @@ -133,7 +134,7 @@ bool schedule_t::insert(const grund_t* gr, uint8 minimum_loading, uint8 waiting_ -bool schedule_t::append(const grund_t* gr, uint8 minimum_loading, uint8 waiting_time_shift) +bool schedule_t::append(const grund_t* gr, uint8 minimum_loading, uint8 waiting_time_shift, sint16 spacing_shift, bool wait_for_time, uint16 delay_tolerance) { // stored in minivec, so we have to avoid adding too many if(entries.get_count()>=254) { @@ -142,7 +143,7 @@ bool schedule_t::append(const grund_t* gr, uint8 minimum_loading, uint8 waiting_ } if(is_stop_allowed(gr)) { - entries.append(schedule_entry_t(gr->get_pos(), minimum_loading, waiting_time_shift), 4); + entries.append(schedule_entry_t(gr->get_pos(), minimum_loading, waiting_time_shift, spacing_shift, wait_for_time, delay_tolerance), 4); return true; } else { @@ -229,7 +230,7 @@ void schedule_t::rdwr(loadsave_t *file) uint32 dummy; pos.rdwr(file); file->rdwr_long(dummy); - entries.append(schedule_entry_t(pos, (uint8)dummy, 0)); + entries.append(schedule_entry_t(pos, (uint8)dummy, 0, 0, false, 0)); } } else { @@ -238,14 +239,25 @@ void schedule_t::rdwr(loadsave_t *file) if(entries.get_count()<=i) { entries.append( schedule_entry_t() ); entries[i] .waiting_time_shift = 0; + entries[i] .spacing_shift = 0; + entries[i] .wait_for_time = false; + entries[i] .delay_tolerance = 0; } entries[i].pos.rdwr(file); file->rdwr_byte(entries[i].minimum_loading); if(file->get_version()>=99018) { file->rdwr_byte(entries[i].waiting_time_shift); } + if(file->get_version()>=120009) { + file->rdwr_short(entries[i].spacing_shift); + file->rdwr_bool(entries[i].wait_for_time); + file->rdwr_short(entries[i].delay_tolerance); + } } } + if(file->get_version()>=120009) { + file->rdwr_short(spacing); + } if(file->is_loading()) { editing_finished = true; } @@ -286,6 +298,10 @@ bool schedule_t::matches(karte_t *welt, const schedule_t *schedule) if( schedule->entries.empty() || entries.empty() ) { return false; } + // is spacing same? + if( schedule->get_spacing()!=spacing ) { + return false; + } // now we have to check all entries ... // we need to do this that complicated, because the last stop may make the difference uint16 f1=0, f2=0; @@ -399,9 +415,9 @@ void schedule_t::add_return_way() void schedule_t::sprintf_schedule( cbuffer_t &buf ) const { - buf.printf("%u|%d|", current_stop, (int)get_type()); + buf.printf("%u,%u|%d|", current_stop, spacing, (int)get_type()); FOR(minivec_tpl, const& i, entries) { - buf.printf("%s,%i,%i|", i.pos.get_str(), (int)i.minimum_loading, (int)i.waiting_time_shift); + buf.printf("%s,%i,%i,%i,%i,%i|", i.pos.get_str(), (int)i.minimum_loading, (int)i.waiting_time_shift, (int)i.spacing_shift, (int)i.wait_for_time, (int)i.delay_tolerance); } } @@ -419,6 +435,15 @@ bool schedule_t::sscanf_schedule( const char *ptr ) } // first get current_stop pointer current_stop = atoi( p ); + while( *p && *p!=',' ) { + p++; + } + if( *p!=',' ) { + dbg->error( "schedule_t::sscanf_schedule()","incomplete entry termination!" ); + return false; + } + p++; + spacing = atoi( p ); while( *p && *p!='|' ) { p++; } @@ -444,24 +469,24 @@ bool schedule_t::sscanf_schedule( const char *ptr ) p++; // now scan the entries while( *p>0 ) { - sint16 values[5]; - for( sint8 i=0; i<5; i++ ) { + sint16 values[8]; + for( sint8 i=0; i<8; i++ ) { values[i] = atoi( p ); while( *p && (*p!=',' && *p!='|') ) { p++; } - if( i<4 && *p!=',' ) { + if( i<7 && *p!=',' ) { dbg->error( "schedule_t::sscanf_schedule()","incomplete string!" ); return false; } - if( i==4 && *p!='|' ) { + if( i==7 && *p!='|' ) { dbg->error( "schedule_t::sscanf_schedule()","incomplete entry termination!" ); return false; } p++; } // ok, now we have a complete entry - entries.append(schedule_entry_t(koord3d(values[0], values[1], values[2]), values[3], values[4])); + entries.append(schedule_entry_t(koord3d(values[0], values[1], values[2]), values[3], values[4],values[5],values[6],values[7])); } return true; } @@ -472,6 +497,9 @@ void schedule_t::gimme_stop_name(cbuffer_t& buf, karte_t* welt, player_t const* const char *p; halthandle_t halt = haltestelle_t::get_halt(entry.pos, player_); if(halt.is_bound()) { + if (entry.wait_for_time && max_chars <= 0) { + buf.printf("[*] "); + } if (entry.minimum_loading != 0 && max_chars <= 0) { buf.printf("%d%% ", entry.minimum_loading); } diff --git a/dataobj/schedule.h b/dataobj/schedule.h index 896cd1bc7..a79879bab 100644 --- a/dataobj/schedule.h +++ b/dataobj/schedule.h @@ -23,6 +23,7 @@ class schedule_t { bool editing_finished; uint8 current_stop; + uint16 spacing; static schedule_entry_t dummy_entry; @@ -41,7 +42,7 @@ class schedule_t } protected: - schedule_t() : editing_finished(false), current_stop(0) {} + schedule_t() : editing_finished(false), current_stop(0), spacing(0) {} public: enum schedule_type { @@ -70,6 +71,9 @@ public: virtual schedule_type get_type() const = 0; virtual waytype_t get_waytype() const = 0; + + uint16 get_spacing() const { return spacing; } + void set_spacing(uint16 s) { spacing = s; } /** * Get current stop of the schedule. @@ -116,12 +120,12 @@ public: /** * Inserts a coordinate at current_stop into the schedule. */ - bool insert(const grund_t* gr, uint8 minimum_loading = 0, uint8 waiting_time_shift = 0); + bool insert(const grund_t* gr, uint8 minimum_loading = 0, uint8 waiting_time_shift = 0, sint16 spacing_shift = 0, bool wait_for_time = false, uint16 delay_tolerance = 0); /** * Appends a coordinate to the schedule. */ - bool append(const grund_t* gr, uint8 minimum_loading = 0, uint8 waiting_time_shift = 0); + bool append(const grund_t* gr, uint8 minimum_loading = 0, uint8 waiting_time_shift = 0, sint16 spacing_shift = 0, bool wait_for_time = false, uint16 delay_tolerance = 0); /** * Cleanup a schedule, removes double entries. diff --git a/dataobj/schedule_entry.h b/dataobj/schedule_entry.h index e95b48997..a75fec556 100644 --- a/dataobj/schedule_entry.h +++ b/dataobj/schedule_entry.h @@ -12,10 +12,13 @@ struct schedule_entry_t public: schedule_entry_t() {} - schedule_entry_t(koord3d const& pos, uint const minimum_loading, sint8 const waiting_time_shift) : + schedule_entry_t(koord3d const& pos, uint const minimum_loading, sint8 const waiting_time_shift, sint16 spacing_shift, bool wait_for_time, uint16 delay_tolerance) : pos(pos), minimum_loading(minimum_loading), - waiting_time_shift(waiting_time_shift) + waiting_time_shift(waiting_time_shift), + spacing_shift(spacing_shift), + wait_for_time(wait_for_time), + delay_tolerance(delay_tolerance) {} /** @@ -37,11 +40,24 @@ public: * @author prissi */ sint8 waiting_time_shift; + + /** + * variables for wait_for_time feature brought from simutrans-extended + * @brought by THLeaderH + */ + sint16 spacing_shift; + + // Whether a convoy must wait for a time slot at this entry. + bool wait_for_time; + + // Departure can be delayed for this duration. + uint16 delay_tolerance; + }; inline bool operator ==(const schedule_entry_t &a, const schedule_entry_t &b) { - return a.pos == b.pos && a.minimum_loading == b.minimum_loading && a.waiting_time_shift == b.waiting_time_shift; + return a.pos == b.pos && a.minimum_loading == b.minimum_loading && a.waiting_time_shift == b.waiting_time_shift && a.spacing_shift == b.spacing_shift && a.wait_for_time == b.wait_for_time && a.delay_tolerance == b.delay_tolerance; } diff --git a/dataobj/settings.cc b/dataobj/settings.cc index 2fa15e5bd..3bbad81cc 100644 --- a/dataobj/settings.cc +++ b/dataobj/settings.cc @@ -243,6 +243,8 @@ settings_t::settings_t() : allow_underground_transformers = true; disable_make_way_public = false; + + spacing_shift_divisor = 24*60; // stop buildings cst_multiply_dock=-50000; @@ -831,6 +833,9 @@ void settings_t::rdwr(loadsave_t *file) if( file->get_version() > 120008 ) { file->rdwr_long(allow_merge_distant_halt); } + if( file->get_version() >= 120009 ) { + file->rdwr_short(spacing_shift_divisor); + } // otherwise the default values of the last one will be used } } @@ -1450,6 +1455,8 @@ void settings_t::parse_simuconf(tabfile_t& simuconf, sint16& disp_width, sint16& if( world_minimum_height>=world_maximum_height ) { world_minimum_height = world_maximum_height-1; } + + spacing_shift_divisor = contents.get_int("spacing_shift_divisor",spacing_shift_divisor); // Default pak file path objfilename = ltrim(contents.get_string("pak_file_path", "" ) ); diff --git a/dataobj/settings.h b/dataobj/settings.h index 535e2707b..6f731e1b6 100644 --- a/dataobj/settings.h +++ b/dataobj/settings.h @@ -286,6 +286,8 @@ private: // true if companies can make ways public bool disable_make_way_public; + + uint16 spacing_shift_divisor; public: /* the big cost section */ @@ -593,6 +595,9 @@ public: bool get_allow_underground_transformers() const { return allow_underground_transformers; } bool get_disable_make_way_public() const { return disable_make_way_public; } + + uint16 get_spacing_shift_divisor() const { return spacing_shift_divisor; } + void set_spacing_shift_divisor(uint16 s) { spacing_shift_divisor = s; } uint32 get_allow_merge_distant_halt() const { return allow_merge_distant_halt; } diff --git a/gui/schedule_gui.cc b/gui/schedule_gui.cc index 606f951d2..7207934e0 100644 --- a/gui/schedule_gui.cc +++ b/gui/schedule_gui.cc @@ -364,6 +364,59 @@ void schedule_gui_t::init(schedule_t* schedule_, player_t* player, convoihandle_ wait_load.set_rigid(true); } end_table(); + + // wait for time feature + add_table(3,3); + { + const uint16 spacing_divisor = welt->get_settings().get_spacing_shift_divisor(); + const uint16 p = schedule->get_spacing(); + + bt_wait_for_time.init(button_t::square_automatic, "Wait for time"); + bt_wait_for_time.set_tooltip("If this is set, convoys will wait until one of the specified times before departing, the specified times being fractions of a month."); + bt_wait_for_time.add_listener(this); + add_component(&bt_wait_for_time); + + lb_spacing.set_align(gui_label_t::align_t::centered); + lb_spacing.set_text_pointer(lb_spacing_str); + p==0 ? sprintf(lb_spacing_str,"off") : sprintf(lb_spacing_str,"%d",welt->get_settings().get_spacing_shift_divisor()/p); + add_component(&lb_spacing); + + new_component(); + + new_component("Spacing cnv/month, shift"); + + numimp_spacing.set_width( 60 ); + numimp_spacing.set_value( schedule->get_spacing() ); + numimp_spacing.set_limits( 0, spacing_divisor ); + numimp_spacing.set_increment_mode(1); + numimp_spacing.add_listener(this); + add_component(&numimp_spacing); + + numimp_spacing_shift.set_width( 90 ); + numimp_spacing_shift.set_value( schedule->get_current_entry().spacing_shift ); + numimp_spacing_shift.set_limits( 0, spacing_divisor ); + numimp_spacing_shift.set_increment_mode(1); + numimp_spacing_shift.add_listener(this); + add_component(&numimp_spacing_shift); + + new_component("Delay tolerance"); + + numimp_delay_tolerance.set_width( 90 ); + numimp_delay_tolerance.set_value( schedule->get_current_entry().delay_tolerance ); + numimp_delay_tolerance.set_limits( 0, p==0 ? 0 : spacing_divisor/schedule->get_spacing()/2 ); + numimp_delay_tolerance.set_increment_mode(1); + numimp_delay_tolerance.add_listener(this); + add_component(&numimp_delay_tolerance); + + new_component(); + } + end_table(); + + bt_same_spacing_shift.init(button_t::square_automatic, "Use same shift and tolerance for all stops"); + bt_same_spacing_shift.set_tooltip("Use one spacing shift and delay tolerance value for all stops in schedule."); + bt_same_spacing_shift.add_listener(this); + bt_same_spacing_shift.pressed = false; + add_component(&bt_same_spacing_shift); // return tickets if( !env_t::hide_rail_return_ticket || schedule->get_waytype()==road_wt || schedule->get_waytype()==air_wt || schedule->get_waytype()==water_wt ) { @@ -441,7 +494,14 @@ void schedule_gui_t::update_tool(bool set) void schedule_gui_t::update_selection() { lb_wait.set_color( SYSCOL_BUTTON_TEXT_DISABLED ); + lb_spacing.set_color( SYSCOL_BUTTON_TEXT_DISABLED ); + numimp_spacing_shift.disable(); + numimp_spacing_shift.set_value( 0 ); + numimp_spacing.disable(); + numimp_delay_tolerance.disable(); + numimp_delay_tolerance.set_value( 0 ); wait_load.disable(); + bt_wait_for_time.pressed = false; if( !schedule->empty() ) { schedule->set_current_stop( min(schedule->get_count()-1,schedule->get_current_stop()) ); @@ -467,6 +527,28 @@ void schedule_gui_t::update_selection() } } } + + if( schedule->entries[current_stop].wait_for_time ) { + lb_spacing.set_color( SYSCOL_TEXT ); + numimp_spacing_shift.enable(); + numimp_spacing_shift.set_value(schedule->entries[current_stop].spacing_shift); + numimp_spacing.enable(); + numimp_delay_tolerance.enable(); + numimp_delay_tolerance.set_value(schedule->entries[current_stop].delay_tolerance); + const uint16 spacing_divisor = welt->get_settings().get_spacing_shift_divisor(); + numimp_delay_tolerance.set_limits( 0, schedule->get_spacing()>0 ? spacing_divisor/schedule->get_spacing()/2 : 0 ); + bt_wait_for_time.pressed = true; + } + + if( bt_same_spacing_shift.pressed ) { + // set same spacing shift and delay tolerance to all entries. + const sint16 shift = schedule->entries[current_stop].spacing_shift; + const uint16 tolerance = schedule->entries[current_stop].delay_tolerance; + for( uint8 i=0; iget_count(); i++ ) { + schedule->entries[i].spacing_shift = shift; + schedule->entries[i].delay_tolerance = tolerance; + } + } } else { @@ -574,6 +656,27 @@ DBG_MESSAGE("schedule_gui_t::action_triggered()","komp=%p combo=%p",komp,&line_s update_selection(); } } + else if(komp == &numimp_spacing) { + schedule->set_spacing((uint16)p.i); + p.i==0 ? sprintf(lb_spacing_str,"off") : sprintf(lb_spacing_str,"%d",welt->get_settings().get_spacing_shift_divisor()/(uint16)p.i); + update_selection(); + } + else if(komp == &numimp_spacing_shift) { + if (!schedule->empty()) { + schedule->entries[schedule->get_current_stop()].spacing_shift = (sint16)p.i; + update_selection(); + } + } + else if(komp == &numimp_delay_tolerance) { + if (!schedule->empty()) { + schedule->entries[schedule->get_current_stop()].delay_tolerance = (sint16)p.i; + update_selection(); + } + } + else if(komp == &bt_wait_for_time) { + schedule->entries[schedule->get_current_stop()].wait_for_time = bt_wait_for_time.pressed; + update_selection(); + } else if(komp == &wait_load) { if(!schedule->empty()) { if (gui_waiting_time_item_t *item = dynamic_cast( wait_load.get_selected_item())) { diff --git a/gui/schedule_gui.h b/gui/schedule_gui.h index 777ad0d84..e7d085bff 100644 --- a/gui/schedule_gui.h +++ b/gui/schedule_gui.h @@ -52,9 +52,11 @@ class schedule_gui_t : public gui_frame_t, // always needed button_t bt_add, bt_insert, bt_remove; // stop management button_t bt_return; + button_t bt_wait_for_time, bt_same_spacing_shift; - gui_label_t lb_wait, lb_load; - gui_numberinput_t numimp_load; + char lb_spacing_str[10]; + gui_label_t lb_wait, lb_load, lb_spacing; + gui_numberinput_t numimp_load, numimp_spacing, numimp_spacing_shift, numimp_delay_tolerance; gui_combobox_t wait_load; schedule_gui_stats_t* stats; diff --git a/gui/simwin.cc b/gui/simwin.cc index 3f156b043..acfce0cfe 100644 --- a/gui/simwin.cc +++ b/gui/simwin.cc @@ -1781,6 +1781,10 @@ void win_display_flush(double konto) } #endif scr_coord_val w_left = 20+display_proportional_rgb(20, status_bar_text_y, time, ALIGN_LEFT, SYSCOL_STATUSBAR_TEXT, true); + char spacing_shift_str[15]; + const uint16 divisor = wl->get_settings().get_spacing_shift_divisor(); + sprintf(spacing_shift_str, "%d/%d", (wl->get_ticks()*divisor/wl->ticks_per_world_month)%divisor, divisor); + display_proportional_rgb(20+w_left, status_bar_text_y, spacing_shift_str, ALIGN_LEFT, SYSCOL_STATUSBAR_TEXT, true); scr_coord_val w_right = display_proportional_rgb(right_border-4, status_bar_text_y, info, ALIGN_RIGHT, SYSCOL_STATUSBAR_TEXT, true); scr_coord_val middle = (disp_width+((w_left+8)&0xFFF0)-((w_right+8)&0xFFF0))/2; diff --git a/simconvoi.cc b/simconvoi.cc index c90b88368..8b804c9a6 100644 --- a/simconvoi.cc +++ b/simconvoi.cc @@ -2968,8 +2968,24 @@ station_tile_search_ready: ; } // loading is finished => maybe drive on - if( loading_level >= loading_limit || no_load - || (schedule->get_current_entry().waiting_time_shift > 0 && welt->get_ticks() - arrived_time > (welt->ticks_per_world_month >> (16 - schedule->get_current_entry().waiting_time_shift)) ) ) { + bool can_go; + sint64 go_on_ticks = 0; + if( schedule->get_current_entry().wait_for_time && schedule->get_spacing()>0 ) { + // consider spacing + // subtract wait_lock (time) from spacing_shift + const sint32 spacing_shift = schedule->get_current_entry().spacing_shift * welt->ticks_per_world_month / welt->get_settings().get_spacing_shift_divisor() - time; + const sint32 spacing = welt->ticks_per_world_month / schedule->get_spacing(); + const uint32 delay_tolerance = schedule->get_current_entry().delay_tolerance * welt->ticks_per_world_month / welt->get_settings().get_spacing_shift_divisor(); + go_on_ticks = ((arrived_time - delay_tolerance - spacing_shift) / spacing + 1) * spacing + spacing_shift; + can_go = welt->get_ticks() >= go_on_ticks; + can_go |= (loading_limit > 0 && loading_level >= loading_limit); + } else { + can_go = loading_level >= loading_limit; + } + can_go |= no_load; + can_go |= (loading_limit > 0 && schedule->get_current_entry().waiting_time_shift > 0 && welt->get_ticks() - arrived_time > (welt->ticks_per_world_month >> (16 - schedule->get_current_entry().waiting_time_shift)) ); + + if( can_go ) { if( withdraw && (loading_level == 0 || goods_catg_index.empty()) ) { // destroy when empty @@ -2989,6 +3005,14 @@ station_tile_search_ready: ; state = ROUTING_1; loading_limit = 0; } + else if( schedule->get_current_entry().wait_for_time && schedule->get_spacing()>0 ) { + const sint32 ticks_remain = go_on_ticks - welt->get_ticks(); + if( ticks_remain>0 && ticks_remain<(sint32)time ) { + // this convoy is about to start. we don't want to wait for 2000 ms or more. + // just wait for ticks_remain + time = ticks_remain; + } + } INT_CHECK( "convoi_t::hat_gehalten" ); @@ -3852,3 +3876,14 @@ const char* convoi_t::send_to_depot(bool local) return txt; } + +uint16 convoi_t::get_time_to_depart() const { + if( !schedule->get_current_entry().wait_for_time ) { + return 0; + } + const uint16 divisor = welt->get_settings().get_spacing_shift_divisor(); + const sint32 spacing_shift = schedule->get_current_entry().spacing_shift; + const uint32 arr_time = arrived_time * divisor / welt->ticks_per_world_month; + const sint32 dep_time = ((arr_time - spacing_shift) * schedule->get_spacing() / divisor + 1) * divisor / schedule->get_spacing() + spacing_shift; + return dep_time - welt->get_ticks() * divisor / welt->ticks_per_world_month; +} diff --git a/simconvoi.h b/simconvoi.h index bea03bf40..51705be3d 100644 --- a/simconvoi.h +++ b/simconvoi.h @@ -771,6 +771,12 @@ public: * @date 12.06.2003 */ const sint32 &get_loading_limit() const { return loading_limit; } + + /** + * returns remaining time to departure in the range of spacing_shift_divisor + * @author THLeaderH + */ + uint16 get_time_to_depart() const; /** * Schedule convois for self destruction. Will be executed diff --git a/vehicle/simvehicle.cc b/vehicle/simvehicle.cc index 49d1dc6e4..f5bce8009 100644 --- a/vehicle/simvehicle.cc +++ b/vehicle/simvehicle.cc @@ -1741,7 +1741,11 @@ void vehicle_t::display_after(int xpos, int ypos, bool is_gobal) const case convoi_t::LOADING: if( state>=1 ) { - sprintf( tooltip_text, translator::translate("Loading (%i->%i%%)!"), cnv->get_loading_level(), cnv->get_loading_limit() ); + if( cnv->get_schedule()->get_current_entry().wait_for_time ) { + sprintf( tooltip_text, translator::translate("Waiting for schedule. %i left!"), cnv->get_time_to_depart() ); + } else { + sprintf( tooltip_text, translator::translate("Loading (%i->%i%%)!"), cnv->get_loading_level(), cnv->get_loading_limit() ); + } color = color_idx_to_rgb(COL_YELLOW); } break;