mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-11-05 00:45:42 +01:00
fix(roads): an axle getting on a crossroads takes the way the other axle took, one starting where it gets on
This commit is contained in:
@@ -12,6 +12,7 @@ http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#include "world/Track.h"
|
||||
#include "world/Event.h"
|
||||
#include "world/MemCell.h"
|
||||
#include "world/RoadOrder.h"
|
||||
#include "vehicle/DynObj.h"
|
||||
#include "vehicle/Driver.h"
|
||||
@@ -356,6 +357,14 @@ void roadpoint_node::import(cParser &Input, glm::dvec3 const &Offset)
|
||||
{
|
||||
m_state.stoplines = (Input.getToken<std::string>() != "no");
|
||||
}
|
||||
else if (token == "memcell")
|
||||
{
|
||||
m_state.memcell = Input.getToken<std::string>();
|
||||
if (m_state.memcell == "none")
|
||||
{
|
||||
m_state.memcell.clear();
|
||||
}
|
||||
}
|
||||
else if (token == "interval")
|
||||
{
|
||||
Input.getTokens();
|
||||
@@ -488,6 +497,7 @@ void roadpoint_node::bind()
|
||||
{
|
||||
case kind_type::crossing:
|
||||
{
|
||||
bind_memcell();
|
||||
bind_crossing();
|
||||
break;
|
||||
}
|
||||
@@ -574,6 +584,42 @@ void roadpoint_node::bind()
|
||||
}
|
||||
}
|
||||
|
||||
// name of the memory cell the crossing is told by
|
||||
std::string roadpoint_node::memcell_name() const
|
||||
{
|
||||
if (false == m_state.memcell.empty())
|
||||
{
|
||||
return m_state.memcell;
|
||||
}
|
||||
return m_name.empty() || m_name == "none" ? std::string{} : m_name + ":block";
|
||||
}
|
||||
|
||||
// finds the memory cell the crossing is told by, or makes it
|
||||
void roadpoint_node::bind_memcell()
|
||||
{
|
||||
auto const name{memcell_name()};
|
||||
if (name.empty())
|
||||
{
|
||||
m_memcell = nullptr;
|
||||
return;
|
||||
}
|
||||
if (m_memcell != nullptr && m_memcell->name() == name)
|
||||
{
|
||||
return;
|
||||
}
|
||||
m_memcell = simulation::Memory.find(name);
|
||||
if (m_memcell == nullptr)
|
||||
{
|
||||
scene::node_data nodedata;
|
||||
nodedata.name = name;
|
||||
m_memcell = new TMemCell(nodedata);
|
||||
// NOTE: it's made anew each time the scenery is loaded, so it isn't saved with it
|
||||
m_memcell->is_exportable = false;
|
||||
m_memcell->location(m_state.position);
|
||||
simulation::Memory.insert(m_memcell);
|
||||
}
|
||||
}
|
||||
|
||||
// finds the rails of a crossing, and the places the vehicles of each lane are to stop at
|
||||
void roadpoint_node::bind_crossing()
|
||||
{
|
||||
@@ -896,9 +942,12 @@ void roadpoint_node::update_crossing(double const Deltatime)
|
||||
}
|
||||
m_seen.swap(seen);
|
||||
}
|
||||
if ((m_hold > 0.0) != m_closed)
|
||||
// the scenery can close the entry, or keep it open, whatever the trains do
|
||||
auto const control{m_memcell != nullptr ? m_memcell->Value1() : 0.0};
|
||||
auto const closed{control > 0.0 || (control == 0.0 && m_hold > 0.0)};
|
||||
if (closed != m_closed)
|
||||
{
|
||||
close(m_hold > 0.0);
|
||||
close(closed);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -906,10 +955,15 @@ void roadpoint_node::update_crossing(double const Deltatime)
|
||||
void roadpoint_node::close(bool const Closed)
|
||||
{
|
||||
m_closed = Closed;
|
||||
// only the vehicles heading for the crossing are stopped, at the stops ahead of it; the ones past them leave it
|
||||
for (std::size_t idx = 0; idx < m_stops.size() && idx < m_events.size(); ++idx)
|
||||
{
|
||||
static_cast<stop_event *>(m_events[idx])->m_velocity = (Closed ? 0.0 : -1.0);
|
||||
}
|
||||
if (m_memcell != nullptr && m_memcell->Value2() != (Closed ? 1.0 : 0.0))
|
||||
{
|
||||
m_memcell->UpdateValues("", 0.0, Closed ? 1.0 : 0.0, basic_event::flags::value2);
|
||||
}
|
||||
}
|
||||
|
||||
// puts a waiting vehicle on the road when its time comes
|
||||
@@ -1150,6 +1204,10 @@ void roadpoint_node::export_as_text_(std::ostream &Output) const
|
||||
case kind_type::crossing:
|
||||
{
|
||||
Output << "clearance " << m_state.clearance << ' ' << "warning " << m_state.warning << ' ' << "stoplines " << (m_state.stoplines ? "yes" : "no") << ' ';
|
||||
if (false == m_state.memcell.empty())
|
||||
{
|
||||
Output << "memcell " << m_state.memcell << ' ';
|
||||
}
|
||||
break;
|
||||
}
|
||||
case kind_type::spawn:
|
||||
|
||||
@@ -25,19 +25,23 @@ class TDynamicObject;
|
||||
class basic_event;
|
||||
|
||||
// a place on a road where something happens to the traffic, or is painted for it:
|
||||
// - a level crossing: vehicles are stopped ahead of the rails for as long as a train is near,
|
||||
// - a level crossing: vehicles are stopped ahead of the rails for as long as a train is near, or the scenery says so,
|
||||
// - a point where vehicles appear, drawn from a set made ready while the scenery is loaded,
|
||||
// - a point where vehicles are taken away. the ones which came from a point of the previous kind go back to its set,
|
||||
// - a pedestrian crossing: stripes painted across the road.
|
||||
// scenery entries:
|
||||
// node <max> <min> <name> crossing <position> [clearance <m>] [warning <m>] [stoplines <yes|no>] endcrossing
|
||||
// node <max> <min> <name> crossing <position> [clearance <m>] [warning <m>] [stoplines <yes|no>] [memcell <name>] endcrossing
|
||||
// node <max> <min> <name> spawn <position> [interval <s>] [variation <0..1>] [velocity <km/h>] [count <n>]
|
||||
// [node <max> <min> <name> dynamic <folder> <skin> <type> <path> <offset> <driver> <velocity> <load> [<load type>] enddynamic]... endspawn
|
||||
// node <max> <min> <name> despawn <position> [radius <m>] enddespawn
|
||||
// node <max> <min> <name> crosswalk <position> [length <m>] endcrosswalk
|
||||
// the vehicles of a spawn point are given the way vehicles are put in a scenery; where they're put and how they're driven
|
||||
// is up to the point, so the path, the offset, the driver and the velocity of these entries don't matter. the listed vehicles
|
||||
// are what the point draws from to have <count> vehicles of its own on the roads; one which gets stuck is taken off the road
|
||||
// are what the point draws from to have <count> vehicles of its own on the roads; one which gets stuck is taken off the road.
|
||||
// a level crossing is told by the scenery through a memory cell: the one named in its entry, or "<name of the crossing>:block",
|
||||
// made if the scenery doesn't have it. its first value above zero closes the entry onto the crossing, below zero keeps it open
|
||||
// whatever comes by the rails, and zero leaves it to the trains. the vehicles past the stops, on the crossing or leaving it, drive on.
|
||||
// the second value is set by the crossing: 1 while its entry is closed, 0 while it's open
|
||||
class roadpoint_node : public scene::basic_node
|
||||
{
|
||||
|
||||
@@ -71,6 +75,7 @@ class roadpoint_node : public scene::basic_node
|
||||
float clearance{6.f}; // distance along the road from the point to the places the vehicles stop at
|
||||
float warning{800.f}; // how far along the rails a train coming closes the crossing
|
||||
bool stoplines{true}; // lines are painted where the vehicles stop
|
||||
std::string memcell; // name of the memory cell the scenery tells the crossing by; empty: the one named after the crossing
|
||||
// point where vehicles appear
|
||||
float interval{20.f}; // seconds from a vehicle to the next
|
||||
float variation{0.5f}; // part of the interval which is drawn at random
|
||||
@@ -135,6 +140,13 @@ class roadpoint_node : public scene::basic_node
|
||||
{
|
||||
return m_rails.size();
|
||||
}
|
||||
// memory cell the level crossing is told by, nullptr if there's none
|
||||
TMemCell *memcell() const
|
||||
{
|
||||
return m_memcell;
|
||||
}
|
||||
// name of that cell: the one the entry gives, or the one made of the name of the crossing; empty if neither is there
|
||||
std::string memcell_name() const;
|
||||
// vehicles made for a spawn point, and how many of them wait for their turn
|
||||
std::size_t vehicles() const
|
||||
{
|
||||
@@ -169,6 +181,7 @@ class roadpoint_node : public scene::basic_node
|
||||
void deserialize_(std::istream &Input) override;
|
||||
void export_as_text_(std::ostream &Output) const override;
|
||||
void bind_crossing();
|
||||
void bind_memcell();
|
||||
void update_crossing(double const Deltatime);
|
||||
void update_spawn(double const Deltatime);
|
||||
void update_despawn();
|
||||
@@ -184,6 +197,7 @@ class roadpoint_node : public scene::basic_node
|
||||
std::map<TDynamicObject const *, double> m_seen; // rail vehicles near, with how far each was the last time
|
||||
bool m_closed{false};
|
||||
double m_hold{0.0}; // time left for the crossing to stay closed after the last train was seen
|
||||
TMemCell *m_memcell{nullptr}; // what the scenery tells the crossing by
|
||||
double m_scan{0.0}; // time left to the next look at the rails
|
||||
// spawn and despawn
|
||||
std::vector<TTrack *> m_lanes; // spawn: the lane the vehicles are put on. despawn: lanes the vehicles are taken from
|
||||
|
||||
@@ -22,6 +22,31 @@ http://mozilla.org/MPL/2.0/.
|
||||
#include "vehicle/Driver.h"
|
||||
#include "utilities/Logs.h"
|
||||
|
||||
namespace {
|
||||
|
||||
// how far the start of a way across a crossroads can be from the end of the path an axle leaves, to be where the axle gets on
|
||||
constexpr double cross_reach { 1.0 };
|
||||
|
||||
// number of the ways across a crossroads; each can be taken either way
|
||||
int cross_count( TTrack const &Cross ) {
|
||||
|
||||
return Cross.SwitchExtension->iRoads == 4 ? 6 : 3;
|
||||
}
|
||||
|
||||
// where the way of specified code starts, and the direction it leads in there. the code is the number of the way, 1..6,
|
||||
// negative if the way is taken against its points
|
||||
bool cross_start( TTrack const &Cross, int const Code, glm::dvec3 &Point, glm::dvec3 &Direction ) {
|
||||
|
||||
if( Code == 0 || std::abs( Code ) > cross_count( Cross ) ) { return false; }
|
||||
auto const &segment { Cross.SwitchExtension->Segments[ std::abs( Code ) - 1 ] };
|
||||
if( segment == nullptr ) { return false; }
|
||||
Point = ( Code > 0 ? segment->FastGetPoint_0() : segment->FastGetPoint_1() );
|
||||
Direction = ( Code > 0 ? segment->FastGetPoint( 0.05 ) : segment->FastGetPoint( 0.95 ) ) - Point;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TTrackFollower::~TTrackFollower()
|
||||
{
|
||||
}
|
||||
@@ -30,6 +55,8 @@ bool TTrackFollower::Init(TTrack *pTrack, TDynamicObject *NewOwner, double fDir)
|
||||
{
|
||||
fDirection = fDir;
|
||||
Owner = NewOwner;
|
||||
// the axle is put on the track, it doesn't come from the one it was on before
|
||||
pCurrentSegment = nullptr;
|
||||
SetCurrentTrack(pTrack, 0);
|
||||
iEventFlag = 3; // na torze startowym również wykonać eventy 1/2
|
||||
iEventallFlag = 3;
|
||||
@@ -92,6 +119,9 @@ TTrack * TTrackFollower::SetCurrentTrack(TTrack *pTrack, int end)
|
||||
// kiedy powstaje!
|
||||
iSegment = 1; // doraźna poprawka
|
||||
}
|
||||
// the way has to start where the axle gets on. the one the driver picked may not: the driver may have looked
|
||||
// again since the other axle got on, the vehicle may have no driver of its own, or it may be going backwards
|
||||
iSegment = CrossEntry( *pTrack, iSegment, end );
|
||||
if ((end ? iSegment : -iSegment) < 0)
|
||||
fDirection = -fDirection; // wtórna zmiana
|
||||
pTrack->SwitchForced(abs(iSegment) - 1, nullptr); // wybór zapamiętanego segmentu
|
||||
@@ -123,6 +153,67 @@ TTrack * TTrackFollower::SetCurrentTrack(TTrack *pTrack, int end)
|
||||
return pCurrentTrack;
|
||||
};
|
||||
|
||||
// picks the way across the crossroads (Cross) for the axle which gets on it by specified end of the path it's on. it's the way
|
||||
// taken by another axle of the vehicle, or of a vehicle coupled to it, which is on the crossroads already; or the one the driver
|
||||
// picked (Wish); or, if neither starts where the axle gets on, the one leading the most straight on of the ways which start there
|
||||
int TTrackFollower::CrossEntry( TTrack const &Cross, int const Wish, int const End ) const {
|
||||
|
||||
if( pCurrentSegment == nullptr || Cross.SwitchExtension == nullptr ) { return Wish; }
|
||||
|
||||
auto const joint { End ? pCurrentSegment->FastGetPoint_1() : pCurrentSegment->FastGetPoint_0() };
|
||||
auto const starts_here = [&]( int const Code ) {
|
||||
glm::dvec3 point, direction;
|
||||
return cross_start( Cross, Code, point, direction ) && glm::distance( point, joint ) < cross_reach; };
|
||||
|
||||
if( Owner != nullptr ) {
|
||||
for( auto const *vehicle : { Owner, Owner->Prev(), Owner->Next() } ) {
|
||||
if( vehicle == nullptr ) { continue; }
|
||||
for( auto const *axle : { &vehicle->Axle0, &vehicle->Axle1 } ) {
|
||||
if( axle == this || axle->pCurrentTrack != &Cross || axle->iSegment == 0 ) { continue; }
|
||||
if( starts_here( axle->iSegment ) ) { return axle->iSegment; }
|
||||
// a vehicle going back over the crossroads gets on the way at its other end
|
||||
if( starts_here( -axle->iSegment ) ) { return -axle->iSegment; }
|
||||
}
|
||||
}
|
||||
}
|
||||
if( starts_here( Wish ) ) { return Wish; }
|
||||
|
||||
auto const incoming { joint - ( End ? pCurrentSegment->FastGetPoint( 0.95 ) : pCurrentSegment->FastGetPoint( 0.05 ) ) };
|
||||
auto nearest { cross_reach };
|
||||
for( int way = 1; way <= cross_count( Cross ); ++way ) {
|
||||
for( auto const code : { way, -way } ) {
|
||||
glm::dvec3 point, direction;
|
||||
if( cross_start( Cross, code, point, direction ) ) {
|
||||
nearest = std::min( nearest, glm::distance( point, joint ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
if( nearest >= cross_reach ) {
|
||||
// no way starts there, the crossroads isn't joined with the path; it's left the way it was
|
||||
return Wish;
|
||||
}
|
||||
auto result { Wish };
|
||||
auto straightest { -2.0 };
|
||||
for( int way = 1; way <= cross_count( Cross ); ++way ) {
|
||||
for( auto const code : { way, -way } ) {
|
||||
glm::dvec3 point, direction;
|
||||
if( false == cross_start( Cross, code, point, direction )
|
||||
|| glm::distance( point, joint ) > nearest + 0.05 ) {
|
||||
continue;
|
||||
}
|
||||
auto const straight {
|
||||
glm::length2( incoming ) > 0.0 && glm::length2( direction ) > 0.0 ?
|
||||
glm::dot( glm::normalize( incoming ), glm::normalize( direction ) ) :
|
||||
-1.0 };
|
||||
if( straight > straightest ) {
|
||||
straightest = straight;
|
||||
result = code;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool TTrackFollower::Move(double fDistance, bool bPrimary)
|
||||
{ // przesuwanie wózka po torach o odległość (fDistance), z wyzwoleniem eventów
|
||||
// bPrimary=true - jest pierwszą osią w pojeździe, czyli generuje eventy i przepisuje pojazd
|
||||
|
||||
@@ -50,6 +50,7 @@ public:
|
||||
// methods
|
||||
bool ComputatePosition(); // przeliczenie pozycji na torze
|
||||
int JoinedEnd( int const End ) const;
|
||||
int CrossEntry( TTrack const &Cross, int const Wish, int const End ) const;
|
||||
// members
|
||||
TTrack *pCurrentTrack = nullptr; // na którym torze siê znajduje
|
||||
std::shared_ptr<TSegment> pCurrentSegment; // zwrotnice mog¹ mieæ dwa segmenty
|
||||
|
||||
Reference in New Issue
Block a user