Files
maszyna/TrkFoll.cpp
2015-04-03 13:34:06 +00:00

296 lines
13 KiB
C++

//---------------------------------------------------------------------------
/*
MaSzyna EU07 locomotive simulator
Copyright (C) 2001-2004 Marcin Wozniak and others
*/
#include "system.hpp"
#include "classes.hpp"
#include "opengl/glew.h"
#include "opengl/glut.h"
#pragma hdrstop
#include "TrkFoll.h"
#include "Globals.h"
#include "DynObj.h"
#include "Ground.h"
#include "Event.h"
__fastcall TTrackFollower::TTrackFollower()
{
pCurrentTrack=NULL;
pCurrentSegment=NULL;
fCurrentDistance=0;
pPosition=vAngles=vector3(0,0,0);
fDirection=1; //jest przodem do Point2
fOffsetH=0.0; //na starcie stoi na środku
}
__fastcall TTrackFollower::~TTrackFollower()
{
}
bool __fastcall TTrackFollower::Init(TTrack *pTrack,TDynamicObject *NewOwner,double fDir)
{
fDirection=fDir;
Owner=NewOwner;
SetCurrentTrack(pTrack,0);
iEventFlag=3; //na torze startowym również wykonać eventy 1/2
iEventallFlag=3;
if ((pCurrentSegment))// && (pCurrentSegment->GetLength()<fFirstDistance))
return false;
return true;
}
TTrack* __fastcall TTrackFollower::SetCurrentTrack(TTrack *pTrack,int end)
{//przejechanie na inny odcinkek toru, z ewentualnym rozpruciem
if (pTrack)
switch (pTrack->eType)
{case tt_Switch: //jeśli zwrotnica, to przekładamy ją, aby uzyskać dobry segment
{int i=(end?pCurrentTrack->iNextDirection:pCurrentTrack->iPrevDirection);
if (i>0) //jeżeli wjazd z ostrza
pTrack->SwitchForced(i>>1,Owner); //to przełożenie zwrotnicy - rozprucie!
}
break;
case tt_Cross: //skrzyżowanie trzeba tymczasowo przełączyć, aby wjechać na właściwy tor
{iSegment=Owner->RouteWish(pTrack); //nr segmentu został ustalony podczas skanowania
//Ra 2014-08: aby ustalić dalszą trasę, należy zapytać AI - trasa jest ustalana podczas skanowania
//Ra 15-01: zapytanie AI nie wybiera segmentu - kolejny skanujący może przestawić
//pTrack->CrossSegment(end?pCurrentTrack->iNextDirection:pCurrentTrack->iPrevDirection,i); //ustawienie właściwego wskaźnika
//powinno zwracać kierunek do zapamiętania, bo segmenty mogą mieć różny kierunek
//if fDirection=(iSegment>0)?1.0:-1.0; //kierunek na tym segmencie jest ustalany bezpośrednio
if (iSegment==0)
{//to jest błędna sytuacja - generuje pętle zawracające za skrzyżowaniem - ustalić, kiedy powstaje!
iSegment=1; //doraźna poprawka
}
if ((end?iSegment:-iSegment)<0)
fDirection=-fDirection; //wtórna zmiana
pTrack->SwitchForced(abs(iSegment)-1,NULL); //wybór zapamiętanego segmentu
}
break;
}
if (!pTrack)
{//gdy nie ma toru w kierunku jazdy
pTrack=pCurrentTrack->NullCreate(end); //tworzenie toru wykolejącego na przedłużeniu pCurrentTrack
if (!end) //jeśli dodana od strony zero, to zmiana kierunku
fDirection=-fDirection; //wtórna zmiana
//if (pTrack->iCategoryFlag&2)
//{//jeśli samochód, zepsuć na miejscu
// Owner->MoverParameters->V=0; //zatrzymać
// Owner->MoverParameters->Power=0; //ukraść silnik
// Owner->MoverParameters->AccS=0; //wchłonąć moc
// Global::iPause|=1; //zapauzowanie symulacji
//}
}
else
{//najpierw +1, później -1, aby odcinek izolowany wspólny dla tych torów nie wykrył zera
pTrack->AxleCounter(+1,Owner); //zajęcie nowego toru
if (pCurrentTrack) pCurrentTrack->AxleCounter(-1,Owner); //opuszczenie tamtego toru
}
pCurrentTrack=pTrack;
pCurrentSegment=(pCurrentTrack?pCurrentTrack->CurrentSegment():NULL);
if (!pCurrentTrack)
Error(Owner->MoverParameters->Name+" at NULL track");
return pCurrentTrack;
};
bool __fastcall 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
//Ra: zwraca false, jeśli pojazd ma być usunięty
fDistance*=fDirection; //dystans mnożnony przez kierunek
double s; //roboczy dystans
double dir; //zapamiętany kierunek do sprawdzenia, czy się zmienił
bool bCanSkip; //czy przemieścić pojazd na inny tor
while (true) //pętla wychodzi, gdy przesunięcie wyjdzie zerowe
{//pętla przesuwająca wózek przez kolejne tory, aż do trafienia w jakiś
if (!pCurrentTrack) return false; //nie ma toru, to nie ma przesuwania
if (pCurrentTrack->iEvents) //sumaryczna informacja o eventach
{//omijamy cały ten blok, gdy tor nie ma on żadnych eventów (większość nie ma)
if (fDistance<0)
{
if (iSetFlag(iEventFlag,-1)) //zawsze zeruje flagę sprawdzenia, jak mechanik dosiądzie, to się nie wykona
if (Owner->Mechanik) //tylko dla jednego członu
//if (TestFlag(iEventFlag,1)) //McZapkie-280503: wyzwalanie event tylko dla pojazdow z obsada
if (bPrimary && pCurrentTrack->evEvent1 && (!pCurrentTrack->evEvent1->iQueued))
Global::AddToQuery(pCurrentTrack->evEvent1,Owner); //dodanie do kolejki
//Owner->RaAxleEvent(pCurrentTrack->Event1); //Ra: dynamic zdecyduje, czy dodać do kolejki
//if (TestFlag(iEventallFlag,1))
if (iSetFlag(iEventallFlag,-1)) //McZapkie-280503: wyzwalanie eventall dla wszystkich pojazdow
if (bPrimary && pCurrentTrack->evEventall1 && (!pCurrentTrack->evEventall1->iQueued))
Global::AddToQuery(pCurrentTrack->evEventall1,Owner); //dodanie do kolejki
//Owner->RaAxleEvent(pCurrentTrack->Eventall1); //Ra: dynamic zdecyduje, czy dodać do kolejki
}
else if (fDistance>0)
{
if (iSetFlag(iEventFlag,-2)) //zawsze ustawia flagę sprawdzenia, jak mechanik dosiądzie, to się nie wykona
if (Owner->Mechanik) //tylko dla jednego członu
//if (TestFlag(iEventFlag,2)) //sprawdzanie jest od razu w pierwszym warunku
if (bPrimary && pCurrentTrack->evEvent2 && (!pCurrentTrack->evEvent2->iQueued))
Global::AddToQuery(pCurrentTrack->evEvent2,Owner);
//Owner->RaAxleEvent(pCurrentTrack->Event2); //Ra: dynamic zdecyduje, czy dodać do kolejki
//if (TestFlag(iEventallFlag,2))
if (iSetFlag(iEventallFlag,-2)) //sprawdza i zeruje na przyszłość, true jeśli zmieni z 2 na 0
if (bPrimary && pCurrentTrack->evEventall2 && (!pCurrentTrack->evEventall2->iQueued))
Global::AddToQuery(pCurrentTrack->evEventall2,Owner);
//Owner->RaAxleEvent(pCurrentTrack->Eventall2); //Ra: dynamic zdecyduje, czy dodać do kolejki
}
else //if (fDistance==0) //McZapkie-140602: wyzwalanie zdarzenia gdy pojazd stoi
{
if (Owner->Mechanik) //tylko dla jednego członu
if (pCurrentTrack->evEvent0)
if (!pCurrentTrack->evEvent0->iQueued)
Global::AddToQuery(pCurrentTrack->evEvent0,Owner);
//Owner->RaAxleEvent(pCurrentTrack->Event0); //Ra: dynamic zdecyduje, czy dodać do kolejki
if (pCurrentTrack->evEventall0)
if (!pCurrentTrack->evEventall0->iQueued)
Global::AddToQuery(pCurrentTrack->evEventall0,Owner);
//Owner->RaAxleEvent(pCurrentTrack->Eventall0); //Ra: dynamic zdecyduje, czy dodać do kolejki
}
}
if (!pCurrentSegment) //jeżeli nie ma powiązanego segmentu toru?
return false;
//if (fDistance==0.0) return true; //Ra: jak stoi, to chyba dalej nie ma co kombinować?
s=fCurrentDistance+fDistance; //doliczenie przesunięcia
//Ra: W Point2 toru może znajdować się "dziura", która zamieni energię kinetyczną
// ruchu wzdłużnego na energię potencjalną, zamieniającą się potem na energię
// sprężystości na amortyzatorach. Należałoby we wpisie toru umieścić współczynnik
// podziału energii kinetycznej.
// Współczynnik normalnie 1, z dziurą np. 0.99, a -1 będzie oznaczało 100% odbicia (kozioł).
// Albo w postaci kąta: normalnie 0°, a 180° oznacza 100% odbicia (cosinus powyższego).
/*
if (pCurrentTrack->eType==tt_Cross)
{//zjazdu ze skrzyżowania nie da się określić przez (iPrevDirection) i (iNextDirection)
//int segment=Owner->RouteWish(pCurrentTrack); //numer segmentu dla skrzyżowań
//pCurrentTrack->SwitchForced(abs(segment)-1,NULL); //tymczasowo ustawienie tego segmentu
//pCurrentSegment=pCurrentTrack->CurrentSegment(); //odświeżyć sobie wskaźnik segmentu (?)
}
*/
if (s<0)
{//jeśli przekroczenie toru od strony Point1
bCanSkip=bPrimary?pCurrentTrack->CheckDynamicObject(Owner):false;
if (bCanSkip) //tylko główna oś przenosi pojazd do innego toru
Owner->MyTrack->RemoveDynamicObject(Owner); //zdejmujemy pojazd z dotychczasowego toru
dir=fDirection;
if (pCurrentTrack->eType==tt_Cross)
{if (!SetCurrentTrack(pCurrentTrack->Neightbour(iSegment,fDirection),0))
return false; //wyjście z błędem
}
else if (!SetCurrentTrack(pCurrentTrack->Neightbour(-1,fDirection),0)) //ustawia fDirection
return false; //wyjście z błędem
if (dir==fDirection) //(pCurrentTrack->iPrevDirection)
{//gdy kierunek bez zmiany (Point1->Point2)
fCurrentDistance=pCurrentSegment->GetLength();
fDistance=s;
}
else
{//gdy zmiana kierunku toru (Point1->Point1)
fCurrentDistance=0;
fDistance=-s;
}
if (bCanSkip)
{//jak główna oś, to dodanie pojazdu do nowego toru
pCurrentTrack->AddDynamicObject(Owner);
iEventFlag=3; //McZapkie-020602: umozliwienie uruchamiania event1,2 po zmianie toru
iEventallFlag=3; //McZapkie-280503: jw, dla eventall1,2
if (!Owner->MyTrack) return false;
}
continue;
}
else if (s>pCurrentSegment->GetLength())
{//jeśli przekroczenie toru od strony Point2
bCanSkip=bPrimary?pCurrentTrack->CheckDynamicObject(Owner):false;
if (bCanSkip) //tylko główna oś przenosi pojazd do innego toru
Owner->MyTrack->RemoveDynamicObject(Owner); //zdejmujemy pojazd z dotychczasowego toru
fDistance=s-pCurrentSegment->GetLength();
dir=fDirection;
if (pCurrentTrack->eType==tt_Cross)
{if (!SetCurrentTrack(pCurrentTrack->Neightbour(iSegment,fDirection),1))
return false; //wyjście z błędem
}
else if (!SetCurrentTrack(pCurrentTrack->Neightbour(1,fDirection),1)) //ustawia fDirection
return false; //wyjście z błędem
if (dir!=fDirection) //(pCurrentTrack->iNextDirection)
{//gdy zmiana kierunku toru (Point2->Point2)
fDistance=-fDistance; //(s-pCurrentSegment->GetLength());
fCurrentDistance=pCurrentSegment->GetLength();
}
else //gdy kierunek bez zmiany (Point2->Point1)
fCurrentDistance=0;
if (bCanSkip)
{//jak główna oś, to dodanie pojazdu do nowego toru
pCurrentTrack->AddDynamicObject(Owner);
iEventFlag=3; //McZapkie-020602: umozliwienie uruchamiania event1,2 po zmianie toru
iEventallFlag=3;
if (!Owner->MyTrack) return false;
}
continue;
}
else
{//gdy zostaje na tym samym torze (przesuwanie już nie zmienia toru)
if (bPrimary)
{//tylko gdy początkowe ustawienie, dodajemy eventy stania do kolejki
if (Owner->MoverParameters->ActiveCab!=0)
//if (Owner->MoverParameters->CabNo!=0)
{
if (pCurrentTrack->evEvent1 && pCurrentTrack->evEvent1->fDelay<=-1.0f)
Global::AddToQuery(pCurrentTrack->evEvent1,Owner);
if (pCurrentTrack->evEvent2 && pCurrentTrack->evEvent2->fDelay<=-1.0f)
Global::AddToQuery(pCurrentTrack->evEvent2,Owner);
}
if (pCurrentTrack->evEventall1 && pCurrentTrack->evEventall1->fDelay<=-1.0f)
Global::AddToQuery(pCurrentTrack->evEventall1,Owner);
if (pCurrentTrack->evEventall2 && pCurrentTrack->evEventall2->fDelay<=-1.0f)
Global::AddToQuery(pCurrentTrack->evEventall2,Owner);
}
fCurrentDistance=s;
//fDistance=0;
return ComputatePosition(); //przeliczenie XYZ, true o ile nie wyjechał na NULL
}
}
};
bool __fastcall TTrackFollower::ComputatePosition()
{//ustalenie współrzędnych XYZ
if (pCurrentSegment) //o ile jest tor
{
pCurrentSegment->RaPositionGet(fCurrentDistance,pPosition,vAngles);
if (fDirection<0) //kąty zależą jeszcze od zwrotu na torze
{//kąty są w przedziale <-M_PI;M_PI>
vAngles.x=-vAngles.x; //przechyłka jest w przecinwą stronę
vAngles.y=-vAngles.y; //pochylenie jest w przecinwą stronę
vAngles.z+=(vAngles.z>=M_PI)?-M_PI:M_PI; //ale kierunek w planie jest obrócony o 180°
}
if (fOffsetH!=0.0)
{//jeśli przesunięcie względem osi toru, to je doliczyć
}
return true;
}
return false;
}
#if RENDER_CONE
#include "opengl/glew.h"
#include "opengl/glut.h"
void __fastcall TTrackFollower::Render(float fNr)
{//funkcja rysująca stożek w miejscu osi
glPushMatrix(); //matryca kamery
glTranslatef(pPosition.x,pPosition.y+6,pPosition.z); //6m ponad
glRotated(RadToDeg(-vAngles.z),0,1,0); //obrót względem osi OY
//glRotated(RadToDeg(vAngles.z),0,1,0); //obrót względem osi OY
glDisable(GL_LIGHTING);
glColor3f(1.0,1.0-fNr,1.0-fNr); //biały dla 0, czerwony dla 1
//glutWireCone(promień podstawy,wysokość,kątność podstawy,ilość segmentów na wysokość)
glutWireCone(0.5,2,4,1); //rysowanie stożka (ostrosłupa o podstawie wieloboka)
glEnable(GL_LIGHTING);
glPopMatrix();
}
#endif
//---------------------------------------------------------------------------
#pragma package(smart_init)