mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-03-22 15:05:03 +01:00
2. Możliwość zdefiniowania koloru tła ekranu ładowania w eu07.ini. 3. Dodanie logowania ruchu ramek WM_COPYDATA do oddzielnego pliku. 4. Dodana ramka skrócona aktywnych AI. 5. Dodana ramka sterowania uszkodzeniami pojazdów. 6. Dodanie kręciołka od świateł (w tym trakcja wielokrotna). 7. Rozbudowa asynchronów, m.in. tempomat, zadawanie siły (w kN), przeciwpoślizg, czyste ED itd. 8. Dodanie animacji drtzwi odskokowo-przesuwnych. 9. Dodanie możliwości uszkadzania pojazdów (EN57). 10. Uruchomienie sprzęgu WN, zwłaszcza w asynchronach - do przejrzenia logowanie braków napięcia. 11. Wyłączanie radiostopu przez wyłączanie radia, a nie przez ruch zaworem maszynisty. 12. Usunięcie wyłącznika ciśnieniowego w EN57 (proteza, trzeba zrobić wpis na to). 13. Wstępna wersja hamowania rekuperacyjnego wraz z przechodzeniem na hamowanie oporowe. 14. Cała masa nowych zmiennych stanu dostępna dla ekranu (Pythona). 15. Nowa stara smuga z dawnego SPKSu. 16. Inne, które mogłem zapomnieć lub nie uszczegółowić. 17. Poprawione wyświetlanie haslera
5666 lines
251 KiB
C++
5666 lines
251 KiB
C++
/*
|
|
This Source Code Form is subject to the
|
|
terms of the Mozilla Public License, v.
|
|
2.0. If a copy of the MPL was not
|
|
distributed with this file, You can
|
|
obtain one at
|
|
http://mozilla.org/MPL/2.0/.
|
|
*/
|
|
/*
|
|
MaSzyna EU07 locomotive simulator
|
|
Copyright (C) 2001-2004 Marcin Wozniak, Maciej Czapkiewicz and others
|
|
|
|
*/
|
|
|
|
#include "system.hpp"
|
|
#include "classes.hpp"
|
|
#pragma hdrstop
|
|
|
|
#include "DynObj.h"
|
|
|
|
#include "Timer.h"
|
|
#include "Usefull.h"
|
|
// McZapkie-260202
|
|
#include "Globals.h"
|
|
#include "Texture.h"
|
|
#include "AirCoupler.h"
|
|
|
|
#include "TractionPower.h"
|
|
#include "Ground.h" //bo Global::pGround->bDynamicRemove
|
|
#include "Event.h"
|
|
#include "Driver.h"
|
|
#include "Camera.h" //bo likwidujemy trzęsienie
|
|
#include "Console.h"
|
|
#include "Traction.h"
|
|
#pragma package(smart_init)
|
|
|
|
// Ra: taki zapis funkcjonuje lepiej, ale może nie jest optymalny
|
|
#define vWorldFront vector3(0, 0, 1)
|
|
#define vWorldUp vector3(0, 1, 0)
|
|
#define vWorldLeft CrossProduct(vWorldUp, vWorldFront)
|
|
|
|
// Ra: bo te poniżej to się powielały w każdym module odobno
|
|
// vector3 vWorldFront=vector3(0,0,1);
|
|
// vector3 vWorldUp=vector3(0,1,0);
|
|
// vector3 vWorldLeft=CrossProduct(vWorldUp,vWorldFront);
|
|
|
|
#define M_2PI 6.283185307179586476925286766559;
|
|
const float maxrot = (M_PI / 3.0); // 60°
|
|
|
|
//---------------------------------------------------------------------------
|
|
void TAnimPant::AKP_4E()
|
|
{ // ustawienie wymiarów dla pantografu AKP-4E
|
|
vPos = vector3(0, 0, 0); // przypisanie domyśnych współczynników do pantografów
|
|
fLenL1 = 1.22; // 1.176289 w modelach
|
|
fLenU1 = 1.755; // 1.724482197 w modelach
|
|
fHoriz = 0.535; // 0.54555075 przesunięcie ślizgu w długości pojazdu względem
|
|
// osi obrotu dolnego
|
|
// ramienia
|
|
fHeight = 0.07; // wysokość ślizgu ponad oś obrotu
|
|
fWidth = 0.635; // połowa szerokości ślizgu, 0.635 dla AKP-1 i AKP-4E
|
|
fAngleL0 = DegToRad(2.8547285515689267247882521833308);
|
|
fAngleL = fAngleL0; // początkowy kąt dolnego ramienia
|
|
// fAngleU0=acos((1.22*cos(fAngleL)+0.535)/1.755); //górne ramię
|
|
fAngleU0 = acos((fLenL1 * cos(fAngleL) + fHoriz) / fLenU1); // górne ramię
|
|
fAngleU = fAngleU0; // początkowy kąt
|
|
// PantWys=1.22*sin(fAngleL)+1.755*sin(fAngleU); //wysokość początkowa
|
|
PantWys = fLenL1 * sin(fAngleL) + fLenU1 * sin(fAngleU) + fHeight; // wysokość początkowa
|
|
PantTraction = PantWys;
|
|
hvPowerWire = NULL;
|
|
fWidthExtra = 0.381; //(2.032m-1.027)/2
|
|
// poza obszarem roboczym jest aproksymacja łamaną o 5 odcinkach
|
|
fHeightExtra[0] = 0.0; //+0.0762
|
|
fHeightExtra[1] = -0.01; //+0.1524
|
|
fHeightExtra[2] = -0.03; //+0.2286
|
|
fHeightExtra[3] = -0.07; //+0.3048
|
|
fHeightExtra[4] = -0.15; //+0.3810
|
|
};
|
|
//---------------------------------------------------------------------------
|
|
int TAnim::TypeSet(int i, int fl)
|
|
{ // ustawienie typu animacji i zależnej od
|
|
// niego ilości animowanych submodeli
|
|
fMaxDist = -1.0; // normalnie nie pokazywać
|
|
switch (i)
|
|
{ // maska 0x000F: ile używa wskaźników na submodele (0 gdy jeden,
|
|
// wtedy bez tablicy)
|
|
// maska 0x00F0:
|
|
// 0-osie,1-drzwi,2-obracane,3-zderzaki,4-wózki,5-pantografy,6-tłoki
|
|
// maska 0xFF00: ile używa liczb float dla współczynników i stanu
|
|
case 0:
|
|
iFlags = 0x000;
|
|
break; // 0-oś
|
|
case 1:
|
|
iFlags = 0x010;
|
|
break; // 1-drzwi
|
|
case 2:
|
|
iFlags = 0x020;
|
|
fParam = fl ? new float[fl] : NULL;
|
|
iFlags += fl << 8;
|
|
break; // 2-wahacz, dźwignia itp.
|
|
case 3:
|
|
iFlags = 0x030;
|
|
break; // 3-zderzak
|
|
case 4:
|
|
iFlags = 0x040;
|
|
break; // 4-wózek
|
|
case 5: // 5-pantograf - 5 submodeli
|
|
iFlags = 0x055;
|
|
fParamPants = new TAnimPant();
|
|
fParamPants->AKP_4E();
|
|
break;
|
|
case 6:
|
|
iFlags = 0x068;
|
|
break; // 6-tłok i rozrząd - 8 submodeli
|
|
default:
|
|
iFlags = 0;
|
|
}
|
|
yUpdate = NULL;
|
|
return iFlags & 15; // ile wskaźników rezerwować dla danego typu animacji
|
|
};
|
|
TAnim::TAnim()
|
|
{ // potrzebne to w ogóle?
|
|
iFlags = -1; // nieznany typ - destruktor nic nie usuwa
|
|
};
|
|
TAnim::~TAnim()
|
|
{ // usuwanie animacji
|
|
switch (iFlags & 0xF0)
|
|
{ // usuwanie struktur, zależnie ile zostało stworzonych
|
|
case 0x20: // 2-wahacz, dźwignia itp.
|
|
delete fParam;
|
|
break;
|
|
case 0x50: // 5-pantograf
|
|
delete fParamPants;
|
|
break;
|
|
case 0x60: // 6-tłok i rozrząd
|
|
break;
|
|
}
|
|
};
|
|
void TAnim::Parovoz(){
|
|
// animowanie tłoka i rozrządu parowozu
|
|
};
|
|
//---------------------------------------------------------------------------
|
|
TDynamicObject * TDynamicObject::FirstFind(int &coupler_nr)
|
|
{ // szukanie skrajnego połączonego pojazdu w pociagu
|
|
// od strony sprzegu (coupler_nr) obiektu (start)
|
|
TDynamicObject *temp = this;
|
|
for (int i = 0; i < 300; i++) // ograniczenie do 300 na wypadek zapętlenia składu
|
|
{
|
|
if (!temp)
|
|
return NULL; // Ra: zabezpieczenie przed ewentaulnymi błędami sprzęgów
|
|
if (temp->MoverParameters->Couplers[coupler_nr].CouplingFlag == 0)
|
|
return temp; // nic nie ma już dalej podłączone
|
|
if (coupler_nr == 0)
|
|
{ // jeżeli szukamy od sprzęgu 0
|
|
if (temp->PrevConnected) // jeśli mamy coś z przodu
|
|
{
|
|
if (temp->PrevConnectedNo == 0) // jeśli pojazd od strony sprzęgu 0 jest odwrócony
|
|
coupler_nr = 1 - coupler_nr; // to zmieniamy kierunek sprzęgu
|
|
temp = temp->PrevConnected; // ten jest od strony 0
|
|
}
|
|
else
|
|
return temp; // jeśli jednak z przodu nic nie ma
|
|
}
|
|
else
|
|
{
|
|
if (temp->NextConnected)
|
|
{
|
|
if (temp->NextConnectedNo == 1) // jeśli pojazd od strony sprzęgu 1 jest odwrócony
|
|
coupler_nr = 1 - coupler_nr; // to zmieniamy kierunek sprzęgu
|
|
temp = temp->NextConnected; // ten pojazd jest od strony 1
|
|
}
|
|
else
|
|
return temp; // jeśli jednak z tyłu nic nie ma
|
|
}
|
|
}
|
|
return NULL; // to tylko po wyczerpaniu pętli
|
|
};
|
|
|
|
//---------------------------------------------------------------------------
|
|
float TDynamicObject::GetEPP()
|
|
{ // szukanie skrajnego połączonego pojazdu w
|
|
// pociagu
|
|
// od strony sprzegu (coupler_nr) obiektu (start)
|
|
TDynamicObject *temp = this;
|
|
int coupler_nr = 0;
|
|
float eq = 0, am = 0;
|
|
|
|
for (int i = 0; i < 300; i++) // ograniczenie do 300 na wypadek zapętlenia składu
|
|
{
|
|
if (!temp)
|
|
break; // Ra: zabezpieczenie przed ewentaulnymi błędami sprzęgów
|
|
eq += temp->MoverParameters->PipePress * temp->MoverParameters->Dim.L;
|
|
am += temp->MoverParameters->Dim.L;
|
|
if ((temp->MoverParameters->Couplers[coupler_nr].CouplingFlag & 2) != 2)
|
|
break; // nic nie ma już dalej podłączone
|
|
if (coupler_nr == 0)
|
|
{ // jeżeli szukamy od sprzęgu 0
|
|
if (temp->PrevConnected) // jeśli mamy coś z przodu
|
|
{
|
|
if (temp->PrevConnectedNo == 0) // jeśli pojazd od strony sprzęgu 0 jest odwrócony
|
|
coupler_nr = 1 - coupler_nr; // to zmieniamy kierunek sprzęgu
|
|
temp = temp->PrevConnected; // ten jest od strony 0
|
|
}
|
|
else
|
|
break; // jeśli jednak z przodu nic nie ma
|
|
}
|
|
else
|
|
{
|
|
if (temp->NextConnected)
|
|
{
|
|
if (temp->NextConnectedNo == 1) // jeśli pojazd od strony sprzęgu 1 jest odwrócony
|
|
coupler_nr = 1 - coupler_nr; // to zmieniamy kierunek sprzęgu
|
|
temp = temp->NextConnected; // ten pojazd jest od strony 1
|
|
}
|
|
else
|
|
break; // jeśli jednak z tyłu nic nie ma
|
|
}
|
|
}
|
|
|
|
temp = this;
|
|
coupler_nr = 1;
|
|
for (int i = 0; i < 300; i++) // ograniczenie do 300 na wypadek zapętlenia składu
|
|
{
|
|
if (!temp)
|
|
break; // Ra: zabezpieczenie przed ewentaulnymi błędami sprzęgów
|
|
eq += temp->MoverParameters->PipePress * temp->MoverParameters->Dim.L;
|
|
am += temp->MoverParameters->Dim.L;
|
|
if ((temp->MoverParameters->Couplers[coupler_nr].CouplingFlag & 2) != 2)
|
|
break; // nic nie ma już dalej podłączone
|
|
if (coupler_nr == 0)
|
|
{ // jeżeli szukamy od sprzęgu 0
|
|
if (temp->PrevConnected) // jeśli mamy coś z przodu
|
|
{
|
|
if (temp->PrevConnectedNo == 0) // jeśli pojazd od strony sprzęgu 0 jest odwrócony
|
|
coupler_nr = 1 - coupler_nr; // to zmieniamy kierunek sprzęgu
|
|
temp = temp->PrevConnected; // ten jest od strony 0
|
|
}
|
|
else
|
|
break; // jeśli jednak z przodu nic nie ma
|
|
}
|
|
else
|
|
{
|
|
if (temp->NextConnected)
|
|
{
|
|
if (temp->NextConnectedNo == 1) // jeśli pojazd od strony sprzęgu 1 jest odwrócony
|
|
coupler_nr = 1 - coupler_nr; // to zmieniamy kierunek sprzęgu
|
|
temp = temp->NextConnected; // ten pojazd jest od strony 1
|
|
}
|
|
else
|
|
break; // jeśli jednak z tyłu nic nie ma
|
|
}
|
|
}
|
|
eq -= MoverParameters->PipePress * MoverParameters->Dim.L;
|
|
am -= MoverParameters->Dim.L;
|
|
return eq / am;
|
|
};
|
|
|
|
//---------------------------------------------------------------------------
|
|
TDynamicObject * TDynamicObject::GetFirstDynamic(int cpl_type)
|
|
{ // Szukanie skrajnego połączonego pojazdu w pociagu
|
|
// od strony sprzegu (cpl_type) obiektu szukajacego
|
|
// Ra: wystarczy jedna funkcja do szukania w obu kierunkach
|
|
return FirstFind(cpl_type); // używa referencji
|
|
};
|
|
|
|
/*
|
|
TDynamicObject* TDynamicObject::GetFirstCabDynamic(int cpl_type)
|
|
{//ZiomalCl: szukanie skrajnego obiektu z kabiną
|
|
TDynamicObject* temp=this;
|
|
int coupler_nr=cpl_type;
|
|
for (int i=0;i<300;i++) //ograniczenie do 300 na wypadek zapętlenia składu
|
|
{
|
|
if (!temp)
|
|
return NULL; //Ra: zabezpieczenie przed ewentaulnymi błędami sprzęgów
|
|
if (temp->MoverParameters->CabNo!=0&&temp->MoverParameters->SandCapacity!=0)
|
|
return temp; //nic nie ma już dalej podłączone
|
|
if (temp->MoverParameters->Couplers[coupler_nr].CouplingFlag==0)
|
|
return NULL;
|
|
if (coupler_nr==0)
|
|
{//jeżeli szukamy od sprzęgu 0
|
|
if (temp->PrevConnectedNo==0) //jeśli pojazd od strony sprzęgu 0 jest
|
|
odwrócony
|
|
coupler_nr=1-coupler_nr; //to zmieniamy kierunek sprzęgu
|
|
if (temp->PrevConnected)
|
|
temp=temp->PrevConnected; //ten jest od strony 0
|
|
}
|
|
else
|
|
{
|
|
if (temp->NextConnectedNo==1) //jeśli pojazd od strony sprzęgu 1 jest
|
|
odwrócony
|
|
coupler_nr=1-coupler_nr; //to zmieniamy kierunek sprzęgu
|
|
if (temp->NextConnected)
|
|
temp=temp->NextConnected; //ten pojazd jest od strony 1
|
|
}
|
|
}
|
|
return NULL; //to tylko po wyczerpaniu pętli
|
|
};
|
|
*/
|
|
|
|
void TDynamicObject::ABuSetModelShake(vector3 mShake)
|
|
{
|
|
modelShake = mShake;
|
|
};
|
|
|
|
int TDynamicObject::GetPneumatic(bool front, bool red)
|
|
{
|
|
int x, y, z; // 1=prosty, 2=skośny
|
|
if (red)
|
|
{
|
|
if (front)
|
|
{
|
|
x = btCPneumatic1.GetStatus();
|
|
y = btCPneumatic1r.GetStatus();
|
|
}
|
|
else
|
|
{
|
|
x = btCPneumatic2.GetStatus();
|
|
y = btCPneumatic2r.GetStatus();
|
|
}
|
|
}
|
|
else if (front)
|
|
{
|
|
x = btPneumatic1.GetStatus();
|
|
y = btPneumatic1r.GetStatus();
|
|
}
|
|
else
|
|
{
|
|
x = btPneumatic2.GetStatus();
|
|
y = btPneumatic2r.GetStatus();
|
|
}
|
|
z = 0; // brak węży?
|
|
if ((x == 1) && (y == 1))
|
|
z = 3; // dwa proste
|
|
if ((x == 2) && (y == 0))
|
|
z = 1; // lewy skośny, brak prawego
|
|
if ((x == 0) && (y == 2))
|
|
z = 2; // brak lewego, prawy skośny
|
|
|
|
return z;
|
|
}
|
|
|
|
void TDynamicObject::SetPneumatic(bool front, bool red)
|
|
{
|
|
int x = 0, ten, tamten;
|
|
ten = GetPneumatic(front, red); // 1=lewy skos,2=prawy skos,3=dwa proste
|
|
if (front)
|
|
if (PrevConnected) // pojazd od strony sprzęgu 0
|
|
tamten = PrevConnected->GetPneumatic((PrevConnectedNo == 0 ? true : false), red);
|
|
if (!front)
|
|
if (NextConnected) // pojazd od strony sprzęgu 1
|
|
tamten = NextConnected->GetPneumatic((NextConnectedNo == 0 ? true : false), red);
|
|
if (ten == tamten) // jeśli układ jest symetryczny
|
|
switch (ten)
|
|
{
|
|
case 1:
|
|
x = 2;
|
|
break; // mamy lewy skos, dać lewe skosy
|
|
case 2:
|
|
x = 3;
|
|
break; // mamy prawy skos, dać prawe skosy
|
|
case 3: // wszystkie cztery na prosto
|
|
if (MoverParameters->Couplers[front ? 0 : 1].Render)
|
|
x = 1;
|
|
else
|
|
x = 4;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
if (ten == 2)
|
|
x = 4;
|
|
if (ten == 1)
|
|
x = 1;
|
|
if (ten == 3)
|
|
if (tamten == 1)
|
|
x = 4;
|
|
else
|
|
x = 1;
|
|
}
|
|
if (front)
|
|
{
|
|
if (red)
|
|
cp1 = x;
|
|
else
|
|
sp1 = x;
|
|
} // który pokazywać z przodu
|
|
else
|
|
{
|
|
if (red)
|
|
cp2 = x;
|
|
else
|
|
sp2 = x;
|
|
} // który pokazywać z tyłu
|
|
}
|
|
|
|
void TDynamicObject::UpdateAxle(TAnim *pAnim)
|
|
{ // animacja osi
|
|
pAnim->smAnimated->SetRotate(float3(1, 0, 0), *pAnim->dWheelAngle);
|
|
};
|
|
|
|
void TDynamicObject::UpdateBoogie(TAnim *pAnim)
|
|
{ // animacja wózka
|
|
pAnim->smAnimated->SetRotate(float3(1, 0, 0), *pAnim->dWheelAngle);
|
|
};
|
|
|
|
void TDynamicObject::UpdateDoorTranslate(TAnim *pAnim)
|
|
{ // animacja drzwi - przesuw
|
|
// WriteLog("Dla drzwi nr:", i);
|
|
// WriteLog("Wspolczynnik", DoorSpeedFactor[i]);
|
|
// Ra: te współczynniki są bez sensu, bo modyfikują wektor przesunięcia
|
|
// w efekcie drzwi otwierane na zewnątrz będą odlatywac dowolnie daleko :)
|
|
// ograniczyłem zakres ruchu funkcją max
|
|
if (pAnim->smAnimated)
|
|
{
|
|
if (pAnim->iNumber & 1)
|
|
pAnim->smAnimated->SetTranslate(
|
|
vector3(0, 0, Min0R(dDoorMoveR * pAnim->fSpeed, dDoorMoveR)));
|
|
else
|
|
pAnim->smAnimated->SetTranslate(
|
|
vector3(0, 0, Min0R(dDoorMoveL * pAnim->fSpeed, dDoorMoveL)));
|
|
}
|
|
};
|
|
|
|
void TDynamicObject::UpdateDoorRotate(TAnim *pAnim)
|
|
{ // animacja drzwi - obrót
|
|
if (pAnim->smAnimated)
|
|
{ // if (MoverParameters->DoorOpenMethod==2) //obrotowe
|
|
// albo dwójłomne (trzeba kombinowac
|
|
// submodelami i ShiftL=90,R=180)
|
|
if (pAnim->iNumber & 1)
|
|
pAnim->smAnimated->SetRotate(float3(1, 0, 0), dDoorMoveR);
|
|
else
|
|
pAnim->smAnimated->SetRotate(float3(1, 0, 0), dDoorMoveL);
|
|
}
|
|
};
|
|
|
|
void TDynamicObject::UpdateDoorFold(TAnim *pAnim)
|
|
{ // animacja drzwi - obrót
|
|
if (pAnim->smAnimated)
|
|
{ // if (MoverParameters->DoorOpenMethod==2) //obrotowe
|
|
// albo dwójłomne (trzeba kombinowac
|
|
// submodelami i ShiftL=90,R=180)
|
|
if (pAnim->iNumber & 1)
|
|
{
|
|
pAnim->smAnimated->SetRotate(float3(0, 0, 1), dDoorMoveR);
|
|
TSubModel *sm = pAnim->smAnimated->ChildGet(); // skrzydło mniejsze
|
|
if (sm)
|
|
{
|
|
sm->SetRotate(float3(0, 0, 1), -dDoorMoveR - dDoorMoveR); // skrzydło większe
|
|
sm = sm->ChildGet();
|
|
if (sm)
|
|
sm->SetRotate(float3(0, 1, 0), dDoorMoveR); // podnóżek?
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pAnim->smAnimated->SetRotate(float3(0, 0, 1), dDoorMoveL);
|
|
// SubModel->SetRotate(float3(0,1,0),fValue*360.0);
|
|
TSubModel *sm = pAnim->smAnimated->ChildGet(); // skrzydło mniejsze
|
|
if (sm)
|
|
{
|
|
sm->SetRotate(float3(0, 0, 1), -dDoorMoveL - dDoorMoveL); // skrzydło większe
|
|
sm = sm->ChildGet();
|
|
if (sm)
|
|
sm->SetRotate(float3(0, 1, 0), dDoorMoveL); // podnóżek?
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
void TDynamicObject::UpdatePant(TAnim *pAnim)
|
|
{ // animacja pantografu - 4 obracane ramiona, ślizg piąty
|
|
float a, b, c;
|
|
a = RadToDeg(pAnim->fParamPants->fAngleL - pAnim->fParamPants->fAngleL0);
|
|
b = RadToDeg(pAnim->fParamPants->fAngleU - pAnim->fParamPants->fAngleU0);
|
|
c = a + b;
|
|
if (pAnim->smElement[0])
|
|
pAnim->smElement[0]->SetRotate(float3(-1, 0, 0), a); // dolne ramię
|
|
if (pAnim->smElement[1])
|
|
pAnim->smElement[1]->SetRotate(float3(1, 0, 0), a);
|
|
if (pAnim->smElement[2])
|
|
pAnim->smElement[2]->SetRotate(float3(1, 0, 0), c); // górne ramię
|
|
if (pAnim->smElement[3])
|
|
pAnim->smElement[3]->SetRotate(float3(-1, 0, 0), c);
|
|
if (pAnim->smElement[4])
|
|
pAnim->smElement[4]->SetRotate(float3(-1, 0, 0), b); //ślizg
|
|
};
|
|
|
|
void TDynamicObject::UpdateDoorPlug(TAnim *pAnim)
|
|
{ // animacja drzwi - odskokprzesuw
|
|
if (pAnim->smAnimated)
|
|
{
|
|
if (pAnim->iNumber & 1)
|
|
pAnim->smAnimated->SetTranslate(
|
|
vector3(Min0R(dDoorMoveR * 2, MoverParameters->DoorMaxPlugShift), 0,
|
|
Max0R(0, Min0R(dDoorMoveR * pAnim->fSpeed, dDoorMoveR) -
|
|
MoverParameters->DoorMaxPlugShift * 0.5f)));
|
|
else
|
|
pAnim->smAnimated->SetTranslate(
|
|
vector3(Min0R(dDoorMoveL * 2, MoverParameters->DoorMaxPlugShift), 0,
|
|
Max0R(0, Min0R(dDoorMoveL * pAnim->fSpeed, dDoorMoveL) -
|
|
MoverParameters->DoorMaxPlugShift * 0.5f)));
|
|
}
|
|
};
|
|
|
|
void TDynamicObject::UpdateLeverDouble(TAnim *pAnim)
|
|
{ // animacja gałki zależna od double
|
|
pAnim->smAnimated->SetRotate(float3(1, 0, 0), pAnim->fSpeed * *pAnim->fDoubleBase);
|
|
};
|
|
void TDynamicObject::UpdateLeverFloat(TAnim *pAnim)
|
|
{ // animacja gałki zależna od float
|
|
pAnim->smAnimated->SetRotate(float3(1, 0, 0), pAnim->fSpeed * *pAnim->fFloatBase);
|
|
};
|
|
void TDynamicObject::UpdateLeverInt(TAnim *pAnim)
|
|
{ // animacja gałki zależna od int
|
|
pAnim->smAnimated->SetRotate(float3(1, 0, 0), pAnim->fSpeed * *pAnim->iIntBase);
|
|
};
|
|
void TDynamicObject::UpdateLeverEnum(TAnim *pAnim)
|
|
{ // ustawienie kąta na
|
|
// wartość wskazaną przez
|
|
// int z tablicy fParam
|
|
// pAnim->fParam[0]; - dodać lepkość
|
|
pAnim->smAnimated->SetRotate(float3(1, 0, 0), pAnim->fParam[*pAnim->iIntBase]);
|
|
};
|
|
|
|
// ABu 29.01.05 przeklejone z render i renderalpha: *********************
|
|
void __inline TDynamicObject::ABuLittleUpdate(double ObjSqrDist)
|
|
{ // ABu290105: pozbierane i uporzadkowane powtarzajace
|
|
// sie rzeczy z Render i RenderAlpha
|
|
// dodatkowy warunek, if (ObjSqrDist<...) zeby niepotrzebnie nie zmianiec w
|
|
// obiektach,
|
|
// ktorych i tak nie widac
|
|
// NBMX wrzesien, MC listopad: zuniwersalnione
|
|
btnOn = false; // czy przywrócić stan domyślny po renderowaniu
|
|
|
|
if (mdLoad) // tymczasowo ładunek na poziom podłogi
|
|
if (vFloor.z > 0.0)
|
|
mdLoad->GetSMRoot()->SetTranslate(modelShake + vFloor);
|
|
|
|
if (ObjSqrDist < 160000) // gdy bliżej niż 400m
|
|
{
|
|
for (int i = 0; i < iAnimations; ++i) // wykonanie kolejnych animacji
|
|
if (ObjSqrDist < pAnimations[i].fMaxDist)
|
|
if (pAnimations[i].yUpdate) // jeśli zdefiniowana funkcja
|
|
pAnimations[i].yUpdate(pAnimations +
|
|
i); // aktualizacja animacji (położenia submodeli
|
|
if (ObjSqrDist < 2500) // gdy bliżej niż 50m
|
|
{
|
|
// ABu290105: rzucanie pudlem
|
|
// te animacje wymagają bananów w modelach!
|
|
mdModel->GetSMRoot()->SetTranslate(modelShake);
|
|
if (mdKabina)
|
|
mdKabina->GetSMRoot()->SetTranslate(modelShake);
|
|
if (mdLoad)
|
|
mdLoad->GetSMRoot()->SetTranslate(modelShake + vFloor);
|
|
if (mdLowPolyInt)
|
|
mdLowPolyInt->GetSMRoot()->SetTranslate(modelShake);
|
|
if (mdPrzedsionek)
|
|
mdPrzedsionek->GetSMRoot()->SetTranslate(modelShake);
|
|
// ABu: koniec rzucania
|
|
// ABu011104: liczenie obrotow wozkow
|
|
ABuBogies();
|
|
// Mczapkie-100402: rysowanie lub nie - sprzegow
|
|
// ABu-240105: Dodatkowy warunek: if (...).Render, zeby rysowal tylko
|
|
// jeden
|
|
// z polaczonych sprzegow
|
|
if ((TestFlag(MoverParameters->Couplers[0].CouplingFlag, ctrain_coupler)) &&
|
|
(MoverParameters->Couplers[0].Render))
|
|
{
|
|
btCoupler1.TurnOn();
|
|
btnOn = true;
|
|
}
|
|
// else btCoupler1.TurnOff();
|
|
if ((TestFlag(MoverParameters->Couplers[1].CouplingFlag, ctrain_coupler)) &&
|
|
(MoverParameters->Couplers[1].Render))
|
|
{
|
|
btCoupler2.TurnOn();
|
|
btnOn = true;
|
|
}
|
|
// else btCoupler2.TurnOff();
|
|
//********************************************************************************
|
|
// przewody powietrzne j.w., ABu: decyzja czy rysowac tylko na podstawie
|
|
// 'render' - juz
|
|
// nie
|
|
// przewody powietrzne, yB: decyzja na podstawie polaczen w t3d
|
|
if (Global::bnewAirCouplers)
|
|
{
|
|
SetPneumatic(false, false); // wczytywanie z t3d ulozenia wezykow
|
|
SetPneumatic(true, false); // i zapisywanie do zmiennej
|
|
SetPneumatic(true, true); // ktore z nich nalezy
|
|
SetPneumatic(false, true); // wyswietlic w tej klatce
|
|
|
|
if (TestFlag(MoverParameters->Couplers[0].CouplingFlag, ctrain_pneumatic))
|
|
{
|
|
switch (cp1)
|
|
{
|
|
case 1:
|
|
btCPneumatic1.TurnOn();
|
|
break;
|
|
case 2:
|
|
btCPneumatic1.TurnxOn();
|
|
break;
|
|
case 3:
|
|
btCPneumatic1r.TurnxOn();
|
|
break;
|
|
case 4:
|
|
btCPneumatic1r.TurnOn();
|
|
break;
|
|
}
|
|
btnOn = true;
|
|
}
|
|
// else
|
|
//{
|
|
// btCPneumatic1.TurnOff();
|
|
// btCPneumatic1r.TurnOff();
|
|
//}
|
|
|
|
if (TestFlag(MoverParameters->Couplers[1].CouplingFlag, ctrain_pneumatic))
|
|
{
|
|
switch (cp2)
|
|
{
|
|
case 1:
|
|
btCPneumatic2.TurnOn();
|
|
break;
|
|
case 2:
|
|
btCPneumatic2.TurnxOn();
|
|
break;
|
|
case 3:
|
|
btCPneumatic2r.TurnxOn();
|
|
break;
|
|
case 4:
|
|
btCPneumatic2r.TurnOn();
|
|
break;
|
|
}
|
|
btnOn = true;
|
|
}
|
|
// else
|
|
//{
|
|
// btCPneumatic2.TurnOff();
|
|
// btCPneumatic2r.TurnOff();
|
|
//}
|
|
|
|
// przewody zasilajace, j.w. (yB)
|
|
if (TestFlag(MoverParameters->Couplers[0].CouplingFlag, ctrain_scndpneumatic))
|
|
{
|
|
switch (sp1)
|
|
{
|
|
case 1:
|
|
btPneumatic1.TurnOn();
|
|
break;
|
|
case 2:
|
|
btPneumatic1.TurnxOn();
|
|
break;
|
|
case 3:
|
|
btPneumatic1r.TurnxOn();
|
|
break;
|
|
case 4:
|
|
btPneumatic1r.TurnOn();
|
|
break;
|
|
}
|
|
btnOn = true;
|
|
}
|
|
// else
|
|
//{
|
|
// btPneumatic1.TurnOff();
|
|
// btPneumatic1r.TurnOff();
|
|
//}
|
|
|
|
if (TestFlag(MoverParameters->Couplers[1].CouplingFlag, ctrain_scndpneumatic))
|
|
{
|
|
switch (sp2)
|
|
{
|
|
case 1:
|
|
btPneumatic2.TurnOn();
|
|
break;
|
|
case 2:
|
|
btPneumatic2.TurnxOn();
|
|
break;
|
|
case 3:
|
|
btPneumatic2r.TurnxOn();
|
|
break;
|
|
case 4:
|
|
btPneumatic2r.TurnOn();
|
|
break;
|
|
}
|
|
btnOn = true;
|
|
}
|
|
// else
|
|
//{
|
|
// btPneumatic2.TurnOff();
|
|
// btPneumatic2r.TurnOff();
|
|
//}
|
|
}
|
|
//*********************************************************************************/
|
|
else // po staremu ABu'oewmu
|
|
{
|
|
// przewody powietrzne j.w., ABu: decyzja czy rysowac tylko na podstawie
|
|
// 'render'
|
|
if (TestFlag(MoverParameters->Couplers[0].CouplingFlag, ctrain_pneumatic))
|
|
{
|
|
if (MoverParameters->Couplers[0].Render)
|
|
btCPneumatic1.TurnOn();
|
|
else
|
|
btCPneumatic1r.TurnOn();
|
|
btnOn = true;
|
|
}
|
|
// else
|
|
//{
|
|
// btCPneumatic1.TurnOff();
|
|
// btCPneumatic1r.TurnOff();
|
|
//}
|
|
|
|
if (TestFlag(MoverParameters->Couplers[1].CouplingFlag, ctrain_pneumatic))
|
|
{
|
|
if (MoverParameters->Couplers[1].Render)
|
|
btCPneumatic2.TurnOn();
|
|
else
|
|
btCPneumatic2r.TurnOn();
|
|
btnOn = true;
|
|
}
|
|
// else
|
|
//{
|
|
// btCPneumatic2.TurnOff();
|
|
// btCPneumatic2r.TurnOff();
|
|
//}
|
|
|
|
// przewody powietrzne j.w., ABu: decyzja czy rysowac tylko na podstawie
|
|
// 'render'
|
|
// //yB - zasilajace
|
|
if (TestFlag(MoverParameters->Couplers[0].CouplingFlag, ctrain_scndpneumatic))
|
|
{
|
|
if (MoverParameters->Couplers[0].Render)
|
|
btPneumatic1.TurnOn();
|
|
else
|
|
btPneumatic1r.TurnOn();
|
|
btnOn = true;
|
|
}
|
|
// else
|
|
//{
|
|
// btPneumatic1.TurnOff();
|
|
// btPneumatic1r.TurnOff();
|
|
//}
|
|
|
|
if (TestFlag(MoverParameters->Couplers[1].CouplingFlag, ctrain_scndpneumatic))
|
|
{
|
|
if (MoverParameters->Couplers[1].Render)
|
|
btPneumatic2.TurnOn();
|
|
else
|
|
btPneumatic2r.TurnOn();
|
|
btnOn = true;
|
|
}
|
|
// else
|
|
//{
|
|
// btPneumatic2.TurnOff();
|
|
// btPneumatic2r.TurnOff();
|
|
//}
|
|
}
|
|
//*************************************************************/// koniec
|
|
// wezykow
|
|
// uginanie zderzakow
|
|
for (int i = 0; i < 2; i++)
|
|
{
|
|
double dist = MoverParameters->Couplers[i].Dist / 2.0;
|
|
if (smBuforLewy[i])
|
|
if (dist < 0)
|
|
smBuforLewy[i]->SetTranslate(vector3(dist, 0, 0));
|
|
if (smBuforPrawy[i])
|
|
if (dist < 0)
|
|
smBuforPrawy[i]->SetTranslate(vector3(dist, 0, 0));
|
|
}
|
|
}
|
|
|
|
// Winger 160204 - podnoszenie pantografow
|
|
|
|
// przewody sterowania ukrotnionego
|
|
if (TestFlag(MoverParameters->Couplers[0].CouplingFlag, ctrain_controll))
|
|
{
|
|
btCCtrl1.TurnOn();
|
|
btnOn = true;
|
|
}
|
|
// else btCCtrl1.TurnOff();
|
|
if (TestFlag(MoverParameters->Couplers[1].CouplingFlag, ctrain_controll))
|
|
{
|
|
btCCtrl2.TurnOn();
|
|
btnOn = true;
|
|
}
|
|
// else btCCtrl2.TurnOff();
|
|
// McZapkie-181103: mostki przejsciowe
|
|
if (TestFlag(MoverParameters->Couplers[0].CouplingFlag, ctrain_passenger))
|
|
{
|
|
btCPass1.TurnOn();
|
|
btnOn = true;
|
|
}
|
|
// else btCPass1.TurnOff();
|
|
if (TestFlag(MoverParameters->Couplers[1].CouplingFlag, ctrain_passenger))
|
|
{
|
|
btCPass2.TurnOn();
|
|
btnOn = true;
|
|
}
|
|
// else btCPass2.TurnOff();
|
|
if (MoverParameters->Battery)
|
|
{ // sygnaly konca pociagu
|
|
if (btEndSignals1.Active())
|
|
{
|
|
if (TestFlag(iLights[0], 2) || TestFlag(iLights[0], 32))
|
|
{
|
|
btEndSignals1.TurnOn();
|
|
btnOn = true;
|
|
}
|
|
// else btEndSignals1.TurnOff();
|
|
}
|
|
else
|
|
{
|
|
if (TestFlag(iLights[0], 2))
|
|
{
|
|
btEndSignals11.TurnOn();
|
|
btnOn = true;
|
|
}
|
|
// else btEndSignals11.TurnOff();
|
|
if (TestFlag(iLights[0], 32))
|
|
{
|
|
btEndSignals13.TurnOn();
|
|
btnOn = true;
|
|
}
|
|
// else btEndSignals13.TurnOff();
|
|
}
|
|
if (btEndSignals2.Active())
|
|
{
|
|
if (TestFlag(iLights[1], 2) || TestFlag(iLights[1], 32))
|
|
{
|
|
btEndSignals2.TurnOn();
|
|
btnOn = true;
|
|
}
|
|
// else btEndSignals2.TurnOff();
|
|
}
|
|
else
|
|
{
|
|
if (TestFlag(iLights[1], 2))
|
|
{
|
|
btEndSignals21.TurnOn();
|
|
btnOn = true;
|
|
}
|
|
// else btEndSignals21.TurnOff();
|
|
if (TestFlag(iLights[1], 32))
|
|
{
|
|
btEndSignals23.TurnOn();
|
|
btnOn = true;
|
|
}
|
|
// else btEndSignals23.TurnOff();
|
|
}
|
|
}
|
|
// tablice blaszane:
|
|
if (TestFlag(iLights[0], 64))
|
|
{
|
|
btEndSignalsTab1.TurnOn();
|
|
btnOn = true;
|
|
}
|
|
// else btEndSignalsTab1.TurnOff();
|
|
if (TestFlag(iLights[1], 64))
|
|
{
|
|
btEndSignalsTab2.TurnOn();
|
|
btnOn = true;
|
|
}
|
|
// else btEndSignalsTab2.TurnOff();
|
|
// McZapkie-181002: krecenie wahaczem (korzysta z kata obrotu silnika)
|
|
if (iAnimType[ANIM_LEVERS])
|
|
for (int i = 0; i < 4; ++i)
|
|
if (smWahacze[i])
|
|
smWahacze[i]->SetRotate(float3(1, 0, 0),
|
|
fWahaczeAmp * cos(MoverParameters->eAngle));
|
|
if (Mechanik && (Controller != Humandriver))
|
|
{ // rysowanie figurki mechanika
|
|
if (smMechanik0) // mechanik od strony sprzęgu 0
|
|
if (smMechanik1) // jak jest drugi, to pierwszego jedynie pokazujemy
|
|
smMechanik0->iVisible = MoverParameters->ActiveCab > 0;
|
|
else
|
|
{ // jak jest tylko jeden, to do drugiej kabiny go obracamy
|
|
smMechanik0->iVisible = (MoverParameters->ActiveCab != 0);
|
|
smMechanik0->SetRotate(
|
|
float3(0, 0, 1),
|
|
MoverParameters->ActiveCab >= 0 ? 0 : 180); // obrót względem osi Z
|
|
}
|
|
if (smMechanik1) // mechanik od strony sprzęgu 1
|
|
smMechanik1->iVisible = MoverParameters->ActiveCab < 0;
|
|
}
|
|
// ABu: Przechyly na zakretach
|
|
// Ra: przechyłkę załatwiamy na etapie przesuwania modelu
|
|
// if (ObjSqrDist<80000) ABuModelRoll(); //przechyłki od 400m
|
|
}
|
|
if (MoverParameters->Battery)
|
|
{ // sygnały czoła pociagu //Ra: wyświetlamy bez
|
|
// ograniczeń odległości, by były widoczne z
|
|
// daleka
|
|
if (TestFlag(iLights[0], 1))
|
|
{
|
|
btHeadSignals11.TurnOn();
|
|
btnOn = true;
|
|
}
|
|
// else btHeadSignals11.TurnOff();
|
|
if (TestFlag(iLights[0], 4))
|
|
{
|
|
btHeadSignals12.TurnOn();
|
|
btnOn = true;
|
|
}
|
|
// else btHeadSignals12.TurnOff();
|
|
if (TestFlag(iLights[0], 16))
|
|
{
|
|
btHeadSignals13.TurnOn();
|
|
btnOn = true;
|
|
}
|
|
// else btHeadSignals13.TurnOff();
|
|
if (TestFlag(iLights[1], 1))
|
|
{
|
|
btHeadSignals21.TurnOn();
|
|
btnOn = true;
|
|
}
|
|
// else btHeadSignals21.TurnOff();
|
|
if (TestFlag(iLights[1], 4))
|
|
{
|
|
btHeadSignals22.TurnOn();
|
|
btnOn = true;
|
|
}
|
|
// else btHeadSignals22.TurnOff();
|
|
if (TestFlag(iLights[1], 16))
|
|
{
|
|
btHeadSignals23.TurnOn();
|
|
btnOn = true;
|
|
}
|
|
// else btHeadSignals23.TurnOff();
|
|
}
|
|
}
|
|
// ABu 29.01.05 koniec przeklejenia *************************************
|
|
|
|
double ABuAcos(const vector3 &calc_temp)
|
|
{ // Odpowiednik funkcji Arccos, bo cos
|
|
// mi tam nie dzialalo.
|
|
return atan2(-calc_temp.x, calc_temp.z); // Ra: tak prościej
|
|
}
|
|
|
|
TDynamicObject * TDynamicObject::ABuFindNearestObject(TTrack *Track,
|
|
TDynamicObject *MyPointer,
|
|
int &CouplNr)
|
|
{ // zwraca wskaznik do obiektu znajdujacego sie na torze
|
|
// (Track), którego sprzęg jest najblizszy
|
|
// kamerze
|
|
// służy np. do łączenia i rozpinania sprzęgów
|
|
// WE: Track - tor, na ktorym odbywa sie poszukiwanie
|
|
// MyPointer - wskaznik do obiektu szukajacego
|
|
// WY: CouplNr - który sprzęg znalezionego obiektu jest bliższy kamerze
|
|
|
|
// Uwaga! Jesli CouplNr==-2 to szukamy njblizszego obiektu, a nie sprzegu!!!
|
|
|
|
if ((Track->iNumDynamics) > 0)
|
|
{ // o ile w ogóle jest co przeglądać na tym torze
|
|
// vector3 poz; //pozycja pojazdu XYZ w scenerii
|
|
// vector3 kon; //wektor czoła względem środka pojazdu wzglęem początku toru
|
|
vector3 tmp; // wektor pomiędzy kamerą i sprzęgiem
|
|
double dist; // odległość
|
|
for (int i = 0; i < Track->iNumDynamics; i++)
|
|
{
|
|
if (CouplNr == -2)
|
|
{ // wektor [kamera-obiekt] - poszukiwanie obiektu
|
|
tmp = Global::GetCameraPosition() - Track->Dynamics[i]->vPosition;
|
|
dist = tmp.x * tmp.x + tmp.y * tmp.y + tmp.z * tmp.z; // odległość do kwadratu
|
|
if (dist < 100.0) // 10 metrów
|
|
return Track->Dynamics[i];
|
|
}
|
|
else // jeśli (CouplNr) inne niz -2, szukamy sprzęgu
|
|
{ // wektor [kamera-sprzeg0], potem [kamera-sprzeg1]
|
|
// Powinno byc wyliczone, ale nie zaszkodzi drugi raz:
|
|
//(bo co, jesli nie wykonuje sie obrotow wozkow?) - Ra: ale zawsze są
|
|
// liczone
|
|
// współrzędne sprzęgów
|
|
// Track->Dynamics[i]->modelRot.z=ABuAcos(Track->Dynamics[i]->Axle0.pPosition-Track->Dynamics[i]->Axle1.pPosition);
|
|
// poz=Track->Dynamics[i]->vPosition; //pozycja środka pojazdu
|
|
// kon=vector3( //położenie przodu względem środka
|
|
// -((0.5*Track->Dynamics[i]->MoverParameters->Dim.L)*sin(Track->Dynamics[i]->modelRot.z)),
|
|
// 0, //yyy... jeśli duże pochylenie i długi pojazd, to może być problem
|
|
// +((0.5*Track->Dynamics[i]->MoverParameters->Dim.L)*cos(Track->Dynamics[i]->modelRot.z))
|
|
//);
|
|
tmp =
|
|
Global::GetCameraPosition() -
|
|
Track->Dynamics[i]->vCoulpler[0]; // Ra: pozycje sprzęgów też są zawsze liczone
|
|
dist = tmp.x * tmp.x + tmp.y * tmp.y + tmp.z * tmp.z; // odległość do kwadratu
|
|
if (dist < 25.0) // 5 metrów
|
|
{
|
|
CouplNr = 0;
|
|
return Track->Dynamics[i];
|
|
}
|
|
tmp = Global::GetCameraPosition() - Track->Dynamics[i]->vCoulpler[1];
|
|
dist = tmp.x * tmp.x + tmp.y * tmp.y + tmp.z * tmp.z; // odległość do kwadratu
|
|
if (dist < 25.0) // 5 metrów
|
|
{
|
|
CouplNr = 1;
|
|
return Track->Dynamics[i];
|
|
}
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
TDynamicObject * TDynamicObject::ABuScanNearestObject(TTrack *Track, double ScanDir,
|
|
double ScanDist, int &CouplNr)
|
|
{ // skanowanie toru w poszukiwaniu obiektu najblizszego
|
|
// kamerze
|
|
// double MyScanDir=ScanDir; //Moja orientacja na torze. //Ra: nie używane
|
|
if (ABuGetDirection() < 0)
|
|
ScanDir = -ScanDir;
|
|
TDynamicObject *FoundedObj;
|
|
FoundedObj =
|
|
ABuFindNearestObject(Track, this, CouplNr); // zwraca numer sprzęgu znalezionego pojazdu
|
|
if (FoundedObj == NULL)
|
|
{
|
|
double ActDist; // Przeskanowana odleglosc.
|
|
double CurrDist = 0; // Aktualna dlugosc toru.
|
|
if (ScanDir >= 0)
|
|
ActDist =
|
|
Track->Length() - RaTranslationGet(); //???-przesunięcie wózka względem Point1 toru
|
|
else
|
|
ActDist = RaTranslationGet(); // przesunięcie wózka względem Point1 toru
|
|
while (ActDist < ScanDist)
|
|
{
|
|
ActDist += CurrDist;
|
|
if (ScanDir > 0) // do przodu
|
|
{
|
|
if (Track->iNextDirection)
|
|
{
|
|
Track = Track->CurrentNext();
|
|
ScanDir = -ScanDir;
|
|
}
|
|
else
|
|
Track = Track->CurrentNext();
|
|
}
|
|
else // do tyłu
|
|
{
|
|
if (Track->iPrevDirection)
|
|
Track = Track->CurrentPrev();
|
|
else
|
|
{
|
|
Track = Track->CurrentPrev();
|
|
ScanDir = -ScanDir;
|
|
}
|
|
}
|
|
if (Track != NULL)
|
|
{ // jesli jest kolejny odcinek toru
|
|
CurrDist = Track->Length();
|
|
FoundedObj = ABuFindNearestObject(Track, this, CouplNr);
|
|
if (FoundedObj != NULL)
|
|
ActDist = ScanDist;
|
|
}
|
|
else // Jesli nie ma, to wychodzimy.
|
|
ActDist = ScanDist;
|
|
}
|
|
} // Koniec szukania najblizszego toru z jakims obiektem.
|
|
return FoundedObj;
|
|
}
|
|
|
|
// ABu 01.11.04 poczatek wyliczania przechylow pudla **********************
|
|
void TDynamicObject::ABuModelRoll()
|
|
{ // ustawienie przechyłki pojazdu i jego
|
|
// zawartości
|
|
// Ra: przechyłkę załatwiamy na etapie przesuwania modelu
|
|
}
|
|
|
|
// ABu 06.05.04 poczatek wyliczania obrotow wozkow **********************
|
|
|
|
void TDynamicObject::ABuBogies()
|
|
{ // Obracanie wozkow na zakretach. Na razie
|
|
// uwzględnia tylko zakręty,
|
|
// bez zadnych gorek i innych przeszkod.
|
|
if ((smBogie[0] != NULL) && (smBogie[1] != NULL))
|
|
{
|
|
// modelRot.z=ABuAcos(Axle0.pPosition-Axle1.pPosition); //kąt obrotu pojazdu
|
|
// [rad]
|
|
// bogieRot[0].z=ABuAcos(Axle0.pPosition-Axle3.pPosition);
|
|
bogieRot[0].z = Axle0.vAngles.z;
|
|
bogieRot[0] = RadToDeg(modelRot - bogieRot[0]); // mnożenie wektora przez stałą
|
|
smBogie[0]->SetRotateXYZ(bogieRot[0]);
|
|
// bogieRot[1].z=ABuAcos(Axle2.pPosition-Axle1.pPosition);
|
|
bogieRot[1].z = Axle1.vAngles.z;
|
|
bogieRot[1] = RadToDeg(modelRot - bogieRot[1]);
|
|
smBogie[1]->SetRotateXYZ(bogieRot[1]);
|
|
}
|
|
};
|
|
// ABu 06.05.04 koniec wyliczania obrotow wozkow ************************
|
|
|
|
// ABu 16.03.03 sledzenie toru przed obiektem: **************************
|
|
void TDynamicObject::ABuCheckMyTrack()
|
|
{ // Funkcja przypisujaca obiekt
|
|
// prawidlowej tablicy Dynamics,
|
|
// bo gdzies jest jakis blad i wszystkie obiekty z danego
|
|
// pociagu na poczatku stawiane sa na jednym torze i wpisywane
|
|
// do jednej tablicy. Wykonuje sie tylko raz - po to 'ABuChecked'
|
|
TTrack *OldTrack = MyTrack;
|
|
TTrack *NewTrack = Axle0.GetTrack();
|
|
if ((NewTrack != OldTrack) && OldTrack)
|
|
{
|
|
OldTrack->RemoveDynamicObject(this);
|
|
NewTrack->AddDynamicObject(this);
|
|
}
|
|
iAxleFirst = 0; // pojazd powiązany z przednią osią - Axle0
|
|
}
|
|
|
|
// Ra: w poniższej funkcji jest problem ze sprzęgami
|
|
TDynamicObject * TDynamicObject::ABuFindObject(TTrack *Track, int ScanDir,
|
|
Byte &CouplFound, double &dist)
|
|
{ // Zwraca wskaźnik najbliższego obiektu znajdującego się
|
|
// na torze w określonym kierunku, ale tylko wtedy, kiedy
|
|
// obiekty mogą się zderzyć, tzn. nie mijają się.
|
|
|
|
// WE: Track - tor, na ktorym odbywa sie poszukiwanie,
|
|
// MyPointer - wskaznik do obiektu szukajacego. //Ra: zamieniłem na "this"
|
|
// ScanDir - kierunek szukania na torze (+1:w stronę Point2, -1:w stronę
|
|
// Point1)
|
|
// MyScanDir - kierunek szukania obiektu szukajacego (na jego torze); Ra:
|
|
// nie potrzebne
|
|
// MyCouplFound - nr sprzegu obiektu szukajacego; Ra: nie potrzebne
|
|
|
|
// WY: wskaznik do znalezionego obiektu.
|
|
// CouplFound - nr sprzegu znalezionego obiektu
|
|
if (Track->iNumDynamics > 0)
|
|
{ // sens szukania na tym torze jest tylko, gdy są na nim pojazdy
|
|
double ObjTranslation; // pozycja najblizszego obiektu na torze
|
|
double MyTranslation; // pozycja szukającego na torze
|
|
double MinDist = Track->Length(); // najmniejsza znaleziona odleglość
|
|
// (zaczynamy od długości toru)
|
|
double TestDist; // robocza odległość od kolejnych pojazdów na danym odcinku
|
|
int iMinDist = -1; // indeks wykrytego obiektu
|
|
// if (Track->iNumDynamics>1)
|
|
// iMinDist+=0; //tymczasowo pułapka
|
|
if (MyTrack == Track) // gdy szukanie na tym samym torze
|
|
MyTranslation = RaTranslationGet(); // położenie wózka względem Point1 toru
|
|
else // gdy szukanie na innym torze
|
|
if (ScanDir > 0)
|
|
MyTranslation = 0; // szukanie w kierunku Point2 (od zera) - jesteśmy w Point1
|
|
else
|
|
MyTranslation = MinDist; // szukanie w kierunku Point1 (do zera) - jesteśmy w Point2
|
|
if (ScanDir >= 0)
|
|
{ // jeśli szukanie w kierunku Point2
|
|
for (int i = 0; i < Track->iNumDynamics; i++)
|
|
{ // pętla po pojazdach
|
|
if (Track->Dynamics[i] != this) // szukający się nie liczy
|
|
{
|
|
TestDist = (Track->Dynamics[i]->RaTranslationGet()) -
|
|
MyTranslation; // odległogłość tamtego od szukającego
|
|
if ((TestDist > 0) && (TestDist <= MinDist))
|
|
{ // gdy jest po właściwej stronie i bliżej
|
|
// niż jakiś wcześniejszy
|
|
CouplFound = (Track->Dynamics[i]->RaDirectionGet() > 0) ?
|
|
1 :
|
|
0; // to, bo (ScanDir>=0)
|
|
if (Track->iCategoryFlag & 254) // trajektoria innego typu niż tor kolejowy
|
|
{ // dla torów nie ma sensu tego sprawdzać, rzadko co jedzie po
|
|
// jednej
|
|
// szynie i się mija
|
|
// Ra: mijanie samochodów wcale nie jest proste
|
|
// Przesuniecie wzgledne pojazdow. Wyznaczane, zeby sprawdzic,
|
|
// czy pojazdy faktycznie sie zderzaja (moga byc przesuniete
|
|
// w/m siebie tak, ze nie zachodza na siebie i wtedy sie mijaja).
|
|
double RelOffsetH; // wzajemna odległość poprzeczna
|
|
if (CouplFound) // my na tym torze byśmy byli w kierunku Point2
|
|
// dla CouplFound=1 są zwroty zgodne - istotna różnica
|
|
// przesunięć
|
|
RelOffsetH = (MoverParameters->OffsetTrackH -
|
|
Track->Dynamics[i]->MoverParameters->OffsetTrackH);
|
|
else
|
|
// dla CouplFound=0 są zwroty przeciwne - przesunięcia sumują
|
|
// się
|
|
RelOffsetH = (MoverParameters->OffsetTrackH +
|
|
Track->Dynamics[i]->MoverParameters->OffsetTrackH);
|
|
if (RelOffsetH < 0)
|
|
RelOffsetH = -RelOffsetH;
|
|
if (RelOffsetH + RelOffsetH >
|
|
MoverParameters->Dim.W + Track->Dynamics[i]->MoverParameters->Dim.W)
|
|
continue; // odległość większa od połowy sumy szerokości -
|
|
// kolizji
|
|
// nie będzie
|
|
// jeśli zahaczenie jest niewielkie, a jest miejsce na poboczu, to
|
|
// zjechać na pobocze
|
|
}
|
|
iMinDist = i; // potencjalna kolizja
|
|
MinDist = TestDist; // odleglość pomiędzy aktywnymi osiami pojazdów
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else //(ScanDir<0)
|
|
{
|
|
for (int i = 0; i < Track->iNumDynamics; i++)
|
|
{
|
|
if (Track->Dynamics[i] != this)
|
|
{
|
|
TestDist = MyTranslation -
|
|
(Track->Dynamics[i]->RaTranslationGet()); //???-przesunięcie wózka
|
|
// względem Point1 toru
|
|
if ((TestDist > 0) && (TestDist < MinDist))
|
|
{
|
|
CouplFound = (Track->Dynamics[i]->RaDirectionGet() > 0) ?
|
|
0 :
|
|
1; // odwrotnie, bo (ScanDir<0)
|
|
if (Track->iCategoryFlag & 254) // trajektoria innego typu niż tor kolejowy
|
|
{ // dla torów nie ma sensu tego sprawdzać, rzadko co jedzie po
|
|
// jednej
|
|
// szynie i się mija
|
|
// Ra: mijanie samochodów wcale nie jest proste
|
|
// Przesunięcie względne pojazdów. Wyznaczane, żeby sprawdzić,
|
|
// czy pojazdy faktycznie się zderzają (mogą być przesunięte
|
|
// w/m siebie tak, że nie zachodzą na siebie i wtedy sie mijają).
|
|
double RelOffsetH; // wzajemna odległość poprzeczna
|
|
if (CouplFound) // my na tym torze byśmy byli w kierunku Point1
|
|
// dla CouplFound=1 są zwroty zgodne - istotna różnica
|
|
// przesunięć
|
|
RelOffsetH = (MoverParameters->OffsetTrackH -
|
|
Track->Dynamics[i]->MoverParameters->OffsetTrackH);
|
|
else
|
|
// dla CouplFound=0 są zwroty przeciwne - przesunięcia sumują
|
|
// się
|
|
RelOffsetH = (MoverParameters->OffsetTrackH +
|
|
Track->Dynamics[i]->MoverParameters->OffsetTrackH);
|
|
if (RelOffsetH < 0)
|
|
RelOffsetH = -RelOffsetH;
|
|
if (RelOffsetH + RelOffsetH >
|
|
MoverParameters->Dim.W + Track->Dynamics[i]->MoverParameters->Dim.W)
|
|
continue; // odległość większa od połowy sumy szerokości -
|
|
// kolizji
|
|
// nie będzie
|
|
}
|
|
iMinDist = i; // potencjalna kolizja
|
|
MinDist = TestDist; // odleglość pomiędzy aktywnymi osiami pojazdów
|
|
}
|
|
}
|
|
}
|
|
}
|
|
dist += MinDist; // doliczenie odległości przeszkody albo długości odcinka
|
|
// do przeskanowanej
|
|
// odległości
|
|
return (iMinDist >= 0) ? Track->Dynamics[iMinDist] : NULL;
|
|
}
|
|
dist += Track->Length(); // doliczenie długości odcinka do przeskanowanej
|
|
// odległości
|
|
return NULL; // nie ma pojazdów na torze, to jest NULL
|
|
}
|
|
|
|
int TDynamicObject::DettachStatus(int dir)
|
|
{ // sprawdzenie odległości sprzęgów
|
|
// rzeczywistych od strony (dir):
|
|
// 0=przód,1=tył
|
|
// Ra: dziwne, że ta funkcja nie jest używana
|
|
if (!MoverParameters->Couplers[dir].CouplingFlag)
|
|
return 0; // jeśli nic nie podłączone, to jest OK
|
|
return (MoverParameters->DettachStatus(dir)); // czy jest w odpowiedniej odległości?
|
|
}
|
|
|
|
int TDynamicObject::Dettach(int dir)
|
|
{ // rozłączenie sprzęgów rzeczywistych od
|
|
// strony (dir): 0=przód,1=tył
|
|
// zwraca maskę bitową aktualnych sprzegów (0 jeśli rozłączony)
|
|
if (ctOwner)
|
|
{ // jeśli pojazd ma przypisany obiekt nadzorujący skład, to póki
|
|
// są wskaźniki
|
|
TDynamicObject *d = this;
|
|
while (d)
|
|
{
|
|
d->ctOwner = NULL; // usuwanie właściciela
|
|
d = d->Prev();
|
|
}
|
|
d = Next();
|
|
while (d)
|
|
{
|
|
d->ctOwner = NULL; // usuwanie właściciela
|
|
d = d->Next(); // i w drugą stronę
|
|
}
|
|
}
|
|
if (MoverParameters->Couplers[dir].CouplingFlag) // odczepianie, o ile coś podłączone
|
|
MoverParameters->Dettach(dir);
|
|
return MoverParameters->Couplers[dir]
|
|
.CouplingFlag; // sprzęg po rozłączaniu (czego się nie da odpiąć
|
|
}
|
|
|
|
void TDynamicObject::CouplersDettach(double MinDist, int MyScanDir)
|
|
{ // funkcja rozłączajaca podłączone sprzęgi,
|
|
// jeśli odległość przekracza (MinDist)
|
|
// MinDist - dystans minimalny, dla ktorego mozna rozłączać
|
|
if (MyScanDir > 0)
|
|
{
|
|
if (PrevConnected) // pojazd od strony sprzęgu 0
|
|
{
|
|
if (MoverParameters->Couplers[0].CoupleDist >
|
|
MinDist) // sprzęgi wirtualne zawsze przekraczają
|
|
{
|
|
if ((PrevConnectedNo ? PrevConnected->NextConnected :
|
|
PrevConnected->PrevConnected) == this)
|
|
{ // Ra: nie rozłączamy znalezionego, jeżeli nie do nas
|
|
// podłączony (może jechać w
|
|
// innym kierunku)
|
|
PrevConnected->MoverParameters->Couplers[PrevConnectedNo].Connected = NULL;
|
|
if (PrevConnectedNo == 0)
|
|
{
|
|
PrevConnected->PrevConnectedNo = 2; // sprzęg 0 nie podłączony
|
|
PrevConnected->PrevConnected = NULL;
|
|
}
|
|
else if (PrevConnectedNo == 1)
|
|
{
|
|
PrevConnected->NextConnectedNo = 2; // sprzęg 1 nie podłączony
|
|
PrevConnected->NextConnected = NULL;
|
|
}
|
|
}
|
|
// za to zawsze odłączamy siebie
|
|
PrevConnected = NULL;
|
|
PrevConnectedNo = 2; // sprzęg 0 nie podłączony
|
|
MoverParameters->Couplers[0].Connected = NULL;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (NextConnected) // pojazd od strony sprzęgu 1
|
|
{
|
|
if (MoverParameters->Couplers[1].CoupleDist >
|
|
MinDist) // sprzęgi wirtualne zawsze przekraczają
|
|
{
|
|
if ((NextConnectedNo ? NextConnected->NextConnected :
|
|
NextConnected->PrevConnected) == this)
|
|
{ // Ra: nie rozłączamy znalezionego, jeżeli nie do nas
|
|
// podłączony (może jechać w
|
|
// innym kierunku)
|
|
NextConnected->MoverParameters->Couplers[NextConnectedNo].Connected = NULL;
|
|
if (NextConnectedNo == 0)
|
|
{
|
|
NextConnected->PrevConnectedNo = 2; // sprzęg 0 nie podłączony
|
|
NextConnected->PrevConnected = NULL;
|
|
}
|
|
else if (NextConnectedNo == 1)
|
|
{
|
|
NextConnected->NextConnectedNo = 2; // sprzęg 1 nie podłączony
|
|
NextConnected->NextConnected = NULL;
|
|
}
|
|
}
|
|
NextConnected = NULL;
|
|
NextConnectedNo = 2; // sprzęg 1 nie podłączony
|
|
MoverParameters->Couplers[1].Connected = NULL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void TDynamicObject::ABuScanObjects(int ScanDir, double ScanDist)
|
|
{ // skanowanie toru w poszukiwaniu kolidujących pojazdów
|
|
// ScanDir - określa kierunek poszukiwania zależnie od zwrotu prędkości
|
|
// pojazdu
|
|
// ScanDir=1 - od strony Coupler0, ScanDir=-1 - od strony Coupler1
|
|
int MyScanDir = ScanDir; // zapamiętanie kierunku poszukiwań na torze
|
|
// początkowym, względem sprzęgów
|
|
TTrackFollower *FirstAxle = (MyScanDir > 0 ? &Axle0 : &Axle1); // można by to trzymać w trainset
|
|
TTrack *Track = FirstAxle->GetTrack(); // tor na którym "stoi" skrajny wózek
|
|
// (może być inny niż tor pojazdu)
|
|
if (FirstAxle->GetDirection() < 0) // czy oś jest ustawiona w stronę Point1?
|
|
ScanDir = -ScanDir; // jeśli tak, to kierunek szukania będzie przeciwny
|
|
// (teraz względem
|
|
// toru)
|
|
Byte MyCouplFound; // numer sprzęgu do podłączenia w obiekcie szukajacym
|
|
MyCouplFound = (MyScanDir < 0) ? 1 : 0;
|
|
Byte CouplFound; // numer sprzęgu w znalezionym obiekcie (znaleziony wypełni)
|
|
TDynamicObject *FoundedObj; // znaleziony obiekt
|
|
double ActDist = 0; // przeskanowana odleglość; odległość do zawalidrogi
|
|
FoundedObj = ABuFindObject(Track, ScanDir, CouplFound,
|
|
ActDist); // zaczynamy szukać na tym samym torze
|
|
|
|
/*
|
|
if (FoundedObj) //jak coś znajdzie, to śledzimy
|
|
{//powtórzenie wyszukiwania tylko do zastawiania pułepek podczas testów
|
|
if (ABuGetDirection()<0) ScanDir=ScanDir; //ustalenie kierunku względem toru
|
|
FoundedObj=ABuFindObject(Track,this,ScanDir,CouplFound);
|
|
}
|
|
*/
|
|
|
|
if (DebugModeFlag)
|
|
if (FoundedObj) // kod służący do logowania błędów
|
|
if (CouplFound == 0)
|
|
{
|
|
if (FoundedObj->PrevConnected)
|
|
if (FoundedObj->PrevConnected != this) // odświeżenie tego samego się nie liczy
|
|
WriteLog("0! Coupler warning on " + asName + ":" +
|
|
AnsiString(MyCouplFound) + " - " + FoundedObj->asName +
|
|
":0 connected to " + FoundedObj->PrevConnected->asName + ":" +
|
|
AnsiString(FoundedObj->PrevConnectedNo));
|
|
}
|
|
else
|
|
{
|
|
if (FoundedObj->NextConnected)
|
|
if (FoundedObj->NextConnected != this) // odświeżenie tego samego się nie liczy
|
|
WriteLog("0! Coupler warning on " + asName + ":" +
|
|
AnsiString(MyCouplFound) + " - " + FoundedObj->asName +
|
|
":1 connected to " + FoundedObj->NextConnected->asName + ":" +
|
|
AnsiString(FoundedObj->NextConnectedNo));
|
|
}
|
|
|
|
if (FoundedObj == NULL) // jeśli nie ma na tym samym, szukamy po okolicy
|
|
{ // szukanie najblizszego toru z jakims obiektem
|
|
// praktycznie przeklejone z TraceRoute()...
|
|
// double CurrDist=0; //aktualna dlugosc toru
|
|
if (ScanDir >= 0) // uwzględniamy kawalek przeanalizowanego wcześniej toru
|
|
ActDist = Track->Length() - FirstAxle->GetTranslation(); // odległość osi od Point2 toru
|
|
else
|
|
ActDist = FirstAxle->GetTranslation(); // odległość osi od Point1 toru
|
|
while (ActDist < ScanDist)
|
|
{
|
|
// ActDist+=CurrDist; //odległość już przeanalizowana
|
|
if (ScanDir > 0) // w kierunku Point2 toru
|
|
{
|
|
if (Track ? Track->iNextDirection :
|
|
false) // jeśli następny tor jest podpięty od Point2
|
|
ScanDir = -ScanDir; // to zmieniamy kierunek szukania na tym torze
|
|
Track = Track->CurrentNext(); // potem dopiero zmieniamy wskaźnik
|
|
}
|
|
else // w kierunku Point1
|
|
{
|
|
if (Track ? !Track->iPrevDirection :
|
|
true) // jeśli poprzedni tor nie jest podpięty od Point2
|
|
ScanDir = -ScanDir; // to zmieniamy kierunek szukania na tym torze
|
|
Track = Track->CurrentPrev(); // potem dopiero zmieniamy wskaźnik
|
|
}
|
|
if (Track)
|
|
{ // jesli jest kolejny odcinek toru
|
|
// CurrDist=Track->Length(); //doliczenie tego toru do przejrzanego
|
|
// dystandu
|
|
FoundedObj = ABuFindObject(Track, ScanDir, CouplFound,
|
|
ActDist); // przejrzenie pojazdów tego toru
|
|
if (FoundedObj)
|
|
{
|
|
// ActDist=ScanDist; //wyjście z pętli poszukiwania
|
|
break;
|
|
}
|
|
}
|
|
else // jeśli toru nie ma, to wychodzimy
|
|
{
|
|
ActDist = ScanDist + 1.0; // koniec przeglądania torów
|
|
break;
|
|
}
|
|
}
|
|
} // Koniec szukania najbliższego toru z jakimś obiektem.
|
|
// teraz odczepianie i jeśli coś się znalazło, doczepianie.
|
|
if (MyScanDir > 0 ? PrevConnected : NextConnected)
|
|
if ((MyScanDir > 0 ? PrevConnected : NextConnected) != FoundedObj)
|
|
CouplersDettach(1.0, MyScanDir); // odłączamy, jeśli dalej niż metr
|
|
// i łączenie sprzęgiem wirtualnym
|
|
if (FoundedObj)
|
|
{ // siebie można bezpiecznie podłączyć jednostronnie do
|
|
// znalezionego
|
|
MoverParameters->Attach(MyCouplFound, CouplFound, FoundedObj->MoverParameters,
|
|
ctrain_virtual);
|
|
// MoverParameters->Couplers[MyCouplFound].Render=false; //wirtualnego nie
|
|
// renderujemy
|
|
if (MyCouplFound == 0)
|
|
{
|
|
PrevConnected = FoundedObj; // pojazd od strony sprzęgu 0
|
|
PrevConnectedNo = CouplFound;
|
|
}
|
|
else
|
|
{
|
|
NextConnected = FoundedObj; // pojazd od strony sprzęgu 1
|
|
NextConnectedNo = CouplFound;
|
|
}
|
|
if (FoundedObj->MoverParameters->Couplers[CouplFound].CouplingFlag == ctrain_virtual)
|
|
{ // Ra: wpinamy się wirtualnym tylko jeśli znaleziony
|
|
// ma wirtualny sprzęg
|
|
FoundedObj->MoverParameters->Attach(CouplFound, MyCouplFound, this->MoverParameters,
|
|
ctrain_virtual);
|
|
if (CouplFound == 0) // jeśli widoczny sprzęg 0 znalezionego
|
|
{
|
|
if (DebugModeFlag)
|
|
if (FoundedObj->PrevConnected)
|
|
if (FoundedObj->PrevConnected != this)
|
|
WriteLog("1! Coupler warning on " + asName + ":" +
|
|
AnsiString(MyCouplFound) + " - " + FoundedObj->asName +
|
|
":0 connected to " + FoundedObj->PrevConnected->asName + ":" +
|
|
AnsiString(FoundedObj->PrevConnectedNo));
|
|
FoundedObj->PrevConnected = this;
|
|
FoundedObj->PrevConnectedNo = MyCouplFound;
|
|
}
|
|
else // jeśli widoczny sprzęg 1 znalezionego
|
|
{
|
|
if (DebugModeFlag)
|
|
if (FoundedObj->NextConnected)
|
|
if (FoundedObj->NextConnected != this)
|
|
WriteLog("1! Coupler warning on " + asName + ":" +
|
|
AnsiString(MyCouplFound) + " - " + FoundedObj->asName +
|
|
":1 connected to " + FoundedObj->NextConnected->asName + ":" +
|
|
AnsiString(FoundedObj->NextConnectedNo));
|
|
FoundedObj->NextConnected = this;
|
|
FoundedObj->NextConnectedNo = MyCouplFound;
|
|
}
|
|
}
|
|
// Ra: jeśli dwa samochody się mijają na odcinku przed zawrotką, to
|
|
// odległość między nimi
|
|
// nie może być liczona w linii prostej!
|
|
fTrackBlock = MoverParameters->Couplers[MyCouplFound]
|
|
.CoupleDist; // odległość do najbliższego pojazdu w linii prostej
|
|
if (Track->iCategoryFlag > 1) // jeśli samochód
|
|
if (ActDist > MoverParameters->Dim.L +
|
|
FoundedObj->MoverParameters->Dim
|
|
.L) // przeskanowana odległość większa od długości pojazdów
|
|
// else if (ActDist<ScanDist) //dla samochodów musi być uwzględniona
|
|
// droga do
|
|
// zawrócenia
|
|
fTrackBlock = ActDist; // ta odległość jest wiecej warta
|
|
// if (fTrackBlock<500.0)
|
|
// WriteLog("Collision of "+AnsiString(fTrackBlock)+"m detected by
|
|
// "+asName+":"+AnsiString(MyCouplFound)+" with "+FoundedObj->asName);
|
|
}
|
|
else // nic nie znalezione, to nie ma przeszkód
|
|
fTrackBlock = 10000.0;
|
|
}
|
|
//----------ABu: koniec skanowania pojazdow
|
|
|
|
TDynamicObject::TDynamicObject()
|
|
{
|
|
modelShake = vector3(0, 0, 0);
|
|
fTrackBlock = 10000.0; // brak przeszkody na drodze
|
|
btnOn = false;
|
|
vUp = vWorldUp;
|
|
vFront = vWorldFront;
|
|
vLeft = vWorldLeft;
|
|
iNumAxles = 0;
|
|
MoverParameters = NULL;
|
|
Mechanik = NULL;
|
|
MechInside = false;
|
|
// McZapkie-270202
|
|
Controller = AIdriver;
|
|
bDisplayCab = false; // 030303
|
|
bBrakeAcc = false;
|
|
NextConnected = PrevConnected = NULL;
|
|
NextConnectedNo = PrevConnectedNo = 2; // ABu: Numery sprzegow. 2=nie podłączony
|
|
CouplCounter = 50; // będzie sprawdzać na początku
|
|
asName = "";
|
|
bEnabled = true;
|
|
MyTrack = NULL;
|
|
// McZapkie-260202
|
|
dRailLength = 25.0;
|
|
for (int i = 0; i < MaxAxles; i++)
|
|
dRailPosition[i] = 0.0;
|
|
for (int i = 0; i < MaxAxles; i++)
|
|
dWheelsPosition[i] = 0.0; // będzie wczytane z MMD
|
|
iAxles = 0;
|
|
dWheelAngle[0] = 0.0;
|
|
dWheelAngle[1] = 0.0;
|
|
dWheelAngle[2] = 0.0;
|
|
// Winger 160204 - pantografy
|
|
// PantVolume = 3.5;
|
|
NoVoltTime = 0;
|
|
dDoorMoveL = 0.0;
|
|
dDoorMoveR = 0.0;
|
|
// for (int i=0;i<8;i++)
|
|
//{
|
|
// DoorSpeedFactor[i]=random(150);
|
|
// DoorSpeedFactor[i]=(DoorSpeedFactor[i]+100)/100;
|
|
//}
|
|
mdModel = NULL;
|
|
mdKabina = NULL;
|
|
ReplacableSkinID[0] = 0;
|
|
ReplacableSkinID[1] = 0;
|
|
ReplacableSkinID[2] = 0;
|
|
ReplacableSkinID[3] = 0;
|
|
ReplacableSkinID[4] = 0;
|
|
iAlpha = 0x30300030; // tak gdy tekstury wymienne nie mają przezroczystości
|
|
// smWiazary[0]=smWiazary[1]=NULL;
|
|
smWahacze[0] = smWahacze[1] = smWahacze[2] = smWahacze[3] = NULL;
|
|
fWahaczeAmp = 0;
|
|
smBrakeMode = NULL;
|
|
smLoadMode = NULL;
|
|
mdLoad = NULL;
|
|
mdLowPolyInt = NULL;
|
|
mdPrzedsionek = NULL;
|
|
smMechanik0 = smMechanik1 = NULL;
|
|
smBuforLewy[0] = smBuforLewy[1] = NULL;
|
|
smBuforPrawy[0] = smBuforPrawy[1] = NULL;
|
|
enginevolume = 0;
|
|
smBogie[0] = smBogie[1] = NULL;
|
|
bogieRot[0] = bogieRot[1] = vector3(0, 0, 0);
|
|
modelRot = vector3(0, 0, 0);
|
|
eng_vol_act = 0.8;
|
|
eng_dfrq = 0;
|
|
eng_frq_act = 1;
|
|
eng_turbo = 0;
|
|
cp1 = cp2 = sp1 = sp2 = 0;
|
|
iDirection = 1; // stoi w kierunku tradycyjnym (0, gdy jest odwrócony)
|
|
iAxleFirst = 0; // numer pierwszej osi w kierunku ruchu (przełączenie
|
|
// następuje, gdy osie sa na
|
|
// tym samym torze)
|
|
iInventory = 0; // flagi bitowe posiadanych submodeli (zaktualizuje się po
|
|
// wczytaniu MMD)
|
|
RaLightsSet(0, 0); // początkowe zerowanie stanu świateł
|
|
// Ra: domyślne ilości animacji dla zgodności wstecz (gdy brak ilości podanych
|
|
// w MMD)
|
|
iAnimType[ANIM_WHEELS] = 8; // 0-osie (8)
|
|
iAnimType[ANIM_DOORS] = 8; // 1-drzwi (8)
|
|
iAnimType[ANIM_LEVERS] = 4; // 2-wahacze (4) - np. nogi konia
|
|
iAnimType[ANIM_BUFFERS] = 4; // 3-zderzaki (4)
|
|
iAnimType[ANIM_BOOGIES] = 2; // 4-wózki (2)
|
|
iAnimType[ANIM_PANTS] = 2; // 5-pantografy (2)
|
|
iAnimType[ANIM_STEAMS] = 0; // 6-tłoki (napęd parowozu)
|
|
iAnimations = 0; // na razie nie ma żadnego
|
|
pAnimations = NULL;
|
|
pAnimated = NULL;
|
|
fShade = 0.0; // standardowe oświetlenie na starcie
|
|
iHornWarning = 1; // numer syreny do użycia po otrzymaniu sygnału do jazdy
|
|
asDestination = "none"; // stojący nigdzie nie jedzie
|
|
pValveGear = NULL; // Ra: tymczasowo
|
|
iCabs = 0; // maski bitowe modeli kabin
|
|
smBrakeSet = NULL; // nastawa hamulca (wajcha)
|
|
smLoadSet = NULL; // nastawa ładunku (wajcha)
|
|
smWiper = NULL; // wycieraczka (poniekąd też wajcha)
|
|
fScanDist = 300.0; // odległość skanowania, zwiększana w trybie łączenia
|
|
ctOwner = NULL; // na początek niczyj
|
|
iOverheadMask = 0; // maska przydzielana przez AI pojazdom posiadającym
|
|
// pantograf, aby wymuszały
|
|
// jazdę bezprądową
|
|
tmpTraction.TractionVoltage = 0; // Ra 2F1H: prowizorka, trzeba przechować
|
|
// napięcie, żeby nie wywalało WS pod
|
|
// izolatorem
|
|
fAdjustment = 0.0; // korekcja odległości pomiędzy wózkami (np. na łukach)
|
|
}
|
|
|
|
TDynamicObject::~TDynamicObject()
|
|
{ // McZapkie-250302 - zamykanie logowania
|
|
// parametrow fizycznych
|
|
SafeDelete(Mechanik);
|
|
SafeDelete(MoverParameters);
|
|
// Ra: wyłączanie dźwięków powinno być dodane w ich destruktorach, ale się
|
|
// sypie
|
|
/* to też się sypie
|
|
for (int i=0;i<MaxAxles;++i)
|
|
rsStukot[i].Stop(); //dzwieki poszczegolnych osi
|
|
rsSilnik.Stop();
|
|
rsWentylator.Stop();
|
|
rsPisk.Stop();
|
|
rsDerailment.Stop();
|
|
sPantUp.Stop();
|
|
sPantDown.Stop();
|
|
sBrakeAcc.Stop(); //dzwiek przyspieszacza
|
|
rsDiesielInc.Stop();
|
|
rscurve.Stop();
|
|
*/
|
|
delete[] pAnimations; // obiekty obsługujące animację
|
|
delete[] pAnimated; // lista animowanych submodeli
|
|
}
|
|
|
|
double
|
|
TDynamicObject::Init(AnsiString Name, // nazwa pojazdu, np. "EU07-424"
|
|
AnsiString BaseDir, // z którego katalogu wczytany, np. "PKP/EU07"
|
|
AnsiString asReplacableSkin, // nazwa wymiennej tekstury
|
|
AnsiString Type_Name, // nazwa CHK/MMD, np. "303E"
|
|
TTrack *Track, // tor początkowy wstwawienia (początek składu)
|
|
double fDist, // dystans względem punktu 1
|
|
AnsiString DriverType, // typ obsady
|
|
double fVel, // prędkość początkowa
|
|
AnsiString TrainName, // nazwa składu, np. "PE2307" albo Vmax, jeśli pliku
|
|
// nie ma a są cyfry
|
|
float Load, // ilość ładunku
|
|
AnsiString LoadType, // nazwa ładunku
|
|
bool Reversed, // true, jeśli ma stać odwrotnie w składzie
|
|
AnsiString MoreParams // dodatkowe parametry wczytywane w postaci tekstowej
|
|
)
|
|
{ // Ustawienie początkowe pojazdu
|
|
iDirection = (Reversed ? 0 : 1); // Ra: 0, jeśli ma być wstawiony jako obrócony tyłem
|
|
asBaseDir = "dynamic\\" + BaseDir + "\\"; // McZapkie-310302
|
|
asName = Name;
|
|
AnsiString asAnimName = ""; // zmienna robocza do wyszukiwania osi i wózków
|
|
// Ra: zmieniamy znaczenie obsady na jednoliterowe, żeby dosadzić kierownika
|
|
if (DriverType == "headdriver")
|
|
DriverType = "1"; // sterujący kabiną +1
|
|
else if (DriverType == "reardriver")
|
|
DriverType = "2"; // sterujący kabiną -1
|
|
// else if (DriverType=="connected") DriverType="c"; //tego trzeba się pozbyć
|
|
// na rzecz
|
|
// ukrotnienia
|
|
else if (DriverType == "passenger")
|
|
DriverType = "p"; // to do przemyślenia
|
|
else if (DriverType == "nobody")
|
|
DriverType = ""; // nikt nie siedzi
|
|
int Cab = 0; // numer kabiny z obsadą (nie można zająć obu)
|
|
if (DriverType.Pos("1")) // od przodu składu
|
|
Cab = 1; // iDirection?1:-1; //iDirection=1 gdy normalnie, =0 odwrotnie
|
|
else if (DriverType.Pos("2")) // od tyłu składu
|
|
Cab = -1; // iDirection?-1:1;
|
|
else if (DriverType == "p")
|
|
{
|
|
if (random(6) < 3)
|
|
Cab = 1;
|
|
else
|
|
Cab = -1; // losowy przydział kabiny
|
|
}
|
|
/* to nie ma uzasadnienia
|
|
else
|
|
{//obsada nie rozpoznana
|
|
Cab=0; //McZapkie-010303: w przyszlosci dac tez pomocnika, palacza,
|
|
konduktora itp.
|
|
Error("Unknown DriverType description: "+DriverType);
|
|
DriverType="nobody";
|
|
}
|
|
*/
|
|
// utworzenie parametrów fizyki
|
|
MoverParameters =
|
|
new TMoverParameters(iDirection ? fVel : -fVel, Type_Name, asName, Load, LoadType, Cab);
|
|
iLights = MoverParameters->iLights; // wskaźnik na stan własnych świateł
|
|
// (zmienimy dla rozrządczych EZT)
|
|
// McZapkie: TypeName musi byc nazwą CHK/MMD pojazdu
|
|
if (!MoverParameters->LoadChkFile(asBaseDir))
|
|
{ // jak wczytanie CHK się nie uda, to błąd
|
|
if (ConversionError == -8)
|
|
ErrorLog("Missed file: " + BaseDir + "\\" + Type_Name + ".fiz");
|
|
Error("Cannot load dynamic object " + asName + " from:\r\n" + BaseDir + "\\" + Type_Name +
|
|
".fiz\r\nError " + ConversionError + " in line " + LineCount);
|
|
return 0.0; // zerowa długość to brak pojazdu
|
|
}
|
|
bool driveractive = (fVel != 0.0); // jeśli prędkość niezerowa, to aktywujemy ruch
|
|
if (!MoverParameters->CheckLocomotiveParameters(
|
|
driveractive,
|
|
(fVel > 0 ? 1 : -1) * Cab *
|
|
(iDirection ? 1 : -1))) // jak jedzie lub obsadzony to gotowy do drogi
|
|
{
|
|
Error("Parameters mismatch: dynamic object " + asName + " from\n" + BaseDir + "\\" +
|
|
Type_Name);
|
|
return 0.0; // zerowa długość to brak pojazdu
|
|
}
|
|
MoverParameters->BrakeLevelSet(
|
|
MoverParameters->BrakeCtrlPos); // poprawienie hamulca po ewentualnym
|
|
// przestawieniu przez Pascal
|
|
|
|
// dodatkowe parametry yB
|
|
MoreParams += "."; // wykonuje o jedną iterację za mało, więc trzeba mu dodać
|
|
// kropkę na koniec
|
|
int kropka = MoreParams.Pos("."); // znajdź kropke
|
|
AnsiString ActPar; // na parametry
|
|
while (kropka > 0) // jesli sa kropki jeszcze
|
|
{
|
|
int dlugosc = MoreParams.Length();
|
|
ActPar = MoreParams.SubString(1, kropka - 1).UpperCase(); // pierwszy parametr;
|
|
MoreParams = MoreParams.SubString(kropka + 1, dlugosc - kropka); // reszta do dalszej
|
|
// obrobki
|
|
kropka = MoreParams.Pos(".");
|
|
|
|
if (ActPar.SubString(1, 1) == "B") // jesli hamulce
|
|
{ // sprawdzanie kolejno nastaw
|
|
WriteLog("Wpis hamulca: " + ActPar);
|
|
if (ActPar.Pos("G") > 0)
|
|
{
|
|
MoverParameters->BrakeDelaySwitch(bdelay_G);
|
|
}
|
|
if (ActPar.Pos("P") > 0)
|
|
{
|
|
MoverParameters->BrakeDelaySwitch(bdelay_P);
|
|
}
|
|
if (ActPar.Pos("R") > 0)
|
|
{
|
|
MoverParameters->BrakeDelaySwitch(bdelay_R);
|
|
}
|
|
if (ActPar.Pos("M") > 0)
|
|
{
|
|
MoverParameters->BrakeDelaySwitch(bdelay_R);
|
|
MoverParameters->BrakeDelaySwitch(bdelay_R + bdelay_M);
|
|
}
|
|
// wylaczanie hamulca
|
|
if (ActPar.Pos("<>") > 0) // wylaczanie na probe hamowania naglego
|
|
{
|
|
MoverParameters->BrakeStatus |= 128; // wylacz
|
|
}
|
|
if (ActPar.Pos("0") > 0) // wylaczanie na sztywno
|
|
{
|
|
MoverParameters->BrakeStatus |= 128; // wylacz
|
|
MoverParameters->Hamulec->ForceEmptiness();
|
|
MoverParameters->BrakeReleaser(1); // odluznij automatycznie
|
|
}
|
|
if (ActPar.Pos("E") > 0) // oprozniony
|
|
{
|
|
MoverParameters->Hamulec->ForceEmptiness();
|
|
MoverParameters->BrakeReleaser(1); // odluznij automatycznie
|
|
MoverParameters->Pipe->CreatePress(0);
|
|
MoverParameters->Pipe2->CreatePress(0);
|
|
}
|
|
if (ActPar.Pos("Q") > 0) // oprozniony
|
|
{
|
|
// MoverParameters->Hamulec->ForceEmptiness(); //TODO: sprawdzic,
|
|
// dlaczego
|
|
// pojawia sie blad przy uzyciu tej linijki w lokomotywie
|
|
MoverParameters->BrakeReleaser(1); // odluznij automatycznie
|
|
MoverParameters->Pipe->CreatePress(0.0);
|
|
MoverParameters->PipePress = 0.0;
|
|
MoverParameters->Pipe2->CreatePress(0.0);
|
|
MoverParameters->ScndPipePress = 0.0;
|
|
MoverParameters->PantVolume = 1;
|
|
MoverParameters->PantPress = 0;
|
|
MoverParameters->CompressedVolume = 0;
|
|
}
|
|
|
|
if (ActPar.Pos("1") > 0) // wylaczanie 10%
|
|
{
|
|
if (random(10) < 1) // losowanie 1/10
|
|
{
|
|
MoverParameters->BrakeStatus |= 128; // wylacz
|
|
MoverParameters->Hamulec->ForceEmptiness();
|
|
MoverParameters->BrakeReleaser(1); // odluznij automatycznie
|
|
}
|
|
}
|
|
if (ActPar.Pos("X") > 0) // agonalny wylaczanie 20%, usrednienie przekladni
|
|
{
|
|
if (random(100) < 20) // losowanie 20/100
|
|
{
|
|
MoverParameters->BrakeStatus |= 128; // wylacz
|
|
MoverParameters->Hamulec->ForceEmptiness();
|
|
MoverParameters->BrakeReleaser(1); // odluznij automatycznie
|
|
}
|
|
if (MoverParameters->BrakeCylMult[2] * MoverParameters->BrakeCylMult[1] >
|
|
0.01) // jesli jest nastawiacz mechaniczny PL
|
|
{
|
|
float rnd = random(100);
|
|
if (rnd < 20) // losowanie 20/100 usrednienie
|
|
{
|
|
MoverParameters->BrakeCylMult[2] = MoverParameters->BrakeCylMult[1] =
|
|
(MoverParameters->BrakeCylMult[2] + MoverParameters->BrakeCylMult[1]) /
|
|
2;
|
|
}
|
|
else if (rnd < 70) // losowanie 70/100-20/100 oslabienie
|
|
{
|
|
MoverParameters->BrakeCylMult[1] = MoverParameters->BrakeCylMult[1] * 0.50;
|
|
MoverParameters->BrakeCylMult[2] = MoverParameters->BrakeCylMult[2] * 0.75;
|
|
}
|
|
else if (rnd < 80) // losowanie 80/100-70/100 tylko prozny
|
|
{
|
|
MoverParameters->BrakeCylMult[2] = MoverParameters->BrakeCylMult[1];
|
|
}
|
|
else // tylko ladowny
|
|
{
|
|
MoverParameters->BrakeCylMult[1] = MoverParameters->BrakeCylMult[2];
|
|
}
|
|
}
|
|
}
|
|
// nastawianie ladunku
|
|
if (ActPar.Pos("T") > 0) // prozny
|
|
{
|
|
MoverParameters->DecBrakeMult();
|
|
MoverParameters->DecBrakeMult();
|
|
} // dwa razy w dol
|
|
if (ActPar.Pos("H") > 0) // ladowny I (dla P-Ł dalej prozny)
|
|
{
|
|
MoverParameters->IncBrakeMult();
|
|
MoverParameters->IncBrakeMult();
|
|
MoverParameters->DecBrakeMult();
|
|
} // dwa razy w gore i obniz
|
|
if (ActPar.Pos("F") > 0) // ladowny II
|
|
{
|
|
MoverParameters->IncBrakeMult();
|
|
MoverParameters->IncBrakeMult();
|
|
} // dwa razy w gore
|
|
if (ActPar.Pos("N") > 0) // parametr neutralny
|
|
{
|
|
}
|
|
} // koniec hamulce
|
|
else if (ActPar.SubString(1, 1) == "") // tu mozna wpisac inny prefiks i inne rzeczy
|
|
{
|
|
// jakies inne prefiksy
|
|
}
|
|
|
|
} // koniec while kropka
|
|
|
|
if (MoverParameters->CategoryFlag & 2) // jeśli samochód
|
|
{ // ustawianie samochodow na poboczu albo na środku drogi
|
|
if (Track->fTrackWidth < 3.5) // jeśli droga wąska
|
|
MoverParameters->OffsetTrackH = 0.0; // to stawiamy na środku, niezależnie od stanu
|
|
// ruchu
|
|
else if (driveractive) // od 3.5m do 8.0m jedzie po środku pasa, dla
|
|
// szerszych w odległości
|
|
// 1.5m
|
|
MoverParameters->OffsetTrackH =
|
|
Track->fTrackWidth <= 8.0 ? -Track->fTrackWidth * 0.25 : -1.5;
|
|
else // jak stoi, to kołem na poboczu i pobieramy szerokość razem z
|
|
// poboczem, ale nie z
|
|
// chodnikiem
|
|
MoverParameters->OffsetTrackH =
|
|
-0.5 * (Track->WidthTotal() - MoverParameters->Dim.W) + 0.05;
|
|
iHornWarning = 0; // nie będzie trąbienia po podaniu zezwolenia na jazdę
|
|
if (fDist < 0.0) //-0.5*MoverParameters->Dim.L) //jeśli jest przesunięcie do tyłu
|
|
if (!Track->CurrentPrev()) // a nie ma tam odcinka i trzeba by coś
|
|
// wygenerować
|
|
fDist = -fDist; // to traktujemy, jakby przesunięcie było w drugą stronę
|
|
}
|
|
// w wagonie tez niech jedzie
|
|
// if (MoverParameters->MainCtrlPosNo>0 &&
|
|
// if (MoverParameters->CabNo!=0)
|
|
if (DriverType != "")
|
|
{ // McZapkie-040602: jeśli coś siedzi w pojeździe
|
|
if (Name == AnsiString(Global::asHumanCtrlVehicle)) // jeśli pojazd wybrany do prowadzenia
|
|
{
|
|
if (DebugModeFlag ? false : MoverParameters->EngineType !=
|
|
Dumb) // jak nie Debugmode i nie jest dumbem
|
|
Controller = Humandriver; // wsadzamy tam sterującego
|
|
else // w przeciwnym razie trzeba włączyć pokazywanie kabiny
|
|
bDisplayCab = true;
|
|
}
|
|
// McZapkie-151102: rozkład jazdy czytany z pliku *.txt z katalogu w którym
|
|
// jest sceneria
|
|
if (DriverType.Pos("1") || DriverType.Pos("2"))
|
|
{ // McZapkie-110303: mechanik i rozklad tylko gdy jest obsada
|
|
// MoverParameters->ActiveCab=MoverParameters->CabNo; //ustalenie aktywnej
|
|
// kabiny
|
|
// (rozrząd)
|
|
Mechanik = new TController(Controller, this, Aggressive);
|
|
if (TrainName.IsEmpty()) // jeśli nie w składzie
|
|
{
|
|
Mechanik->DirectionInitial(); // załączenie rozrządu (wirtualne kabiny) itd.
|
|
Mechanik->PutCommand(
|
|
"Timetable:", iDirection ? -fVel : fVel, 0,
|
|
NULL); // tryb pociągowy z ustaloną prędkością (względem sprzęgów)
|
|
}
|
|
// if (TrainName!="none")
|
|
// Mechanik->PutCommand("Timetable:"+TrainName,fVel,0,NULL);
|
|
}
|
|
else if (DriverType == "p")
|
|
{ // obserwator w charakterze pasażera
|
|
// Ra: to jest niebezpieczne, bo w razie co będzie pomagał hamulcem
|
|
// bezpieczeństwa
|
|
Mechanik = new TController(Controller, this, Easyman, false);
|
|
}
|
|
}
|
|
// McZapkie-250202
|
|
iAxles = (MaxAxles < MoverParameters->NAxles) ? MaxAxles : MoverParameters->NAxles; // ilość osi
|
|
// wczytywanie z pliku nazwatypu.mmd, w tym model
|
|
LoadMMediaFile(asBaseDir, Type_Name, asReplacableSkin);
|
|
// McZapkie-100402: wyszukiwanie submodeli sprzegów
|
|
btCoupler1.Init("coupler1", mdModel, false); // false - ma być wyłączony
|
|
btCoupler2.Init("coupler2", mdModel, false);
|
|
btCPneumatic1.Init("cpneumatic1", mdModel);
|
|
btCPneumatic2.Init("cpneumatic2", mdModel);
|
|
btCPneumatic1r.Init("cpneumatic1r", mdModel);
|
|
btCPneumatic2r.Init("cpneumatic2r", mdModel);
|
|
btPneumatic1.Init("pneumatic1", mdModel);
|
|
btPneumatic2.Init("pneumatic2", mdModel);
|
|
btPneumatic1r.Init("pneumatic1r", mdModel);
|
|
btPneumatic2r.Init("pneumatic2r", mdModel);
|
|
btCCtrl1.Init("cctrl1", mdModel, false);
|
|
btCCtrl2.Init("cctrl2", mdModel, false);
|
|
btCPass1.Init("cpass1", mdModel, false);
|
|
btCPass2.Init("cpass2", mdModel, false);
|
|
// sygnaly
|
|
// ABu 060205: Zmiany dla koncowek swiecacych:
|
|
btEndSignals11.Init("endsignal13", mdModel, false);
|
|
btEndSignals21.Init("endsignal23", mdModel, false);
|
|
btEndSignals13.Init("endsignal12", mdModel, false);
|
|
btEndSignals23.Init("endsignal22", mdModel, false);
|
|
iInventory |= btEndSignals11.Active() ? 0x01 : 0; // informacja, czy ma poszczególne światła
|
|
iInventory |= btEndSignals21.Active() ? 0x02 : 0;
|
|
iInventory |= btEndSignals13.Active() ? 0x04 : 0;
|
|
iInventory |= btEndSignals23.Active() ? 0x08 : 0;
|
|
// ABu: to niestety zostawione dla kompatybilnosci modeli:
|
|
btEndSignals1.Init("endsignals1", mdModel, false);
|
|
btEndSignals2.Init("endsignals2", mdModel, false);
|
|
btEndSignalsTab1.Init("endtab1", mdModel, false);
|
|
btEndSignalsTab2.Init("endtab2", mdModel, false);
|
|
iInventory |= btEndSignals1.Active() ? 0x10 : 0;
|
|
iInventory |= btEndSignals2.Active() ? 0x20 : 0;
|
|
iInventory |= btEndSignalsTab1.Active() ? 0x40 : 0; // tabliczki blaszane
|
|
iInventory |= btEndSignalsTab2.Active() ? 0x80 : 0;
|
|
// ABu Uwaga! tu zmienic w modelu!
|
|
btHeadSignals11.Init("headlamp13", mdModel, false); // lewe
|
|
btHeadSignals12.Init("headlamp11", mdModel, false); // górne
|
|
btHeadSignals13.Init("headlamp12", mdModel, false); // prawe
|
|
btHeadSignals21.Init("headlamp23", mdModel, false);
|
|
btHeadSignals22.Init("headlamp21", mdModel, false);
|
|
btHeadSignals23.Init("headlamp22", mdModel, false);
|
|
TurnOff(); // resetowanie zmiennych submodeli
|
|
// wyszukiwanie zderzakow
|
|
if (mdModel) // jeśli ma w czym szukać
|
|
for (int i = 0; i < 2; i++)
|
|
{
|
|
asAnimName = AnsiString("buffer_left0") + (i + 1);
|
|
smBuforLewy[i] = mdModel->GetFromName(asAnimName.c_str());
|
|
if (smBuforLewy[i])
|
|
smBuforLewy[i]->WillBeAnimated(); // ustawienie flagi animacji
|
|
asAnimName = AnsiString("buffer_right0") + (i + 1);
|
|
smBuforPrawy[i] = mdModel->GetFromName(asAnimName.c_str());
|
|
if (smBuforPrawy[i])
|
|
smBuforPrawy[i]->WillBeAnimated();
|
|
}
|
|
for (int i = 0; i < iAxles; i++) // wyszukiwanie osi (0 jest na końcu, dlatego dodajemy
|
|
// długość?)
|
|
dRailPosition[i] =
|
|
(Reversed ? -dWheelsPosition[i] : (dWheelsPosition[i] + MoverParameters->Dim.L)) +
|
|
fDist;
|
|
// McZapkie-250202 end.
|
|
Track->AddDynamicObject(this); // wstawiamy do toru na pozycję 0, a potem przesuniemy
|
|
// McZapkie: zmieniono na ilosc osi brane z chk
|
|
// iNumAxles=(MoverParameters->NAxles>3 ? 4 : 2 );
|
|
iNumAxles = 2;
|
|
// McZapkie-090402: odleglosc miedzy czopami skretu lub osiami
|
|
fAxleDist = Max0R(MoverParameters->BDist, MoverParameters->ADist);
|
|
if (fAxleDist < 0.2)
|
|
fAxleDist = 0.2; //żeby się dało wektory policzyć
|
|
if (fAxleDist > MoverParameters->Dim.L - 0.2) // nie mogą być za daleko
|
|
fAxleDist = MoverParameters->Dim.L - 0.2; // bo będzie "walenie w mur"
|
|
double fAxleDistHalf = fAxleDist * 0.5;
|
|
// WriteLog("Dynamic "+Type_Name+" of length "+MoverParameters->Dim.L+" at
|
|
// "+AnsiString(fDist));
|
|
// if (Cab) //jeśli ma obsadę - zgodność wstecz, jeśli tor startowy ma Event0
|
|
// if (Track->Event0) //jeśli tor ma Event0
|
|
// if (fDist>=0.0) //jeśli jeśli w starych sceneriach początek składu byłby
|
|
// wysunięty na ten
|
|
// tor
|
|
// if (fDist<=0.5*MoverParameters->Dim.L+0.2) //ale nie jest wysunięty
|
|
// fDist+=0.5*MoverParameters->Dim.L+0.2; //wysunąć go na ten tor
|
|
// przesuwanie pojazdu tak, aby jego początek był we wskazanym miejcu
|
|
fDist -= 0.5 * MoverParameters->Dim.L; // dodajemy pół długości pojazdu, bo
|
|
// ustawiamy jego środek (zliczanie na
|
|
// minus)
|
|
switch (iNumAxles)
|
|
{ // Ra: pojazdy wstawiane są na tor początkowy, a potem
|
|
// przesuwane
|
|
case 2: // ustawianie osi na torze
|
|
Axle0.Init(Track, this, iDirection ? 1 : -1);
|
|
Axle0.Move((iDirection ? fDist : -fDist) + fAxleDistHalf, false);
|
|
Axle1.Init(Track, this, iDirection ? 1 : -1);
|
|
Axle1.Move((iDirection ? fDist : -fDist) - fAxleDistHalf,
|
|
false); // false, żeby nie generować eventów
|
|
// Axle2.Init(Track,this,iDirection?1:-1);
|
|
// Axle2.Move((iDirection?fDist:-fDist)-fAxleDistHalft+0.01),false);
|
|
// Axle3.Init(Track,this,iDirection?1:-1);
|
|
// Axle3.Move((iDirection?fDist:-fDist)+fAxleDistHalf-0.01),false);
|
|
break;
|
|
case 4:
|
|
Axle0.Init(Track, this, iDirection ? 1 : -1);
|
|
Axle0.Move((iDirection ? fDist : -fDist) + (fAxleDistHalf + MoverParameters->ADist * 0.5),
|
|
false);
|
|
Axle1.Init(Track, this, iDirection ? 1 : -1);
|
|
Axle1.Move((iDirection ? fDist : -fDist) - (fAxleDistHalf + MoverParameters->ADist * 0.5),
|
|
false);
|
|
// Axle2.Init(Track,this,iDirection?1:-1);
|
|
// Axle2.Move((iDirection?fDist:-fDist)-(fAxleDistHalf-MoverParameters->ADist*0.5),false);
|
|
// Axle3.Init(Track,this,iDirection?1:-1);
|
|
// Axle3.Move((iDirection?fDist:-fDist)+(fAxleDistHalf-MoverParameters->ADist*0.5),false);
|
|
break;
|
|
}
|
|
Move(0.0001); // potrzebne do wyliczenia aktualnej pozycji; nie może być zero,
|
|
// bo nie przeliczy
|
|
// pozycji
|
|
// teraz jeszcze trzeba przypisać pojazdy do nowego toru, bo przesuwanie
|
|
// początkowe osi nie
|
|
// zrobiło tego
|
|
ABuCheckMyTrack(); // zmiana toru na ten, co oś Axle0 (oś z przodu)
|
|
TLocation loc; // Ra: ustawienie pozycji do obliczania sprzęgów
|
|
loc.X = -vPosition.x;
|
|
loc.Y = vPosition.z;
|
|
loc.Z = vPosition.y;
|
|
MoverParameters->Loc = loc; // normalnie przesuwa ComputeMovement() w Update()
|
|
// pOldPos4=Axle1.pPosition; //Ra: nie używane
|
|
// pOldPos1=Axle0.pPosition;
|
|
// ActualTrack= GetTrack(); //McZapkie-030303
|
|
// ABuWozki 060504
|
|
if (mdModel) // jeśli ma w czym szukać
|
|
{
|
|
smBogie[0] = mdModel->GetFromName("bogie1"); // Ra: bo nazwy są małymi
|
|
smBogie[1] = mdModel->GetFromName("bogie2");
|
|
if (!smBogie[0])
|
|
smBogie[0] = mdModel->GetFromName("boogie01"); // Ra: alternatywna nazwa
|
|
if (!smBogie[1])
|
|
smBogie[1] = mdModel->GetFromName("boogie02"); // Ra: alternatywna nazwa
|
|
if (smBogie[0])
|
|
smBogie[0]->WillBeAnimated();
|
|
if (smBogie[1])
|
|
smBogie[1]->WillBeAnimated();
|
|
}
|
|
// ABu: zainicjowanie zmiennej, zeby nic sie nie ruszylo
|
|
// w pierwszej klatce, potem juz liczona prawidlowa wartosc masy
|
|
MoverParameters->ComputeConstans();
|
|
/*Ra: to nie działa - Event0 musi być wykonywany ciągle
|
|
if (fVel==0.0) //jeśli stoi
|
|
if (MoverParameters->CabNo!=0) //i ma kogoś w kabinie
|
|
if (Track->Event0) //a jest w tym torze event od stania
|
|
RaAxleEvent(Track->Event0); //dodanie eventu stania do kolejki
|
|
*/
|
|
vFloor = vector3(0, 0, MoverParameters->Floor); // wektor podłogi dla wagonów, przesuwa ładunek
|
|
return MoverParameters->Dim.L; // długość większa od zera oznacza OK; 2mm docisku?
|
|
}
|
|
|
|
void TDynamicObject::FastMove(double fDistance)
|
|
{
|
|
MoverParameters->dMoveLen = MoverParameters->dMoveLen + fDistance;
|
|
}
|
|
|
|
void TDynamicObject::Move(double fDistance)
|
|
{ // przesuwanie pojazdu po
|
|
// trajektorii polega na
|
|
// przesuwaniu poszczególnych osi
|
|
// Ra: wartość prędkości 2km/h ma ograniczyć aktywację eventów w przypadku
|
|
// drgań
|
|
if (Axle0.GetTrack() == Axle1.GetTrack()) // przed przesunięciem
|
|
{ // powiązanie pojazdu z osią można zmienić tylko wtedy, gdy skrajne osie są
|
|
// na tym samym torze
|
|
if (MoverParameters->Vel >
|
|
2) //|[km/h]| nie ma sensu zmiana osi, jesli pojazd drga na postoju
|
|
iAxleFirst = (MoverParameters->V >= 0.0) ?
|
|
1 :
|
|
0; //[m/s] ?1:0 - aktywna druga oś w kierunku jazdy
|
|
// aktualnie eventy aktywuje druga oś, żeby AI nie wyłączało sobie semafora
|
|
// za szybko
|
|
}
|
|
if (fDistance > 0.0)
|
|
{ // gdy ruch w stronę sprzęgu 0, doliczyć korektę do osi 1
|
|
bEnabled &= Axle0.Move(fDistance, !iAxleFirst); // oś z przodu pojazdu
|
|
bEnabled &= Axle1.Move(fDistance /*-fAdjustment*/, iAxleFirst); // oś z tyłu pojazdu
|
|
}
|
|
else if (fDistance < 0.0)
|
|
{ // gdy ruch w stronę sprzęgu 1, doliczyć korektę do osi 0
|
|
bEnabled &= Axle1.Move(fDistance, iAxleFirst); // oś z tyłu pojazdu prusza się pierwsza
|
|
bEnabled &= Axle0.Move(fDistance /*-fAdjustment*/, !iAxleFirst); // oś z przodu pojazdu
|
|
}
|
|
else // gf: bez wywolania Move na postoju nie ma event0
|
|
{
|
|
bEnabled &= Axle1.Move(fDistance, iAxleFirst); // oś z tyłu pojazdu prusza się pierwsza
|
|
bEnabled &= Axle0.Move(fDistance, !iAxleFirst); // oś z przodu pojazdu
|
|
}
|
|
if (fDistance != 0.0) // nie liczyć ponownie, jeśli stoi
|
|
{ // liczenie pozycji pojazdu tutaj, bo jest używane w wielu miejscach
|
|
vPosition = 0.5 * (Axle1.pPosition + Axle0.pPosition); //środek między skrajnymi osiami
|
|
vFront = Axle0.pPosition - Axle1.pPosition; // wektor pomiędzy skrajnymi osiami
|
|
// Ra 2F1J: to nie jest stabilne (powoduje rzucanie taborem) i wymaga
|
|
// dopracowania
|
|
fAdjustment = vFront.Length() - fAxleDist; // na łuku będzie ujemny
|
|
// if (fabs(fAdjustment)>0.02) //jeśli jest zbyt dużo, to rozłożyć na kilka
|
|
// przeliczeń
|
|
// (wygasza drgania?)
|
|
//{//parę centymetrów trzeba by już skorygować; te błędy mogą się też
|
|
// generować na ostrych
|
|
//łukach
|
|
// fAdjustment*=0.5; //w jednym kroku korygowany jest ułamek błędu
|
|
//}
|
|
// else
|
|
// fAdjustment=0.0;
|
|
vFront = Normalize(vFront); // kierunek ustawienia pojazdu (wektor jednostkowy)
|
|
vLeft = Normalize(CrossProduct(vWorldUp, vFront)); // wektor poziomy w lewo,
|
|
// normalizacja potrzebna z powodu
|
|
// pochylenia (vFront)
|
|
vUp = CrossProduct(vFront, vLeft); // wektor w górę, będzie jednostkowy
|
|
modelRot.z = atan2(-vFront.x, vFront.z); // kąt obrotu pojazdu [rad]; z ABuBogies()
|
|
double a = ((Axle1.GetRoll() + Axle0.GetRoll())); // suma przechyłek
|
|
if (a != 0.0)
|
|
{ // wyznaczanie przechylenia tylko jeśli jest przechyłka
|
|
// można by pobrać wektory normalne z toru...
|
|
mMatrix.Identity(); // ta macierz jest potrzebna głównie do wyświetlania
|
|
mMatrix.Rotation(a * 0.5, vFront); // obrót wzdłuż osi o przechyłkę
|
|
vUp = mMatrix * vUp; // wektor w górę pojazdu (przekręcenie na przechyłce)
|
|
// vLeft=mMatrix*DynamicObject->vLeft;
|
|
// vUp=CrossProduct(vFront,vLeft); //wektor w górę
|
|
// vLeft=Normalize(CrossProduct(vWorldUp,vFront)); //wektor w lewo
|
|
vLeft = Normalize(CrossProduct(vUp, vFront)); // wektor w lewo
|
|
// vUp=CrossProduct(vFront,vLeft); //wektor w górę
|
|
}
|
|
mMatrix.Identity(); // to też można by od razu policzyć, ale potrzebne jest
|
|
// do wyświetlania
|
|
mMatrix.BasisChange(vLeft, vUp, vFront); // przesuwanie jest jednak rzadziej niż
|
|
// renderowanie
|
|
mMatrix = Inverse(mMatrix); // wyliczenie macierzy dla pojazdu (potrzebna
|
|
// tylko do wyświetlania?)
|
|
// if (MoverParameters->CategoryFlag&2)
|
|
{ // przesunięcia są używane po wyrzuceniu pociągu z toru
|
|
vPosition.x += MoverParameters->OffsetTrackH * vLeft.x; // dodanie przesunięcia w bok
|
|
vPosition.z +=
|
|
MoverParameters->OffsetTrackH * vLeft.z; // vLeft jest wektorem poprzecznym
|
|
// if () na przechyłce będzie dodatkowo zmiana wysokości samochodu
|
|
vPosition.y += MoverParameters->OffsetTrackV; // te offsety są liczone przez moverparam
|
|
}
|
|
// Ra: skopiowanie pozycji do fizyki, tam potrzebna do zrywania sprzęgów
|
|
// MoverParameters->Loc.X=-vPosition.x; //robi to {Fast}ComputeMovement()
|
|
// MoverParameters->Loc.Y= vPosition.z;
|
|
// MoverParameters->Loc.Z= vPosition.y;
|
|
// obliczanie pozycji sprzęgów do liczenia zderzeń
|
|
vector3 dir = (0.5 * MoverParameters->Dim.L) * vFront; // wektor sprzęgu
|
|
vCoulpler[0] = vPosition + dir; // współrzędne sprzęgu na początku
|
|
vCoulpler[1] = vPosition - dir; // współrzędne sprzęgu na końcu
|
|
MoverParameters->vCoulpler[0] = vCoulpler[0]; // tymczasowo kopiowane na inny poziom
|
|
MoverParameters->vCoulpler[1] = vCoulpler[1];
|
|
// bCameraNear=
|
|
// if (bCameraNear) //jeśli istotne są szczegóły (blisko kamery)
|
|
{ // przeliczenie cienia
|
|
TTrack *t0 = Axle0.GetTrack(); // już po przesunięciu
|
|
TTrack *t1 = Axle1.GetTrack();
|
|
if ((t0->eEnvironment == e_flat) && (t1->eEnvironment == e_flat)) // może być
|
|
// e_bridge...
|
|
fShade = 0.0; // standardowe oświetlenie
|
|
else
|
|
{ // jeżeli te tory mają niestandardowy stopień zacienienia
|
|
// (e_canyon, e_tunnel)
|
|
if (t0->eEnvironment == t1->eEnvironment)
|
|
{
|
|
switch (t0->eEnvironment)
|
|
{ // typ zmiany oświetlenia
|
|
case e_canyon:
|
|
fShade = 0.65;
|
|
break; // zacienienie w kanionie
|
|
case e_tunnel:
|
|
fShade = 0.20;
|
|
break; // zacienienie w tunelu
|
|
}
|
|
}
|
|
else // dwa różne
|
|
{ // liczymy proporcję
|
|
double d = Axle0.GetTranslation(); // aktualne położenie na torze
|
|
if (Axle0.GetDirection() < 0)
|
|
d = t0->fTrackLength - d; // od drugiej strony liczona długość
|
|
d /= fAxleDist; // rozsataw osi procentowe znajdowanie się na torze
|
|
switch (t0->eEnvironment)
|
|
{ // typ zmiany oświetlenia - zakładam, że
|
|
// drugi tor ma e_flat
|
|
case e_canyon:
|
|
fShade = (d * 0.65) + (1.0 - d);
|
|
break; // zacienienie w kanionie
|
|
case e_tunnel:
|
|
fShade = (d * 0.20) + (1.0 - d);
|
|
break; // zacienienie w tunelu
|
|
}
|
|
switch (t1->eEnvironment)
|
|
{ // typ zmiany oświetlenia - zakładam, że
|
|
// pierwszy tor ma e_flat
|
|
case e_canyon:
|
|
fShade = d + (1.0 - d) * 0.65;
|
|
break; // zacienienie w kanionie
|
|
case e_tunnel:
|
|
fShade = d + (1.0 - d) * 0.20;
|
|
break; // zacienienie w tunelu
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
void TDynamicObject::AttachPrev(TDynamicObject *Object, int iType)
|
|
{ // Ra: doczepia Object na końcu
|
|
// składu (nazwa funkcji może być
|
|
// myląca)
|
|
// Ra: używane tylko przy wczytywaniu scenerii
|
|
/*
|
|
//Ra: po wstawieniu pojazdu do scenerii nie miał on ustawionej pozycji, teraz
|
|
już ma
|
|
TLocation loc;
|
|
loc.X=-vPosition.x;
|
|
loc.Y=vPosition.z;
|
|
loc.Z=vPosition.y;
|
|
MoverParameters->Loc=loc; //Ra: do obliczania sprzęgów, na starcie nie są
|
|
przesunięte
|
|
loc.X=-Object->vPosition.x;
|
|
loc.Y=Object->vPosition.z;
|
|
loc.Z=Object->vPosition.y;
|
|
Object->MoverParameters->Loc=loc; //ustawienie dodawanego pojazdu
|
|
*/
|
|
MoverParameters->Attach(iDirection, Object->iDirection ^ 1, Object->MoverParameters, iType,
|
|
true);
|
|
MoverParameters->Couplers[iDirection].Render = false;
|
|
Object->MoverParameters->Attach(Object->iDirection ^ 1, iDirection, MoverParameters, iType,
|
|
true);
|
|
Object->MoverParameters->Couplers[Object->iDirection ^ 1].Render =
|
|
true; // rysowanie sprzęgu w dołączanym
|
|
if (iDirection)
|
|
{ //łączenie standardowe
|
|
NextConnected = Object; // normalnie doczepiamy go sobie do sprzęgu 1
|
|
NextConnectedNo = Object->iDirection ^ 1;
|
|
}
|
|
else
|
|
{ //łączenie odwrotne
|
|
PrevConnected = Object; // doczepiamy go sobie do sprzęgu 0, gdy stoimy odwrotnie
|
|
PrevConnectedNo = Object->iDirection ^ 1;
|
|
}
|
|
if (Object->iDirection)
|
|
{ // dołączany jest normalnie ustawiany
|
|
Object->PrevConnected = this; // on ma nas z przodu
|
|
Object->PrevConnectedNo = iDirection;
|
|
}
|
|
else
|
|
{ // dołączany jest odwrotnie ustawiany
|
|
Object->NextConnected = this; // on ma nas z tyłu
|
|
Object->NextConnectedNo = iDirection;
|
|
}
|
|
if (MoverParameters->TrainType & dt_EZT) // w przypadku łączenia członów,
|
|
// światła w rozrządczym zależą od
|
|
// stanu w silnikowym
|
|
if (MoverParameters->Couplers[iDirection].AllowedFlag &
|
|
ctrain_depot) // gdy sprzęgi łączone warsztatowo (powiedzmy)
|
|
if ((MoverParameters->Power < 1.0) &&
|
|
(Object->MoverParameters->Power > 1.0)) // my nie mamy mocy, ale ten drugi ma
|
|
iLights = Object->MoverParameters->iLights; // to w tym z mocą będą światła
|
|
// załączane, a w tym bez tylko widoczne
|
|
else if ((MoverParameters->Power > 1.0) &&
|
|
(Object->MoverParameters->Power < 1.0)) // my mamy moc, ale ten drugi nie ma
|
|
Object->iLights = MoverParameters->iLights; // to w tym z mocą będą światła
|
|
// załączane, a w tym bez tylko widoczne
|
|
return;
|
|
// SetPneumatic(1,1); //Ra: to i tak się nie wykonywało po return
|
|
// SetPneumatic(1,0);
|
|
// SetPneumatic(0,1);
|
|
// SetPneumatic(0,0);
|
|
}
|
|
|
|
bool TDynamicObject::UpdateForce(double dt, double dt1, bool FullVer)
|
|
{
|
|
if (!bEnabled)
|
|
return false;
|
|
if (dt > 0)
|
|
MoverParameters->ComputeTotalForce(dt, dt1,
|
|
FullVer); // wywalenie WS zależy od ustawienia kierunku
|
|
return true;
|
|
}
|
|
|
|
void TDynamicObject::LoadUpdate()
|
|
{ // przeładowanie modelu ładunku
|
|
// Ra: nie próbujemy wczytywać modeli miliony razy podczas renderowania!!!
|
|
if ((mdLoad == NULL) && (MoverParameters->Load > 0))
|
|
{
|
|
AnsiString asLoadName =
|
|
asBaseDir + MoverParameters->LoadType + ".t3d"; // zapamiętany katalog pojazdu
|
|
// asLoadName=MoverParameters->LoadType;
|
|
// if (MoverParameters->LoadType!=AnsiString("passengers"))
|
|
Global::asCurrentTexturePath = asBaseDir; // bieżąca ścieżka do tekstur to dynamic/...
|
|
mdLoad = TModelsManager::GetModel(asLoadName.c_str()); // nowy ładunek
|
|
Global::asCurrentTexturePath =
|
|
AnsiString(szTexturePath); // z powrotem defaultowa sciezka do tekstur
|
|
// Ra: w MMD można by zapisać położenie modelu ładunku (np. węgiel) w
|
|
// zależności od
|
|
// załadowania
|
|
}
|
|
else if (MoverParameters->Load == 0)
|
|
mdLoad = NULL; // nie ma ładunku
|
|
// if ((mdLoad==NULL)&&(MoverParameters->Load>0))
|
|
// {
|
|
// mdLoad=NULL; //Ra: to jest tu bez sensu - co autor miał na myśli?
|
|
// }
|
|
MoverParameters->LoadStatus &= 3; // po zakończeniu będzie równe zero
|
|
};
|
|
|
|
/*
|
|
double ComputeRadius(double p1x, double p1z, double p2x, double p2z,
|
|
double p3x, double p3z, double p4x, double p4z)
|
|
{
|
|
|
|
double v1z= p1x-p2x;
|
|
double v1x= p1z-p2z;
|
|
double v4z= p3x-p4x;
|
|
double v4x= p3z-p4z;
|
|
double A1= p2z-p1z;
|
|
double B1= p1x-p2x;
|
|
double C1= -p1z*B1-p1x*A1;
|
|
double A2= p4z-p3z;
|
|
double B2= p3x-p4x;
|
|
double C2= -p3z*B1-p3x*A1;
|
|
double y= (A1*C2/A2-C1)/(B1-A1*B2/A2);
|
|
double x= (-B2*y-C2)/A2;
|
|
}
|
|
*/
|
|
double TDynamicObject::ComputeRadius(vector3 p1, vector3 p2, vector3 p3, vector3 p4)
|
|
{
|
|
// vector3 v1
|
|
|
|
// TLine l1= TLine(p1,p1-p2);
|
|
// TLine l4= TLine(p4,p4-p3);
|
|
// TPlane p1= l1.GetPlane();
|
|
// vector3 pt;
|
|
// CrossPoint(pt,l4,p1);
|
|
double R = 0.0;
|
|
vector3 p12 = p1 - p2;
|
|
vector3 p34 = p3 - p4;
|
|
p12 = CrossProduct(p12, vector3(0.0, 0.1, 0.0));
|
|
p12 = Normalize(p12);
|
|
p34 = CrossProduct(p34, vector3(0.0, 0.1, 0.0));
|
|
p34 = Normalize(p34);
|
|
if (fabs(p1.x - p2.x) > 0.01)
|
|
{
|
|
if (fabs(p12.x - p34.x) > 0.001)
|
|
R = (p1.x - p4.x) / (p34.x - p12.x);
|
|
}
|
|
else
|
|
{
|
|
if (fabs(p12.z - p34.z) > 0.001)
|
|
R = (p1.z - p4.z) / (p34.z - p12.z);
|
|
}
|
|
return (R);
|
|
}
|
|
|
|
/*
|
|
double TDynamicObject::ComputeRadius()
|
|
{
|
|
double L=0;
|
|
double d=0;
|
|
d=sqrt(SquareMagnitude(Axle0.pPosition-Axle1.pPosition));
|
|
L=Axle1.GetLength(Axle1.pPosition,Axle1.pPosition-Axle2.pPosition,Axle0.pPosition-Axle3.pPosition,Axle0.pPosition);
|
|
|
|
double eps=0.01;
|
|
double R= 0;
|
|
double L_d;
|
|
if ((L>0) || (d>0))
|
|
{
|
|
L_d= L-d;
|
|
if (L_d>eps)
|
|
{
|
|
R=L*sqrt(L/(24*(L_d)));
|
|
}
|
|
}
|
|
return R;
|
|
}
|
|
*/
|
|
|
|
/* Ra: na razie nie potrzebne
|
|
void TDynamicObject::UpdatePos()
|
|
{
|
|
MoverParameters->Loc.X= -vPosition.x;
|
|
MoverParameters->Loc.Y= vPosition.z;
|
|
MoverParameters->Loc.Z= vPosition.y;
|
|
}
|
|
*/
|
|
|
|
/*
|
|
Ra:
|
|
Powinny być dwie funkcje wykonujące aktualizację fizyki. Jedna wykonująca
|
|
krok obliczeń, powtarzana odpowiednią liczbę razy, a druga wykonująca zbiorczą
|
|
aktualzację mniej istotnych elementów.
|
|
Ponadto należało by ustalić odległość składów od kamery i jeśli przekracza
|
|
ona np. 10km, to traktować składy jako uproszczone, np. bez wnikania w siły
|
|
na sprzęgach, opóźnienie działania hamulca itp. Oczywiście musi mieć to pewną
|
|
histerezę czasową, aby te tryby pracy nie przełączały się zbyt szybko.
|
|
*/
|
|
|
|
bool TDynamicObject::Update(double dt, double dt1)
|
|
{
|
|
if (dt == 0)
|
|
return true; // Ra: pauza
|
|
if (!MoverParameters->PhysicActivation &&
|
|
!MechInside) // to drugie, bo będąc w maszynowym blokuje się fizyka
|
|
return true; // McZapkie: wylaczanie fizyki gdy nie potrzeba
|
|
if (!MyTrack)
|
|
return false; // pojazdy postawione na torach portalowych mają MyTrack==NULL
|
|
if (!bEnabled)
|
|
return false; // a normalnie powinny mieć bEnabled==false
|
|
|
|
// Ra: przeniosłem - no już lepiej tu, niż w wyświetlaniu!
|
|
// if ((MoverParameters->ConverterFlag==false) &&
|
|
// (MoverParameters->TrainType!=dt_ET22))
|
|
// Ra: to nie może tu być, bo wyłącza sprężarkę w rozrządczym EZT!
|
|
// if
|
|
// ((MoverParameters->ConverterFlag==false)&&(MoverParameters->CompressorPower!=0))
|
|
// MoverParameters->CompressorFlag=false;
|
|
// if (MoverParameters->CompressorPower==2)
|
|
// MoverParameters->CompressorAllow=MoverParameters->ConverterFlag;
|
|
|
|
// McZapkie-260202
|
|
if ((MoverParameters->EnginePowerSource.SourceType == CurrentCollector) &&
|
|
(MoverParameters->Power > 1.0)) // aby rozrządczy nie opuszczał silnikowemu
|
|
if ((MechInside) || (MoverParameters->TrainType == dt_EZT))
|
|
{
|
|
// if
|
|
// ((!MoverParameters->PantCompFlag)&&(MoverParameters->CompressedVolume>=2.8))
|
|
// MoverParameters->PantVolume=MoverParameters->CompressedVolume;
|
|
if (MoverParameters->PantPress < (MoverParameters->TrainType == dt_EZT ? 2.4 : 3.5))
|
|
{ // 3.5 wg
|
|
// http://www.transportszynowy.pl/eu06-07pneumat.php
|
|
//"Wyłączniki ciśnieniowe odbieraków prądu wyłączają sterowanie
|
|
// wyłącznika szybkiego
|
|
// oraz uniemożliwiają podniesienie odbieraków prądu, gdy w instalacji
|
|
// rozrządu
|
|
// ciśnienie spadnie poniżej wartości 3,5 bara."
|
|
// Ra 2013-12: Niebugocław mówi, że w EZT podnoszą się przy 2.5
|
|
// if (!MoverParameters->PantCompFlag)
|
|
// MoverParameters->PantVolume=MoverParameters->CompressedVolume;
|
|
MoverParameters->PantFront(false); // opuszczenie pantografów przy niskim ciśnieniu
|
|
MoverParameters->PantRear(false); // to idzie w ukrotnieniu, a nie powinno...
|
|
}
|
|
// Winger - automatyczne wylaczanie malej sprezarki
|
|
else if (MoverParameters->PantPress >= 4.8)
|
|
MoverParameters->PantCompFlag = false;
|
|
} // Ra: do Mover to trzeba przenieść, żeby AI też mogło sobie podpompować
|
|
|
|
double dDOMoveLen;
|
|
|
|
TLocation l;
|
|
l.X = -vPosition.x; // przekazanie pozycji do fizyki
|
|
l.Y = vPosition.z;
|
|
l.Z = vPosition.y;
|
|
TRotation r;
|
|
r.Rx = r.Ry = r.Rz = 0;
|
|
// McZapkie: parametry powinny byc pobierane z toru
|
|
|
|
// TTrackShape ts;
|
|
// ts.R=MyTrack->fRadius;
|
|
// if (ABuGetDirection()<0) ts.R=-ts.R;
|
|
// ts.R=MyTrack->fRadius; //ujemne promienie są już zamienione przy
|
|
// wczytywaniu
|
|
if (Axle0.vAngles.z != Axle1.vAngles.z)
|
|
{ // wyliczenie promienia z obrotów
|
|
// osi - modyfikację zgłosił youBy
|
|
ts.R = Axle0.vAngles.z - Axle1.vAngles.z; // różnica może dawać stałą ±M_2PI
|
|
if (ts.R > M_PI)
|
|
ts.R -= M_2PI else if (ts.R < -M_PI) ts.R += M_2PI; // normalizacja
|
|
// ts.R=fabs(0.5*MoverParameters->BDist/sin(ts.R*0.5));
|
|
ts.R = -0.5 * MoverParameters->BDist / sin(ts.R * 0.5);
|
|
if ((ts.R > 15000.0) || (ts.R < -15000.0))
|
|
ts.R = 0.0; // szkoda czasu na zbyt duże promienie, 4km to promień nie
|
|
// wymagający
|
|
// przechyłki
|
|
}
|
|
else
|
|
ts.R = 0.0;
|
|
// ts.R=ComputeRadius(Axle1.pPosition,Axle2.pPosition,Axle3.pPosition,Axle0.pPosition);
|
|
// Ra: składową pochylenia wzdłużnego mamy policzoną w jednostkowym wektorze
|
|
// vFront
|
|
ts.Len = 1.0; // Max0R(MoverParameters->BDist,MoverParameters->ADist);
|
|
ts.dHtrack = -vFront.y; // Axle1.pPosition.y-Axle0.pPosition.y; //wektor
|
|
// między skrajnymi osiami
|
|
// (!!!odwrotny)
|
|
ts.dHrail = (Axle1.GetRoll() + Axle0.GetRoll()) * 0.5; //średnia przechyłka pudła
|
|
// TTrackParam tp;
|
|
tp.Width = MyTrack->fTrackWidth;
|
|
// McZapkie-250202
|
|
tp.friction = MyTrack->fFriction * Global::fFriction;
|
|
tp.CategoryFlag = MyTrack->iCategoryFlag & 15;
|
|
tp.DamageFlag = MyTrack->iDamageFlag;
|
|
tp.QualityFlag = MyTrack->iQualityFlag;
|
|
if ((MoverParameters->Couplers[0].CouplingFlag > 0) &&
|
|
(MoverParameters->Couplers[1].CouplingFlag > 0))
|
|
{
|
|
MoverParameters->InsideConsist = true;
|
|
}
|
|
else
|
|
{
|
|
MoverParameters->InsideConsist = false;
|
|
}
|
|
// napiecie sieci trakcyjnej
|
|
// Ra 15-01: przeliczenie poboru prądu powinno być robione wcześniej, żeby na
|
|
// tym etapie były
|
|
// znane napięcia
|
|
// TTractionParam tmpTraction;
|
|
// tmpTraction.TractionVoltage=0;
|
|
if (MoverParameters->EnginePowerSource.SourceType == CurrentCollector)
|
|
{ // dla EZT tylko silnikowy
|
|
// if (Global::bLiveTraction)
|
|
{ // Ra 2013-12: to niżej jest chyba trochę bez sensu
|
|
double v = MoverParameters->PantRearVolt;
|
|
if (v == 0.0)
|
|
{
|
|
v = MoverParameters->PantFrontVolt;
|
|
if (v == 0.0)
|
|
if ((MoverParameters->TrainType & (dt_EZT | dt_ET40 | dt_ET41 | dt_ET42)) &&
|
|
MoverParameters->EngineType !=
|
|
ElectricInductionMotor) // dwuczłony mogą mieć sprzęg WN
|
|
v = MoverParameters->GetTrainsetVoltage(); // ostatnia szansa
|
|
}
|
|
if (v != 0.0)
|
|
{ // jeśli jest zasilanie
|
|
NoVoltTime = 0;
|
|
tmpTraction.TractionVoltage = v;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
if (MoverParameters->Vel>0.1f) //jeśli jedzie
|
|
if (NoVoltTime==0.0) //tylko przy pierwszym zaniku napięcia
|
|
if (MoverParameters->PantFrontUp||MoverParameters->PantRearUp)
|
|
//if
|
|
((pants[0].fParamPants->PantTraction>1.0)||(pants[1].fParamPants->PantTraction>1.0))
|
|
{//wspomagacz usuwania problemów z siecią
|
|
if (!Global::iPause)
|
|
{//Ra: tymczasowa teleportacja do miejsca, gdzie brakuje prądu
|
|
Global::SetCameraPosition(vPosition+vector3(0,0,5)); //nowa
|
|
pozycja dla
|
|
generowania obiektów
|
|
Global::pCamera->Init(vPosition+vector3(0,0,5),Global::pFreeCameraInitAngle[0]);
|
|
//przestawienie
|
|
}
|
|
Global:l::pGround->Silence(Global::pCamera->Pos); //wyciszenie
|
|
wszystkiego
|
|
z poprzedniej pozycji
|
|
Globa:iPause|=1; //tymczasowe zapauzowanie, gdy problem z
|
|
siecią
|
|
}
|
|
*/
|
|
NoVoltTime = NoVoltTime + dt;
|
|
if (NoVoltTime > 0.2) // jeśli brak zasilania dłużej niż 0.2 sekundy (25km/h pod
|
|
// izolatorem daje 0.15s)
|
|
{ // Ra 2F1H: prowizorka, trzeba przechować napięcie, żeby nie wywalało
|
|
// WS pod
|
|
// izolatorem
|
|
if (MoverParameters->Vel > 0.5) // jeśli jedzie
|
|
if (MoverParameters->PantFrontUp ||
|
|
MoverParameters->PantRearUp) // Ra 2014-07: doraźna blokada logowania
|
|
// zimnych lokomotyw - zrobić to trzeba
|
|
// inaczej
|
|
// if (NoVoltTime>0.02) //tu można ograniczyć czas rozłączenia
|
|
// if (DebugModeFlag) //logowanie nie zawsze
|
|
if (MoverParameters->Mains)
|
|
{ // Ra 15-01: logować tylko, jeśli WS załączony
|
|
// if (MoverParameters->PantFrontUp&&pants)
|
|
// Ra 15-01: bezwzględne współrzędne pantografu nie są dostępne,
|
|
// więc lepiej się tego nie zaloguje
|
|
ErrorLog("Voltage loss: by " + MoverParameters->Name + " at " +
|
|
FloatToStrF(vPosition.x, ffFixed, 7, 2) + " " +
|
|
FloatToStrF(vPosition.y, ffFixed, 7, 2) + " " +
|
|
FloatToStrF(vPosition.z, ffFixed, 7, 2) + ", time " +
|
|
FloatToStrF(NoVoltTime, ffFixed, 7, 2));
|
|
// if (MoverParameters->PantRearUp)
|
|
// if (iAnimType[ANIM_PANTS]>1)
|
|
// if (pants[1])
|
|
// ErrorLog("Voltage loss: by "+MoverParameters->Name+" at
|
|
// "+FloatToStrF(vPosition.x,ffFixed,7,2)+"
|
|
// "+FloatToStrF(vPosition.y,ffFixed,7,2)+"
|
|
// "+FloatToStrF(vPosition.z,ffFixed,7,2)+", time
|
|
// "+FloatToStrF(NoVoltTime,ffFixed,7,2));
|
|
}
|
|
// Ra 2F1H: nie było sensu wpisywać tu zera po upływie czasu, bo
|
|
// zmienna była
|
|
// tymczasowa, a napięcie zerowane od razu
|
|
tmpTraction.TractionVoltage = 0; // Ra 2013-12: po co tak?
|
|
// pControlled->MainSwitch(false); //może tak?
|
|
}
|
|
}
|
|
}
|
|
// else //Ra: nie no, trzeba podnieść pantografy, jak nie będzie drutu, to
|
|
// będą miały prąd
|
|
// po osiągnięciu 1.4m
|
|
// tmpTraction.TractionVoltage=0.95*MoverParameters->EnginePowerSource.MaxVoltage;
|
|
}
|
|
else
|
|
tmpTraction.TractionVoltage = 0.95 * MoverParameters->EnginePowerSource.MaxVoltage;
|
|
tmpTraction.TractionFreq = 0;
|
|
tmpTraction.TractionMaxCurrent = 7500; // Ra: chyba za dużo? powinno wywalać przy 1500
|
|
tmpTraction.TractionResistivity = 0.3;
|
|
|
|
// McZapkie: predkosc w torze przekazac do TrackParam
|
|
// McZapkie: Vel ma wymiar [km/h] (absolutny), V ma wymiar [m/s], taka
|
|
// przyjalem notacje
|
|
tp.Velmax = MyTrack->VelocityGet();
|
|
|
|
if (Mechanik)
|
|
{ // Ra 2F3F: do Driver.cpp to przenieść?
|
|
MoverParameters->EqvtPipePress = GetEPP(); // srednie cisnienie w PG
|
|
if ((Mechanik->Primary()) &&
|
|
(MoverParameters->EngineType == ElectricInductionMotor)) // jesli glowny i z
|
|
// asynchronami, to
|
|
// niech steruje
|
|
{ // hamulcem lacznie dla calego pociagu/ezt
|
|
bool kier = (DirectionGet() * MoverParameters->ActiveCab > 0);
|
|
float FED = 0;
|
|
float np = 0;
|
|
float masa = 0;
|
|
float FrED = 0;
|
|
float masamax = 0;
|
|
float FmaxPN = 0;
|
|
float FfulED = 0;
|
|
float FmaxED = 0;
|
|
float Fzad = 0;
|
|
float FzadED = 0;
|
|
float FzadPN = 0;
|
|
float Frj = 0;
|
|
float amax = 0;
|
|
float osie = 0;
|
|
// 1. ustal wymagana sile hamowania calego pociagu
|
|
// - opoznienie moze byc ustalane na podstawie charakterystyki
|
|
// - opoznienie moze byc ustalane na podstawie mas i cisnien granicznych
|
|
//
|
|
|
|
// 2. ustal mozliwa do realizacji sile hamowania ED
|
|
// - w szczegolnosci powinien brac pod uwage rozne sily hamowania
|
|
for (TDynamicObject *p = GetFirstDynamic(MoverParameters->ActiveCab < 0 ? 1 : 0); p;
|
|
(kier ? p = p->NextC(4) : p = p->PrevC(4)))
|
|
{
|
|
np++;
|
|
masamax += p->MoverParameters->MBPM +
|
|
(p->MoverParameters->MBPM > 1 ? 0 : p->MoverParameters->Mass) +
|
|
p->MoverParameters->Mred;
|
|
float Nmax = ((p->MoverParameters->P2FTrans * p->MoverParameters->MaxBrakePress[0] -
|
|
p->MoverParameters->BrakeCylSpring) *
|
|
p->MoverParameters->BrakeCylMult[0] -
|
|
p->MoverParameters->BrakeSlckAdj) *
|
|
p->MoverParameters->BrakeCylNo * p->MoverParameters->BrakeRigEff;
|
|
FmaxPN += Nmax * p->MoverParameters->Hamulec->GetFC(
|
|
Nmax / (p->MoverParameters->NAxles * p->MoverParameters->NBpA),
|
|
p->MoverParameters->Vmax) *
|
|
1000; // sila hamowania pn
|
|
FmaxED += ((p->MoverParameters->Mains) && (p->MoverParameters->ActiveDir != 0) &&
|
|
(p->MoverParameters->eimc[eimc_p_Fh] * p->MoverParameters->NPoweredAxles >
|
|
0) ?
|
|
p->MoverParameters->eimc[eimc_p_Fh] * 1000 :
|
|
0); // chwilowy max ED -> do rozdzialu sil
|
|
FED -= Min0R(p->MoverParameters->eimv[eimv_Fmax], 0) *
|
|
1000; // chwilowy max ED -> do rozdzialu sil
|
|
FfulED = Min0R(p->MoverParameters->eimv[eimv_Fful], 0) *
|
|
1000; // chwilowy max ED -> do rozdzialu sil
|
|
FrED -= Min0R(p->MoverParameters->eimv[eimv_Fr], 0) *
|
|
1000; // chwilowo realizowane ED -> do pneumatyki
|
|
Frj += Max0R(p->MoverParameters->eimv[eimv_Fr], 0) *
|
|
1000;// chwilowo realizowany napęd -> do utrzymującego
|
|
masa += p->MoverParameters->TotalMass;
|
|
osie += p->MoverParameters->NAxles;
|
|
}
|
|
amax = FmaxPN / masamax;
|
|
if ((MoverParameters->Vel < 0.5) && (MoverParameters->BrakePress > 0.2) ||
|
|
(dDoorMoveL > 0.001) || (dDoorMoveR > 0.001))
|
|
{
|
|
MoverParameters->ShuntMode = true;
|
|
}
|
|
if (MoverParameters->ShuntMode)
|
|
{
|
|
MoverParameters->ShuntModeAllow = (dDoorMoveL < 0.001) && (dDoorMoveR < 0.001) &&
|
|
(MoverParameters->LocalBrakeRatio() < 0.01);
|
|
}
|
|
if ((MoverParameters->Vel > 1) && (dDoorMoveL < 0.001) && (dDoorMoveR < 0.001))
|
|
{
|
|
MoverParameters->ShuntMode = false;
|
|
MoverParameters->ShuntModeAllow = (MoverParameters->BrakePress > 0.2) &&
|
|
(MoverParameters->LocalBrakeRatio() < 0.01);
|
|
}
|
|
Fzad = amax * MoverParameters->LocalBrakeRatio() * masa;
|
|
if ((MoverParameters->ScndS) &&
|
|
(MoverParameters->Vel > MoverParameters->eimc[eimc_p_Vh1]) && (FmaxED > 0))
|
|
{
|
|
Fzad = Min0R(MoverParameters->LocalBrakeRatio() * FmaxED, FfulED);
|
|
}
|
|
if (((MoverParameters->ShuntMode) && (Frj < 0.0015 * masa)) ||
|
|
(MoverParameters->V * MoverParameters->DirAbsolute < -0.2))
|
|
{
|
|
Fzad = Max0R(0.5 * masa, Fzad);
|
|
}
|
|
FzadED = Min0R(Fzad, FmaxED);
|
|
FzadPN = Fzad - FrED;
|
|
np = 0;
|
|
bool* PrzekrF = new bool[np];
|
|
float nPrzekrF = 0;
|
|
bool test = true;
|
|
float* FzED = new float[np];
|
|
float* FzEP = new float[np];
|
|
float* FmaxEP = new float[np];
|
|
// 3. ustaw pojazdom sile hamowania ED
|
|
// - proporcjonalnie do mozliwosci
|
|
|
|
// 4. ustal potrzebne dohamowanie pneumatyczne
|
|
// - od sily zadanej trzeba odjac realizowana przez ED
|
|
// 5. w razie potrzeby wlacz hamulec utrzymujacy
|
|
// - gdy zahamowany ma ponizej 2 km/h
|
|
// 6. ustaw pojazdom sile hamowania ep
|
|
// - proporcjonalnie do masy, do liczby osi, rowne cisnienia - jak
|
|
// bedzie, tak bedzie dobrze
|
|
float Fpoj = 0; // MoverParameters->ActiveCab < 0
|
|
////ALGORYTM 2 - KAZDEMU PO ROWNO, ale nie wiecej niz eped * masa
|
|
// 1. najpierw daj kazdemu tyle samo
|
|
int i = 0;
|
|
for (TDynamicObject *p = GetFirstDynamic(MoverParameters->ActiveCab < 0 ? 1 : 0); p;
|
|
(kier > 0 ? p = p->NextC(4) : p = p->PrevC(4)))
|
|
{
|
|
np++;
|
|
float Nmax = ((p->MoverParameters->P2FTrans * p->MoverParameters->MaxBrakePress[0] -
|
|
p->MoverParameters->BrakeCylSpring) *
|
|
p->MoverParameters->BrakeCylMult[0] -
|
|
p->MoverParameters->BrakeSlckAdj) *
|
|
p->MoverParameters->BrakeCylNo * p->MoverParameters->BrakeRigEff;
|
|
FmaxEP[i] = Nmax *
|
|
p->MoverParameters->Hamulec->GetFC(
|
|
Nmax / (p->MoverParameters->NAxles * p->MoverParameters->NBpA),
|
|
p->MoverParameters->Vmax) *
|
|
1000; // sila hamowania pn
|
|
|
|
PrzekrF[i] = false;
|
|
FzED[i] = (FmaxED > 0 ? FzadED / FmaxED : 0);
|
|
p->MoverParameters->AnPos =
|
|
(MoverParameters->ScndS ? MoverParameters->LocalBrakeRatio() : FzED[i]);
|
|
FzEP[i] = FzadPN * p->MoverParameters->NAxles / osie;
|
|
i++;
|
|
p->MoverParameters->ShuntMode = MoverParameters->ShuntMode;
|
|
p->MoverParameters->ShuntModeAllow = MoverParameters->ShuntModeAllow;
|
|
}
|
|
while (test)
|
|
{
|
|
test = false;
|
|
i = 0;
|
|
float przek = 0;
|
|
for (TDynamicObject *p = GetFirstDynamic(MoverParameters->ActiveCab < 0 ? 1 : 0); p;
|
|
(kier > 0 ? p = p->NextC(4) : p = p->PrevC(4)))
|
|
{
|
|
if ((FzEP[i] > 0.01) &&
|
|
(FzEP[i] >
|
|
p->MoverParameters->TotalMass * p->MoverParameters->eimc[eimc_p_eped] +
|
|
Min0R(p->MoverParameters->eimv[eimv_Fr], 0) * 1000) &&
|
|
(!PrzekrF[i]))
|
|
{
|
|
float przek1 = -Min0R(p->MoverParameters->eimv[eimv_Fr], 0) * 1000 +
|
|
FzEP[i] -
|
|
p->MoverParameters->TotalMass *
|
|
p->MoverParameters->eimc[eimc_p_eped] * 0.999;
|
|
PrzekrF[i] = true;
|
|
test = true;
|
|
nPrzekrF++;
|
|
przek1 = Min0R(przek1, FzEP[i]);
|
|
FzEP[i] -= przek1;
|
|
if (FzEP[i] < 0)
|
|
FzEP[i] = 0;
|
|
przek += przek1;
|
|
}
|
|
i++;
|
|
}
|
|
i = 0;
|
|
przek = przek / (np - nPrzekrF);
|
|
for (TDynamicObject *p = GetFirstDynamic(MoverParameters->ActiveCab < 0 ? 1 : 0); p;
|
|
(kier > 0 ? p = p->NextC(4) : p = p->PrevC(4)))
|
|
{
|
|
if (!PrzekrF[i])
|
|
{
|
|
FzEP[i] += przek;
|
|
}
|
|
i++;
|
|
}
|
|
}
|
|
i = 0;
|
|
for (TDynamicObject *p = GetFirstDynamic(MoverParameters->ActiveCab < 0 ? 1 : 0); p;
|
|
(kier > 0 ? p = p->NextC(4) : p = p->PrevC(4)))
|
|
{
|
|
float Nmax = ((p->MoverParameters->P2FTrans * p->MoverParameters->MaxBrakePress[0] -
|
|
p->MoverParameters->BrakeCylSpring) *
|
|
p->MoverParameters->BrakeCylMult[0] -
|
|
p->MoverParameters->BrakeSlckAdj) *
|
|
p->MoverParameters->BrakeCylNo * p->MoverParameters->BrakeRigEff;
|
|
float FmaxPoj = Nmax *
|
|
p->MoverParameters->Hamulec->GetFC(
|
|
Nmax / (p->MoverParameters->NAxles * p->MoverParameters->NBpA),
|
|
p->MoverParameters->Vel) *
|
|
1000; // sila hamowania pn
|
|
p->MoverParameters->LocalBrakePosA = (p->MoverParameters->SlippingWheels ? 0 : FzEP[i] / FmaxPoj);
|
|
if (p->MoverParameters->LocalBrakePosA>0.009)
|
|
if (p->MoverParameters->P2FTrans * p->MoverParameters->BrakeCylMult[0] *
|
|
p->MoverParameters->MaxBrakePress[0] != 0)
|
|
{
|
|
float x = (p->MoverParameters->BrakeSlckAdj / p->MoverParameters->BrakeCylMult[0] +
|
|
p->MoverParameters->BrakeCylSpring) / (p->MoverParameters->P2FTrans *
|
|
p->MoverParameters->MaxBrakePress[0]);
|
|
p->MoverParameters->LocalBrakePosA = x + (1 - x) * p->MoverParameters->LocalBrakePosA;
|
|
}
|
|
else
|
|
p->MoverParameters->LocalBrakePosA = p->MoverParameters->LocalBrakePosA;
|
|
else
|
|
p->MoverParameters->LocalBrakePosA = 0;
|
|
i++;
|
|
}
|
|
/* ////ALGORYTM 1 - KAZDEMU PO ROWNO
|
|
for (TDynamicObject *p = GetFirstDynamic(MoverParameters->ActiveCab < 0 ? 1 : 0); p;
|
|
(iDirection > 0 ? p = p->NextC(4) : p = p->PrevC(4)))
|
|
{
|
|
|
|
float Nmax = ((p->MoverParameters->P2FTrans * p->MoverParameters->MaxBrakePress[0] -
|
|
p->MoverParameters->BrakeCylSpring) *
|
|
p->MoverParameters->BrakeCylMult[0] -
|
|
p->MoverParameters->BrakeSlckAdj) *
|
|
p->MoverParameters->BrakeCylNo * p->MoverParameters->BrakeRigEff;
|
|
float FmaxPoj = Nmax *
|
|
p->MoverParameters->Hamulec->GetFC(
|
|
Nmax / (p->MoverParameters->NAxles * p->MoverParameters->NBpA),
|
|
p->MoverParameters->Vel) *
|
|
1000; // sila hamowania pn
|
|
// Fpoj=(FED>0?-FzadED*p->MoverParameters->eimv[eimv_Fmax]*1000/FED:0);
|
|
// p->MoverParameters->AnPos=(p->MoverParameters->eimc[eimc_p_Fh]>1?0.001f*Fpoj/(p->MoverParameters->eimc[eimc_p_Fh]):0);
|
|
p->MoverParameters->AnPos = (FmaxED > 0 ? FzadED / FmaxED : 0);
|
|
// Fpoj = FzadPN * Min0R(p->MoverParameters->TotalMass / masa, 1);
|
|
// p->MoverParameters->LocalBrakePosA =
|
|
// (p->MoverParameters->SlippingWheels ? 0 : Min0R(Max0R(Fpoj / FmaxPoj, 0), 1));
|
|
p->MoverParameters->LocalBrakePosA = (p->MoverParameters->SlippingWheels ? 0 : FzadPN / FmaxPN);
|
|
} */
|
|
|
|
MED[0][0] = masa*0.001;
|
|
MED[0][1] = amax;
|
|
MED[0][2] = Fzad*0.001;
|
|
MED[0][3] = FmaxPN*0.001;
|
|
MED[0][4] = FmaxED*0.001;
|
|
MED[0][5] = FrED*0.001;
|
|
MED[0][6] = FzadPN*0.001;
|
|
MED[0][7] = nPrzekrF;
|
|
|
|
delete[] PrzekrF;
|
|
delete[] FzED;
|
|
delete[] FzEP;
|
|
}
|
|
|
|
// yB: cos (AI) tu jest nie kompatybilne z czyms (hamulce)
|
|
// if (Controller!=Humandriver)
|
|
// if (Mechanik->LastReactionTime>0.5)
|
|
// {
|
|
// MoverParameters->BrakeCtrlPos=0;
|
|
// Mechanik->LastReactionTime=0;
|
|
// }
|
|
|
|
Mechanik->UpdateSituation(dt1); // przebłyski świadomości AI
|
|
}
|
|
|
|
// fragment "z EXE Kursa"
|
|
if (MoverParameters->Mains) // nie wchodzić w funkcję bez potrzeby
|
|
if ((!MoverParameters->Battery) && (Controller == Humandriver) &&
|
|
(MoverParameters->EngineType != DieselEngine) &&
|
|
(MoverParameters->EngineType != WheelsDriven))
|
|
{ // jeśli bateria wyłączona, a nie diesel ani drezyna
|
|
// reczna
|
|
if (MoverParameters->MainSwitch(false)) // wyłączyć zasilanie
|
|
MoverParameters->EventFlag = true;
|
|
}
|
|
if (MoverParameters->TrainType == dt_ET42)
|
|
{ // powinny być wszystkie dwuczłony oraz EZT
|
|
/*
|
|
//Ra: to jest bez sensu, bo wyłącza WS przy przechodzeniu przez
|
|
"wewnętrzne" kabiny (z
|
|
powodu ActiveCab)
|
|
//trzeba to zrobić inaczej, np. dla członu A sprawdzać, czy jest B
|
|
//albo sprawdzać w momencie załączania WS i zmiany w sprzęgach
|
|
if
|
|
(((TestFlag(MoverParameters->Couplers[1].CouplingFlag,ctrain_controll))&&(MoverParameters->ActiveCab>0)&&(NextConnected->MoverParameters->TrainType!=dt_ET42))||((TestFlag(MoverParameters->Couplers[0].CouplingFlag,ctrain_controll))&&(MoverParameters->ActiveCab<0)&&(PrevConnected->MoverParameters->TrainType!=dt_ET42)))
|
|
{//sprawdzenie, czy z tyłu kabiny mamy drugi człon
|
|
if (MoverParameters->MainSwitch(false))
|
|
MoverParameters->EventFlag=true;
|
|
}
|
|
if
|
|
((!(TestFlag(MoverParameters->Couplers[1].CouplingFlag,ctrain_controll))&&(MoverParameters->ActiveCab>0))||(!(TestFlag(MoverParameters->Couplers[0].CouplingFlag,ctrain_controll))&&(MoverParameters->ActiveCab<0)))
|
|
{
|
|
if (MoverParameters->MainSwitch(false))
|
|
MoverParameters->EventFlag=true;
|
|
}
|
|
*/
|
|
}
|
|
|
|
// McZapkie-260202 - dMoveLen przyda sie przy stukocie kol
|
|
dDOMoveLen =
|
|
GetdMoveLen() + MoverParameters->ComputeMovement(dt, dt1, ts, tp, tmpTraction, l, r);
|
|
// yB: zeby zawsze wrzucalo w jedna strone zakretu
|
|
MoverParameters->AccN *= -ABuGetDirection();
|
|
// if (dDOMoveLen!=0.0) //Ra: nie może być, bo blokuje Event0
|
|
Move(dDOMoveLen);
|
|
if (!bEnabled) // usuwane pojazdy nie mają toru
|
|
{ // pojazd do usunięcia
|
|
Global::pGround->bDynamicRemove = true; // sprawdzić
|
|
return false;
|
|
}
|
|
Global::ABuDebug = dDOMoveLen / dt1;
|
|
ResetdMoveLen();
|
|
// McZapkie-260202
|
|
// tupot mew, tfu, stukot kol:
|
|
DWORD stat;
|
|
// taka prowizorka zeby sciszyc stukot dalekiej lokomotywy
|
|
double ObjectDist;
|
|
double vol = 0;
|
|
// double freq; //Ra: nie używane
|
|
ObjectDist = SquareMagnitude(Global::pCameraPosition - vPosition);
|
|
// McZapkie-270202
|
|
if (MyTrack->fSoundDistance != -1)
|
|
{
|
|
if (ObjectDist < rsStukot[0].dSoundAtt * rsStukot[0].dSoundAtt * 15.0)
|
|
{
|
|
vol = (20.0 + MyTrack->iDamageFlag) / 21;
|
|
if (MyTrack->eEnvironment == e_tunnel)
|
|
{
|
|
vol *= 1.1;
|
|
// freq=1.02;
|
|
}
|
|
else if (MyTrack->eEnvironment == e_bridge)
|
|
{
|
|
vol *= 1.2;
|
|
// freq=0.99; //MC: stukot w zaleznosci od
|
|
// tego gdzie
|
|
// jest tor
|
|
}
|
|
if (MyTrack->fSoundDistance != dRailLength)
|
|
{
|
|
dRailLength = MyTrack->fSoundDistance;
|
|
for (int i = 0; i < iAxles; i++)
|
|
{
|
|
dRailPosition[i] = dWheelsPosition[i] + MoverParameters->Dim.L;
|
|
}
|
|
}
|
|
if (dRailLength != -1)
|
|
{
|
|
if (abs(MoverParameters->V) > 0)
|
|
{
|
|
for (int i = 0; i < iAxles; i++)
|
|
{
|
|
dRailPosition[i] -= dDOMoveLen * Sign(dDOMoveLen);
|
|
if (dRailPosition[i] < 0)
|
|
{
|
|
// McZapkie-040302
|
|
if (i == iAxles - 1)
|
|
{
|
|
rsStukot[0].Stop();
|
|
MoverParameters->AccV +=
|
|
0.5 * GetVelocity() / (1 + MoverParameters->Vmax);
|
|
}
|
|
else
|
|
{
|
|
rsStukot[i + 1].Stop();
|
|
}
|
|
rsStukot[i].Play(vol, 0, MechInside,
|
|
vPosition); // poprawic pozycje o uklad osi
|
|
if (i == 1)
|
|
MoverParameters->AccV -=
|
|
0.5 * GetVelocity() / (1 + MoverParameters->Vmax);
|
|
dRailPosition[i] += dRailLength;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// McZapkie-260202 end
|
|
|
|
// yB: przyspieszacz (moze zadziala, ale dzwiek juz jest)
|
|
int flag = MoverParameters->Hamulec->GetSoundFlag();
|
|
if ((bBrakeAcc) && (TestFlag(flag, sf_Acc)) && (ObjectDist < 2500))
|
|
{
|
|
sBrakeAcc->SetVolume(-ObjectDist * 3 - (FreeFlyModeFlag ? 0 : 2000));
|
|
sBrakeAcc->Play(0, 0, 0);
|
|
sBrakeAcc->SetPan(10000 * sin(ModCamRot));
|
|
}
|
|
if ((rsUnbrake.AM != 0) && (ObjectDist < 5000))
|
|
{
|
|
if ((TestFlag(flag, sf_CylU)) &&
|
|
((MoverParameters->BrakePress * MoverParameters->MaxBrakePress[3]) > 0.05))
|
|
{
|
|
vol = Min0R(
|
|
0.2 +
|
|
1.6 * sqrt((MoverParameters->BrakePress > 0 ? MoverParameters->BrakePress : 0) /
|
|
MoverParameters->MaxBrakePress[3]),
|
|
1);
|
|
vol = vol + (FreeFlyModeFlag ? 0 : -0.5) - ObjectDist / 5000;
|
|
rsUnbrake.SetPan(10000 * sin(ModCamRot));
|
|
rsUnbrake.Play(vol, DSBPLAY_LOOPING, MechInside, GetPosition());
|
|
}
|
|
else
|
|
rsUnbrake.Stop();
|
|
}
|
|
|
|
// fragment z EXE Kursa
|
|
/* if (MoverParameters->TrainType==dt_ET42)
|
|
{
|
|
if ((MoverParameters->DynamicBrakeType=dbrake_switch) &&
|
|
((MoverParameters->BrakePress >
|
|
0.2) || ( MoverParameters->PipePress < 0.36 )))
|
|
{
|
|
MoverParameters->StLinFlag=true;
|
|
}
|
|
else
|
|
if ((MoverParameters->DynamicBrakeType=dbrake_switch) &&
|
|
(MoverParameters->BrakePress <
|
|
0.1))
|
|
{
|
|
MoverParameters->StLinFlag=false;
|
|
|
|
}
|
|
} */
|
|
if ((MoverParameters->TrainType == dt_ET40) || (MoverParameters->TrainType == dt_EP05))
|
|
{ // dla ET40 i EU05 automatyczne cofanie nastawnika - i tak
|
|
// nie będzie to działać dobrze...
|
|
/* if
|
|
((MoverParameters->MainCtrlPos>MoverParameters->MainCtrlActualPos)&&(abs(MoverParameters->Im)>MoverParameters->IminHi))
|
|
{
|
|
MoverParameters->DecMainCtrl(1);
|
|
} */
|
|
if ((!Console::Pressed(Global::Keys[k_IncMainCtrl])) &&
|
|
(MoverParameters->MainCtrlPos > MoverParameters->MainCtrlActualPos))
|
|
{
|
|
MoverParameters->DecMainCtrl(1);
|
|
}
|
|
if ((!Console::Pressed(Global::Keys[k_DecMainCtrl])) &&
|
|
(MoverParameters->MainCtrlPos < MoverParameters->MainCtrlActualPos))
|
|
{
|
|
MoverParameters->IncMainCtrl(1); // Ra 15-01: a to nie miało być tylko cofanie?
|
|
}
|
|
}
|
|
|
|
if (MoverParameters->Vel != 0)
|
|
{ // McZapkie-050402: krecenie kolami:
|
|
dWheelAngle[0] += 114.59155902616464175359630962821 * MoverParameters->V * dt1 /
|
|
MoverParameters->WheelDiameterL; // przednie toczne
|
|
dWheelAngle[1] += MoverParameters->nrot * dt1 * 360.0; // napędne
|
|
dWheelAngle[2] += 114.59155902616464175359630962821 * MoverParameters->V * dt1 /
|
|
MoverParameters->WheelDiameterT; // tylne toczne
|
|
if (dWheelAngle[0] > 360.0)
|
|
dWheelAngle[0] -= 360.0; // a w drugą stronę jak się kręcą?
|
|
if (dWheelAngle[1] > 360.0)
|
|
dWheelAngle[1] -= 360.0;
|
|
if (dWheelAngle[2] > 360.0)
|
|
dWheelAngle[2] -= 360.0;
|
|
}
|
|
if (pants) // pantograf może być w wagonie kuchennym albo pojeździe rewizyjnym
|
|
// (np. SR61)
|
|
{ // przeliczanie kątów dla pantografów
|
|
double k; // tymczasowy kąt
|
|
double PantDiff;
|
|
TAnimPant *p; // wskaźnik do obiektu danych pantografu
|
|
double fCurrent = (MoverParameters->DynamicBrakeFlag && MoverParameters->ResistorsFlag ?
|
|
0 :
|
|
MoverParameters->Itot) +
|
|
MoverParameters->TotalCurrent; // prąd pobierany przez pojazd - bez
|
|
// sensu z tym (TotalCurrent)
|
|
// TotalCurrent to bedzie prad nietrakcyjny (niezwiazany z napedem)
|
|
// fCurrent+=fabs(MoverParameters->Voltage)*1e-6; //prąd płynący przez
|
|
// woltomierz,
|
|
// rozładowuje kondensator orgromowy 4µF
|
|
double fPantCurrent = fCurrent; // normalnie cały prąd przez jeden pantograf
|
|
if (pants)
|
|
if (iAnimType[ANIM_PANTS] > 1) // a jeśli są dwa pantografy //Ra 1014-11:
|
|
// proteza, trzeba zrobić sensowniej
|
|
if (pants[0].fParamPants->hvPowerWire &&
|
|
pants[1].fParamPants->hvPowerWire) // i oba podłączone do drutów
|
|
fPantCurrent = fCurrent * 0.5; // to dzielimy prąd równo na oba (trochę bez
|
|
// sensu, ale lepiej tak niż podwoić prąd)
|
|
for (int i = 0; i < iAnimType[ANIM_PANTS]; ++i)
|
|
{ // pętla po wszystkich pantografach
|
|
p = pants[i].fParamPants;
|
|
if (p->PantWys < 0)
|
|
{ // patograf został połamany, liczony nie będzie
|
|
if (p->fAngleL > p->fAngleL0)
|
|
p->fAngleL -= 0.2 * dt1; // nieco szybciej niż jak dla opuszczania
|
|
if (p->fAngleL < p->fAngleL0)
|
|
p->fAngleL = p->fAngleL0; // kąt graniczny
|
|
if (p->fAngleU < M_PI)
|
|
p->fAngleU += 0.5 * dt1; // górne się musi ruszać szybciej.
|
|
if (p->fAngleU > M_PI)
|
|
p->fAngleU = M_PI;
|
|
if (i & 1) // zgłoszono, że po połamaniu potrafi zostać zasilanie
|
|
MoverParameters->PantRearVolt = 0.0;
|
|
else
|
|
MoverParameters->PantFrontVolt = 0.0;
|
|
continue; // reszta wtedy nie jest wykonywana
|
|
}
|
|
PantDiff = p->PantTraction - p->PantWys; // docelowy-aktualny
|
|
switch (i) // numer pantografu
|
|
{ // trzeba usunąć to rozróżnienie
|
|
case 0:
|
|
if (Global::bLiveTraction ? false :
|
|
!p->hvPowerWire) // jeśli nie ma drutu, może pooszukiwać
|
|
MoverParameters->PantFrontVolt =
|
|
(p->PantWys >= 1.2) ? 0.95 * MoverParameters->EnginePowerSource.MaxVoltage :
|
|
0.0;
|
|
else if (MoverParameters->PantFrontUp ? (PantDiff < 0.01) :
|
|
false) // tolerancja niedolegania
|
|
{
|
|
if ((MoverParameters->PantFrontVolt == 0.0) &&
|
|
(MoverParameters->PantRearVolt == 0.0))
|
|
sPantUp.Play(vol, 0, MechInside, vPosition);
|
|
if (p->hvPowerWire) // TODO: wyliczyć trzeba prąd przypadający na
|
|
// pantograf i
|
|
// wstawić do GetVoltage()
|
|
{
|
|
MoverParameters->PantFrontVolt =
|
|
p->hvPowerWire->VoltageGet(MoverParameters->Voltage, fPantCurrent);
|
|
fCurrent -= fPantCurrent; // taki prąd płynie przez powyższy pantograf
|
|
}
|
|
else
|
|
MoverParameters->PantFrontVolt = 0.0;
|
|
}
|
|
else
|
|
MoverParameters->PantFrontVolt = 0.0;
|
|
break;
|
|
case 1:
|
|
if (Global::bLiveTraction ? false :
|
|
!p->hvPowerWire) // jeśli nie ma drutu, może pooszukiwać
|
|
MoverParameters->PantRearVolt =
|
|
(p->PantWys >= 1.2) ? 0.95 * MoverParameters->EnginePowerSource.MaxVoltage :
|
|
0.0;
|
|
else if (MoverParameters->PantRearUp ? (PantDiff < 0.01) : false)
|
|
{
|
|
if ((MoverParameters->PantRearVolt == 0.0) &&
|
|
(MoverParameters->PantFrontVolt == 0.0))
|
|
sPantUp.Play(vol, 0, MechInside, vPosition);
|
|
if (p->hvPowerWire) // TODO: wyliczyć trzeba prąd przypadający na
|
|
// pantograf i
|
|
// wstawić do GetVoltage()
|
|
{
|
|
MoverParameters->PantRearVolt =
|
|
p->hvPowerWire->VoltageGet(MoverParameters->Voltage, fPantCurrent);
|
|
fCurrent -= fPantCurrent; // taki prąd płynie przez powyższy pantograf
|
|
}
|
|
else
|
|
MoverParameters->PantRearVolt = 0.0;
|
|
}
|
|
else
|
|
MoverParameters->PantRearVolt = 0.0;
|
|
break;
|
|
} // pozostałe na razie nie obsługiwane
|
|
if (MoverParameters->PantPress >
|
|
(MoverParameters->TrainType == dt_EZT ? 2.5 : 3.3)) // Ra 2013-12:
|
|
// Niebugocław
|
|
// mówi, że w EZT
|
|
// podnoszą się
|
|
// przy 2.5
|
|
pantspeedfactor = 0.015 * (MoverParameters->PantPress) *
|
|
dt1; // z EXE Kursa //Ra: wysokość zależy od ciśnienia !!!
|
|
else
|
|
pantspeedfactor = 0.0;
|
|
if (pantspeedfactor < 0)
|
|
pantspeedfactor = 0;
|
|
k = p->fAngleL;
|
|
if (i ? MoverParameters->PantRearUp :
|
|
MoverParameters->PantFrontUp) // jeśli ma być podniesiony
|
|
{
|
|
if (PantDiff > 0.001) // jeśli nie dolega do drutu
|
|
{ // jeśli poprzednia wysokość jest mniejsza niż pożądana, zwiększyć kąt
|
|
// dolnego
|
|
// ramienia zgodnie z ciśnieniem
|
|
if (pantspeedfactor >
|
|
0.55 * PantDiff) // 0.55 to około pochodna kąta po wysokości
|
|
k += 0.55 * PantDiff; // ograniczenie "skoku" w danej klatce
|
|
else
|
|
k += pantspeedfactor; // dolne ramię
|
|
// jeśli przekroczono kąt graniczny, zablokować pantograf (wymaga
|
|
// interwencji
|
|
// pociągu sieciowego)
|
|
}
|
|
else if (PantDiff < -0.001)
|
|
{ // drut się obniżył albo pantograf
|
|
// podniesiony za wysoko
|
|
// jeśli wysokość jest zbyt duża, wyznaczyć zmniejszenie kąta
|
|
// jeśli zmniejszenie kąta jest zbyt duże, przejść do trybu łamania
|
|
// pantografu
|
|
// if (PantFrontDiff<-0.05) //skok w dół o 5cm daje złąmanie
|
|
// pantografu
|
|
k += 0.4 * PantDiff; // mniej niż pochodna kąta po wysokości
|
|
} // jeśli wysokość jest dobra, nic więcej nie liczyć
|
|
}
|
|
else
|
|
{ // jeśli ma być na dole
|
|
if (k > p->fAngleL0) // jeśli wyżej niż położenie wyjściowe
|
|
k -= 0.15 * dt1; // ruch w dół
|
|
if (k < p->fAngleL0)
|
|
k = p->fAngleL0; // położenie minimalne
|
|
}
|
|
if (k != p->fAngleL)
|
|
{ //żeby nie liczyć w kilku miejscach ani gdy nie potrzeba
|
|
if (k + p->fAngleU < M_PI)
|
|
{ // o ile nie został osiągnięty kąt maksymalny
|
|
p->fAngleL = k; // zmieniony kąt
|
|
// wyliczyć kąt górnego ramienia z wzoru (a)cosinusowego
|
|
//=acos((b*cos()+c)/a)
|
|
// p->dPantAngleT=acos((1.22*cos(k)+0.535)/1.755); //górne ramię
|
|
p->fAngleU = acos((p->fLenL1 * cos(k) + p->fHoriz) / p->fLenU1); // górne ramię
|
|
// wyliczyć aktualną wysokość z wzoru sinusowego
|
|
// h=a*sin()+b*sin()
|
|
p->PantWys = p->fLenL1 * sin(k) + p->fLenU1 * sin(p->fAngleU) +
|
|
p->fHeight; // wysokość całości
|
|
}
|
|
}
|
|
} // koniec pętli po pantografach
|
|
if ((MoverParameters->PantFrontSP == false) && (MoverParameters->PantFrontUp == false))
|
|
{
|
|
sPantDown.Play(vol, 0, MechInside, vPosition);
|
|
MoverParameters->PantFrontSP = true;
|
|
}
|
|
if ((MoverParameters->PantRearSP == false) && (MoverParameters->PantRearUp == false))
|
|
{
|
|
sPantDown.Play(vol, 0, MechInside, vPosition);
|
|
MoverParameters->PantRearSP = true;
|
|
}
|
|
if (MoverParameters->EnginePowerSource.SourceType == CurrentCollector)
|
|
{ // Winger 240404 - wylaczanie sprezarki i
|
|
// przetwornicy przy braku napiecia
|
|
if (tmpTraction.TractionVoltage == 0)
|
|
{ // to coś wyłączało dźwięk silnika w ST43!
|
|
MoverParameters->ConverterFlag = false;
|
|
MoverParameters->CompressorFlag = false; // Ra: to jest wątpliwe - wyłączenie
|
|
// sprężarki powinno być w jednym miejscu!
|
|
}
|
|
}
|
|
}
|
|
else if (MoverParameters->EnginePowerSource.SourceType == InternalSource)
|
|
if (MoverParameters->EnginePowerSource.PowerType == SteamPower)
|
|
// if (smPatykird1[0])
|
|
{ // Ra: animacja rozrządu parowozu, na razie nieoptymalizowane
|
|
/* //Ra: tymczasowo wyłączone ze względu na porządkowanie animacji
|
|
pantografów
|
|
double fi,dx,c2,ka,kc;
|
|
double sin_fi,cos_fi;
|
|
double L1=1.6688888888888889;
|
|
double L2=5.6666666666666667; //2550/450
|
|
double Lc=0.4;
|
|
double L=5.686422222; //2558.89/450
|
|
double G1,G2,G3,ksi,sin_ksi,gam;
|
|
double G1_2,G2_2,G3_2; //kwadraty
|
|
//ruch tłoków oraz korbowodów
|
|
for (int i=0;i<=1;++i)
|
|
{//obie strony w ten sam sposób
|
|
fi=DegToRad(dWheelAngle[1]+(i?pant2x:pant1x)); //kąt obrotu koła dla
|
|
tłoka 1
|
|
sin_fi=sin(fi);
|
|
cos_fi=cos(fi);
|
|
dx=panty*cos_fi+sqrt(panth*panth-panty*panty*sin_fi*sin_fi)-panth;
|
|
//nieoptymalne
|
|
if (smPatykird1[i]) //na razie zabezpieczenie
|
|
smPatykird1[i]->SetTranslate(float3(dx,0,0));
|
|
ka=-asin(panty/panth)*sin_fi;
|
|
if (smPatykirg1[i]) //na razie zabezpieczenie
|
|
smPatykirg1[i]->SetRotateXYZ(vector3(RadToDeg(ka),0,0));
|
|
//smPatykirg1[0]->SetRotate(float3(0,1,0),RadToDeg(fi)); //obracamy
|
|
//ruch drążka mimośrodkowego oraz jarzma
|
|
//korzystałem z pliku PDF "mm.pdf" (opis czworoboku
|
|
korbowo-wahaczowego):
|
|
//"MECHANIKA MASZYN. Szkic wykładu i laboratorium komputerowego."
|
|
//Prof. dr hab. inż. Jerzy Zajączkowski, 2007, Politechnika Łódzka
|
|
//L1 - wysokość (w pionie) osi jarzma ponad osią koła
|
|
//L2 - odległość w poziomie osi jarzma od osi koła
|
|
//Lc - długość korby mimośrodu na kole
|
|
//Lr - promień jarzma =1.0 (pozostałe przeliczone proporcjonalnie)
|
|
//L - długość drążka mimośrodowego
|
|
//fi - kąt obrotu koła
|
|
//ksi - kąt obrotu jarzma (od pionu)
|
|
//gam - odchylenie drążka mimośrodowego od poziomu
|
|
//G1=(Lr*Lr+L1*L1+L2*L2+Kc*Lc-L*L-2.0*Lc*L2*cos(fi)+2.0*Lc*L1*sin(fi))/(Lr*Lr);
|
|
//G2=2.0*(L2-Lc*cos(fi))/Lr;
|
|
//G3=2.0*(L1-Lc*sin(fi))/Lr;
|
|
fi=DegToRad(dWheelAngle[1]+(i?pant2x:pant1x)-96.77416667); //kąt
|
|
obrotu koła dla
|
|
tłoka 1
|
|
//1) dla dWheelAngle[1]=0° korba jest w dół, a mimośród w stronę
|
|
jarzma, czyli
|
|
fi=-7°
|
|
//2) dla dWheelAngle[1]=90° korba jest do tyłu, a mimośród w dół,
|
|
czyli fi=83°
|
|
sin_fi=sin(fi);
|
|
cos_fi=cos(fi);
|
|
G1=(1.0+L1*L1+L2*L2+Lc*Lc-L*L-2.0*Lc*L2*cos_fi+2.0*Lc*L1*sin_fi);
|
|
G1_2=G1*G1;
|
|
G2=2.0*(L2-Lc*cos_fi);
|
|
G2_2=G2*G2;
|
|
G3=2.0*(L1-Lc*sin_fi);
|
|
G3_2=G3*G3;
|
|
sin_ksi=(G1*G2-G3*_fm_sqrt(G2_2+G3_2-G1_2))/(G2_2+G3_2); //x1 (minus
|
|
delta)
|
|
ksi=asin(sin_ksi); //kąt jarzma
|
|
if (smPatykirg2[i])
|
|
smPatykirg2[i]->SetRotateXYZ(vector3(RadToDeg(ksi),0,0)); //obrócenie
|
|
jarzma
|
|
//1) ksi=-23°, gam=
|
|
//2) ksi=10°, gam=
|
|
//gam=acos((L2-sin_ksi-Lc*cos_fi)/L); //kąt od poziomu, liczony
|
|
względem poziomu
|
|
//gam=asin((L1-cos_ksi-Lc*sin_fi)/L); //kąt od poziomu, liczony
|
|
względem pionu
|
|
gam=atan2((L1-cos(ksi)+Lc*sin_fi),(L2-sin_ksi+Lc*cos_fi)); //kąt od
|
|
poziomu
|
|
if (smPatykird2[i]) //na razie zabezpieczenie
|
|
smPatykird2[i]->SetRotateXYZ(vector3(RadToDeg(-gam-ksi),0,0));
|
|
//obrócenie drążka
|
|
mimośrodowego
|
|
}
|
|
*/
|
|
}
|
|
|
|
// NBMX Obsluga drzwi, MC: zuniwersalnione
|
|
if ((dDoorMoveL < MoverParameters->DoorMaxShiftL) && (MoverParameters->DoorLeftOpened))
|
|
dDoorMoveL += dt1 * 0.5 * MoverParameters->DoorOpenSpeed;
|
|
if ((dDoorMoveL > 0) && (!MoverParameters->DoorLeftOpened))
|
|
{
|
|
dDoorMoveL -= dt1 * MoverParameters->DoorCloseSpeed;
|
|
if (dDoorMoveL < 0)
|
|
dDoorMoveL = 0;
|
|
}
|
|
if ((dDoorMoveR < MoverParameters->DoorMaxShiftR) && (MoverParameters->DoorRightOpened))
|
|
dDoorMoveR += dt1 * 0.5 * MoverParameters->DoorOpenSpeed;
|
|
if ((dDoorMoveR > 0) && (!MoverParameters->DoorRightOpened))
|
|
{
|
|
dDoorMoveR -= dt1 * MoverParameters->DoorCloseSpeed;
|
|
if (dDoorMoveR < 0)
|
|
dDoorMoveR = 0;
|
|
}
|
|
|
|
// ABu-160303 sledzenie toru przed obiektem: *******************************
|
|
// Z obserwacji: v>0 -> Coupler 0; v<0 ->coupler1 (Ra: prędkość jest związana
|
|
// z pojazdem)
|
|
// Rozroznienie jest tutaj, zeby niepotrzebnie nie skakac do funkcji. Nie jest
|
|
// uzaleznione
|
|
// od obecnosci AI, zeby uwzglednic np. jadace bez lokomotywy wagony.
|
|
// Ra: można by przenieść na poziom obiektu reprezentującego skład, aby nie
|
|
// sprawdzać środkowych
|
|
if (CouplCounter > 25) // licznik, aby nie robić za każdym razem
|
|
{ // poszukiwanie czegoś do zderzenia się
|
|
fTrackBlock = 10000.0; // na razie nie ma przeszkód (na wypadek nie
|
|
// uruchomienia skanowania)
|
|
// jeśli nie ma zwrotnicy po drodze, to tylko przeliczyć odległość?
|
|
if (MoverParameters->V > 0.03) //[m/s] jeśli jedzie do przodu (w kierunku Coupler 0)
|
|
{
|
|
if (MoverParameters->Couplers[0].CouplingFlag ==
|
|
ctrain_virtual) // brak pojazdu podpiętego?
|
|
{
|
|
ABuScanObjects(1, fScanDist); // szukanie czegoś do podłączenia
|
|
// WriteLog(asName+" - block 0: "+AnsiString(fTrackBlock));
|
|
}
|
|
}
|
|
else if (MoverParameters->V < -0.03) //[m/s] jeśli jedzie do tyłu (w kierunku Coupler 1)
|
|
if (MoverParameters->Couplers[1].CouplingFlag ==
|
|
ctrain_virtual) // brak pojazdu podpiętego?
|
|
{
|
|
ABuScanObjects(-1, fScanDist);
|
|
// WriteLog(asName+" - block 1: "+AnsiString(fTrackBlock));
|
|
}
|
|
CouplCounter = random(20); // ponowne sprawdzenie po losowym czasie
|
|
}
|
|
if (MoverParameters->Vel > 0.1) //[km/h]
|
|
++CouplCounter; // jazda sprzyja poszukiwaniu połączenia
|
|
else
|
|
{
|
|
CouplCounter = 25; // a bezruch nie, ale trzeba zaktualizować odległość, bo
|
|
// zawalidroga może
|
|
// sobie pojechać
|
|
}
|
|
if (MoverParameters->DerailReason > 0)
|
|
{
|
|
switch (MoverParameters->DerailReason)
|
|
{
|
|
case 1:
|
|
ErrorLog("Bad driving: " + asName + " derailed due to end of track");
|
|
break;
|
|
case 2:
|
|
ErrorLog("Bad driving: " + asName + " derailed due to too high speed");
|
|
break;
|
|
case 3:
|
|
ErrorLog("Bad dynamic: " + asName + " derailed due to track width");
|
|
break; // błąd w scenerii
|
|
case 4:
|
|
ErrorLog("Bad dynamic: " + asName + " derailed due to wrong track type");
|
|
break; // błąd w scenerii
|
|
}
|
|
MoverParameters->DerailReason = 0; //żeby tylko raz
|
|
}
|
|
if (MoverParameters->LoadStatus)
|
|
LoadUpdate(); // zmiana modelu ładunku
|
|
return true; // Ra: chyba tak?
|
|
}
|
|
|
|
bool TDynamicObject::FastUpdate(double dt)
|
|
{
|
|
if (dt == 0.0)
|
|
return true; // Ra: pauza
|
|
double dDOMoveLen;
|
|
if (!MoverParameters->PhysicActivation)
|
|
return true; // McZapkie: wylaczanie fizyki gdy nie potrzeba
|
|
|
|
if (!bEnabled)
|
|
return false;
|
|
|
|
TLocation l;
|
|
l.X = -vPosition.x;
|
|
l.Y = vPosition.z;
|
|
l.Z = vPosition.y;
|
|
TRotation r;
|
|
r.Rx = r.Ry = r.Rz = 0;
|
|
|
|
// McZapkie: parametry powinny byc pobierane z toru
|
|
// ts.R=MyTrack->fRadius;
|
|
// ts.Len= Max0R(MoverParameters->BDist,MoverParameters->ADist);
|
|
// ts.dHtrack=Axle1.pPosition.y-Axle0.pPosition.y;
|
|
// ts.dHrail=((Axle1.GetRoll())+(Axle0.GetRoll()))*0.5f;
|
|
// tp.Width=MyTrack->fTrackWidth;
|
|
// McZapkie-250202
|
|
// tp.friction= MyTrack->fFriction;
|
|
// tp.CategoryFlag= MyTrack->iCategoryFlag&15;
|
|
// tp.DamageFlag=MyTrack->iDamageFlag;
|
|
// tp.QualityFlag=MyTrack->iQualityFlag;
|
|
dDOMoveLen = MoverParameters->FastComputeMovement(dt, ts, tp, l, r); // ,ts,tp,tmpTraction);
|
|
// Move(dDOMoveLen);
|
|
// ResetdMoveLen();
|
|
FastMove(dDOMoveLen);
|
|
|
|
if (MoverParameters->LoadStatus)
|
|
LoadUpdate(); // zmiana modelu ładunku
|
|
return true; // Ra: chyba tak?
|
|
}
|
|
|
|
// McZapkie-040402: liczenie pozycji uwzgledniajac wysokosc szyn itp.
|
|
// vector3 TDynamicObject::GetPosition()
|
|
//{//Ra: pozycja pojazdu jest liczona zaraz po przesunięciu
|
|
// return vPosition;
|
|
//};
|
|
|
|
void TDynamicObject::TurnOff()
|
|
{ // wyłączenie rysowania submodeli zmiennych dla
|
|
// egemplarza pojazdu
|
|
btnOn = false;
|
|
btCoupler1.TurnOff();
|
|
btCoupler2.TurnOff();
|
|
btCPneumatic1.TurnOff();
|
|
btCPneumatic1r.TurnOff();
|
|
btCPneumatic2.TurnOff();
|
|
btCPneumatic2r.TurnOff();
|
|
btPneumatic1.TurnOff();
|
|
btPneumatic1r.TurnOff();
|
|
btPneumatic2.TurnOff();
|
|
btPneumatic2r.TurnOff();
|
|
btCCtrl1.TurnOff();
|
|
btCCtrl2.TurnOff();
|
|
btCPass1.TurnOff();
|
|
btCPass2.TurnOff();
|
|
btEndSignals11.TurnOff();
|
|
btEndSignals13.TurnOff();
|
|
btEndSignals21.TurnOff();
|
|
btEndSignals23.TurnOff();
|
|
btEndSignals1.TurnOff();
|
|
btEndSignals2.TurnOff();
|
|
btEndSignalsTab1.TurnOff();
|
|
btEndSignalsTab2.TurnOff();
|
|
btHeadSignals11.TurnOff();
|
|
btHeadSignals12.TurnOff();
|
|
btHeadSignals13.TurnOff();
|
|
btHeadSignals21.TurnOff();
|
|
btHeadSignals22.TurnOff();
|
|
btHeadSignals23.TurnOff();
|
|
};
|
|
|
|
void TDynamicObject::Render()
|
|
{ // rysowanie elementów nieprzezroczystych
|
|
// youBy - sprawdzamy, czy jest sens renderowac
|
|
double modelrotate;
|
|
vector3 tempangle;
|
|
// zmienne
|
|
renderme = false;
|
|
// przeklejka
|
|
double ObjSqrDist = SquareMagnitude(Global::pCameraPosition - vPosition);
|
|
// koniec przeklejki
|
|
if (ObjSqrDist < 500) // jak jest blisko - do 70m
|
|
modelrotate = 0.01; // mały kąt, żeby nie znikało
|
|
else
|
|
{ // Global::pCameraRotation to kąt bewzględny w świecie (zero - na
|
|
// północ)
|
|
tempangle = (vPosition - Global::pCameraPosition); // wektor od kamery
|
|
modelrotate = ABuAcos(tempangle); // określenie kąta
|
|
// if (modelrotate>M_PI) modelrotate-=(2*M_PI);
|
|
modelrotate += Global::pCameraRotation;
|
|
}
|
|
if (modelrotate > M_PI)
|
|
modelrotate -= (2 * M_PI);
|
|
if (modelrotate < -M_PI)
|
|
modelrotate += (2 * M_PI);
|
|
ModCamRot = modelrotate;
|
|
|
|
modelrotate = abs(modelrotate);
|
|
|
|
if (modelrotate < maxrot)
|
|
renderme = true;
|
|
|
|
if (renderme)
|
|
{
|
|
TSubModel::iInstance = (int)this; //żeby nie robić cudzych animacji
|
|
// AnsiString asLoadName="";
|
|
double ObjSqrDist = SquareMagnitude(Global::pCameraPosition - vPosition);
|
|
ABuLittleUpdate(ObjSqrDist); // ustawianie zmiennych submodeli dla wspólnego modelu
|
|
|
|
// Cone(vCoulpler[0],modelRot.z,0);
|
|
// Cone(vCoulpler[1],modelRot.z,1);
|
|
|
|
// ActualTrack= GetTrack(); //McZapkie-240702
|
|
|
|
#if RENDER_CONE
|
|
{ // Ra: testowe renderowanie pozycji wózków w postaci ostrosłupów, wymaga
|
|
// GLUT32.DLL
|
|
double dir = RadToDeg(atan2(vLeft.z, vLeft.x));
|
|
Axle0.Render(0);
|
|
Axle1.Render(1); // bogieRot[0]
|
|
// if (PrevConnected) //renderowanie połączenia
|
|
}
|
|
#endif
|
|
|
|
glPushMatrix();
|
|
// vector3 pos= vPosition;
|
|
// double ObjDist= SquareMagnitude(Global::pCameraPosition-pos);
|
|
if (this == Global::pUserDynamic)
|
|
{ // specjalne ustawienie, aby nie trzęsło
|
|
if (Global::bSmudge)
|
|
{ // jak jest widoczna smuga, to pojazd renderować po
|
|
// wyrenderowaniu smugi
|
|
glPopMatrix(); // a to trzeba zebrać przed wyjściem
|
|
return;
|
|
}
|
|
// if (Global::pWorld->) //tu trzeba by ustawić animacje na modelu
|
|
// zewnętrznym
|
|
glLoadIdentity(); // zacząć od macierzy jedynkowej
|
|
Global::pCamera->SetCabMatrix(vPosition); // specjalne ustawienie kamery
|
|
}
|
|
else
|
|
glTranslated(vPosition.x, vPosition.y,
|
|
vPosition.z); // standardowe przesunięcie względem początku scenerii
|
|
glMultMatrixd(mMatrix.getArray());
|
|
if (fShade > 0.0)
|
|
{ // Ra: zmiana oswietlenia w tunelu, wykopie
|
|
GLfloat ambientLight[4] = {0.5f, 0.5f, 0.5f, 1.0f};
|
|
GLfloat diffuseLight[4] = {0.5f, 0.5f, 0.5f, 1.0f};
|
|
GLfloat specularLight[4] = {0.5f, 0.5f, 0.5f, 1.0f};
|
|
// trochę problem z ambientem w wykopie...
|
|
for (int li = 0; li < 3; li++)
|
|
{
|
|
ambientLight[li] = Global::ambientDayLight[li] * fShade;
|
|
diffuseLight[li] = Global::diffuseDayLight[li] * fShade;
|
|
specularLight[li] = Global::specularDayLight[li] * fShade;
|
|
}
|
|
glLightfv(GL_LIGHT0, GL_AMBIENT, ambientLight);
|
|
glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuseLight);
|
|
glLightfv(GL_LIGHT0, GL_SPECULAR, specularLight);
|
|
}
|
|
if (Global::bUseVBO)
|
|
{ // wersja VBO
|
|
if (mdLowPolyInt)
|
|
if (FreeFlyModeFlag ? true : !mdKabina || !bDisplayCab)
|
|
mdLowPolyInt->RaRender(ObjSqrDist, ReplacableSkinID, iAlpha);
|
|
mdModel->RaRender(ObjSqrDist, ReplacableSkinID, iAlpha);
|
|
if (mdLoad) // renderowanie nieprzezroczystego ładunku
|
|
mdLoad->RaRender(ObjSqrDist, ReplacableSkinID, iAlpha);
|
|
if (mdPrzedsionek)
|
|
mdPrzedsionek->RaRender(ObjSqrDist, ReplacableSkinID, iAlpha);
|
|
}
|
|
else
|
|
{ // wersja Display Lists
|
|
if (mdLowPolyInt)
|
|
if (FreeFlyModeFlag ? true : !mdKabina || !bDisplayCab)
|
|
mdLowPolyInt->Render(ObjSqrDist, ReplacableSkinID, iAlpha);
|
|
mdModel->Render(ObjSqrDist, ReplacableSkinID, iAlpha);
|
|
if (mdLoad) // renderowanie nieprzezroczystego ładunku
|
|
mdLoad->Render(ObjSqrDist, ReplacableSkinID, iAlpha);
|
|
if (mdPrzedsionek)
|
|
mdPrzedsionek->Render(ObjSqrDist, ReplacableSkinID, iAlpha);
|
|
}
|
|
|
|
// Ra: czy ta kabina tu ma sens?
|
|
// Ra: czy nie renderuje się dwukrotnie?
|
|
// Ra: dlaczego jest zablokowana w przezroczystych?
|
|
if (mdKabina) // jeśli ma model kabiny
|
|
if ((mdKabina != mdModel) && bDisplayCab && FreeFlyModeFlag)
|
|
{ // rendering kabiny gdy jest oddzielnym modelem i
|
|
// ma byc wyswietlana
|
|
// ABu: tylko w trybie FreeFly, zwykly tryb w world.cpp
|
|
// Ra: świetła są ustawione dla zewnętrza danego pojazdu
|
|
// oswietlenie kabiny
|
|
GLfloat ambientCabLight[4] = {0.5f, 0.5f, 0.5f, 1.0f};
|
|
GLfloat diffuseCabLight[4] = {0.5f, 0.5f, 0.5f, 1.0f};
|
|
GLfloat specularCabLight[4] = {0.5f, 0.5f, 0.5f, 1.0f};
|
|
for (int li = 0; li < 3; li++)
|
|
{
|
|
ambientCabLight[li] = Global::ambientDayLight[li] * 0.9;
|
|
diffuseCabLight[li] = Global::diffuseDayLight[li] * 0.5;
|
|
specularCabLight[li] = Global::specularDayLight[li] * 0.5;
|
|
}
|
|
switch (MyTrack->eEnvironment)
|
|
{
|
|
case e_canyon:
|
|
{
|
|
for (int li = 0; li < 3; li++)
|
|
{
|
|
diffuseCabLight[li] *= 0.6;
|
|
specularCabLight[li] *= 0.7;
|
|
}
|
|
}
|
|
break;
|
|
case e_tunnel:
|
|
{
|
|
for (int li = 0; li < 3; li++)
|
|
{
|
|
ambientCabLight[li] *= 0.3;
|
|
diffuseCabLight[li] *= 0.1;
|
|
specularCabLight[li] *= 0.2;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
glLightfv(GL_LIGHT0, GL_AMBIENT, ambientCabLight);
|
|
glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuseCabLight);
|
|
glLightfv(GL_LIGHT0, GL_SPECULAR, specularCabLight);
|
|
if (Global::bUseVBO)
|
|
mdKabina->RaRender(ObjSqrDist, 0);
|
|
else
|
|
mdKabina->Render(ObjSqrDist, 0);
|
|
glLightfv(GL_LIGHT0, GL_AMBIENT, Global::ambientDayLight);
|
|
glLightfv(GL_LIGHT0, GL_DIFFUSE, Global::diffuseDayLight);
|
|
glLightfv(GL_LIGHT0, GL_SPECULAR, Global::specularDayLight);
|
|
}
|
|
if (fShade != 0.0) // tylko jeśli było zmieniane
|
|
{ // przywrócenie standardowego oświetlenia
|
|
glLightfv(GL_LIGHT0, GL_AMBIENT, Global::ambientDayLight);
|
|
glLightfv(GL_LIGHT0, GL_DIFFUSE, Global::diffuseDayLight);
|
|
glLightfv(GL_LIGHT0, GL_SPECULAR, Global::specularDayLight);
|
|
}
|
|
glPopMatrix();
|
|
if (btnOn)
|
|
TurnOff(); // przywrócenie domyślnych pozycji submodeli
|
|
} // yB - koniec mieszania z grafika
|
|
};
|
|
|
|
void TDynamicObject::RenderSounds()
|
|
{ // przeliczanie dźwięków, bo będzie
|
|
// słychać bez wyświetlania sektora z
|
|
// pojazdem
|
|
// McZapkie-010302: ulepszony dzwiek silnika
|
|
double freq;
|
|
double vol = 0;
|
|
double dt = Timer::GetDeltaTime();
|
|
|
|
// double sounddist;
|
|
// sounddist=SquareMagnitude(Global::pCameraPosition-vPosition);
|
|
|
|
if (MoverParameters->Power > 0)
|
|
{
|
|
if ((rsSilnik.AM != 0) && ((MoverParameters->Mains) || (MoverParameters->EngineType ==
|
|
DieselEngine))) // McZapkie-280503:
|
|
// zeby dla dumb
|
|
// dzialal silnik na
|
|
// jalowych obrotach
|
|
{
|
|
if ((fabs(MoverParameters->enrot) > 0.01) ||
|
|
(MoverParameters->EngineType == Dumb)) //&& (MoverParameters->EnginePower>0.1))
|
|
{
|
|
freq = rsSilnik.FM * fabs(MoverParameters->enrot) + rsSilnik.FA;
|
|
if (MoverParameters->EngineType == Dumb)
|
|
freq = freq -
|
|
0.2 * MoverParameters->EnginePower / (1 + MoverParameters->Power * 1000);
|
|
rsSilnik.AdjFreq(freq, dt);
|
|
if (MoverParameters->EngineType == DieselEngine)
|
|
{
|
|
if (MoverParameters->enrot > 0)
|
|
{
|
|
if (MoverParameters->EnginePower > 0)
|
|
vol = rsSilnik.AM * MoverParameters->dizel_fill + rsSilnik.AA;
|
|
else
|
|
vol =
|
|
rsSilnik.AM * fabs(MoverParameters->enrot / MoverParameters->nmax) +
|
|
rsSilnik.AA * 0.9;
|
|
}
|
|
else
|
|
vol = 0;
|
|
}
|
|
else if (MoverParameters->EngineType == DieselElectric)
|
|
vol = rsSilnik.AM *
|
|
(MoverParameters->EnginePower / 1000 / MoverParameters->Power) +
|
|
0.2 * (MoverParameters->enrot * 60) /
|
|
(MoverParameters->DElist[MoverParameters->MainCtrlPosNo].RPM) +
|
|
rsSilnik.AA;
|
|
else if (MoverParameters->EngineType == ElectricInductionMotor)
|
|
vol = rsSilnik.AM *
|
|
(MoverParameters->EnginePower + fabs(MoverParameters->enrot * 2)) +
|
|
rsSilnik.AA;
|
|
else
|
|
vol = rsSilnik.AM * (MoverParameters->EnginePower / 1000 +
|
|
fabs(MoverParameters->enrot) * 60.0) +
|
|
rsSilnik.AA;
|
|
// McZapkie-250302 - natezenie zalezne od obrotow i mocy
|
|
if ((vol < 1) && (MoverParameters->EngineType == ElectricSeriesMotor) &&
|
|
(MoverParameters->EnginePower < 100))
|
|
{
|
|
float volrnd =
|
|
random(100) * MoverParameters->enrot / (1 + MoverParameters->nmax);
|
|
if (volrnd < 2)
|
|
vol = vol + volrnd / 200.0;
|
|
}
|
|
switch (MyTrack->eEnvironment)
|
|
{
|
|
case e_tunnel:
|
|
{
|
|
vol += 0.1;
|
|
}
|
|
break;
|
|
case e_canyon:
|
|
{
|
|
vol += 0.05;
|
|
}
|
|
break;
|
|
}
|
|
if ((MoverParameters->DynamicBrakeFlag) && (MoverParameters->EnginePower > 0.1) &&
|
|
(MoverParameters->EngineType ==
|
|
ElectricSeriesMotor)) // Szociu - 29012012 - jeżeli uruchomiony
|
|
// jest hamulec
|
|
// elektrodynamiczny, odtwarzany jest dźwięk silnika
|
|
vol += 0.8;
|
|
|
|
if (enginevolume > 0.0001)
|
|
if (MoverParameters->EngineType != DieselElectric)
|
|
{
|
|
rsSilnik.Play(enginevolume, DSBPLAY_LOOPING, MechInside, GetPosition());
|
|
}
|
|
else
|
|
{
|
|
sConverter.UpdateAF(vol, freq, MechInside, GetPosition());
|
|
|
|
float fincvol;
|
|
fincvol = 0;
|
|
if ((MoverParameters->ConverterFlag) &&
|
|
(MoverParameters->enrot * 60 > MoverParameters->DElist[0].RPM))
|
|
{
|
|
fincvol = (MoverParameters->DElist[MoverParameters->MainCtrlPos].RPM -
|
|
(MoverParameters->enrot * 60));
|
|
fincvol /= (0.05 * MoverParameters->DElist[0].RPM);
|
|
};
|
|
if (fincvol > 0.02)
|
|
rsDiesielInc.Play(fincvol, DSBPLAY_LOOPING, MechInside, GetPosition());
|
|
else
|
|
rsDiesielInc.Stop();
|
|
}
|
|
}
|
|
else
|
|
rsSilnik.Stop();
|
|
}
|
|
enginevolume = (enginevolume + vol) / 2;
|
|
if (enginevolume < 0.01)
|
|
rsSilnik.Stop();
|
|
if ((MoverParameters->EngineType == ElectricSeriesMotor) ||
|
|
(MoverParameters->EngineType == ElectricInductionMotor) && rsWentylator.AM != 0)
|
|
{
|
|
if (MoverParameters->RventRot > 0.1)
|
|
{
|
|
freq = rsWentylator.FM * MoverParameters->RventRot + rsWentylator.FA;
|
|
rsWentylator.AdjFreq(freq, dt);
|
|
if (MoverParameters->EngineType == ElectricInductionMotor)
|
|
vol =
|
|
rsWentylator.AM * sqrt(fabs(MoverParameters->dizel_fill)) + rsWentylator.AA;
|
|
else
|
|
vol = rsWentylator.AM * MoverParameters->RventRot + rsWentylator.AA;
|
|
rsWentylator.Play(vol, DSBPLAY_LOOPING, MechInside, GetPosition());
|
|
}
|
|
else
|
|
rsWentylator.Stop();
|
|
}
|
|
if (MoverParameters->TrainType == dt_ET40)
|
|
{
|
|
if (MoverParameters->Vel > 0.1)
|
|
{
|
|
freq = rsPrzekladnia.FM * (MoverParameters->Vel) + rsPrzekladnia.FA;
|
|
rsPrzekladnia.AdjFreq(freq, dt);
|
|
vol = rsPrzekladnia.AM * (MoverParameters->Vel) + rsPrzekladnia.AA;
|
|
rsPrzekladnia.Play(vol, DSBPLAY_LOOPING, MechInside, GetPosition());
|
|
}
|
|
else
|
|
rsPrzekladnia.Stop();
|
|
}
|
|
}
|
|
|
|
// youBy: dzwiek ostrych lukow i ciasnych zwrotek
|
|
|
|
if ((ts.R * ts.R > 1) && (MoverParameters->Vel > 0))
|
|
vol = MoverParameters->AccN * MoverParameters->AccN;
|
|
else
|
|
vol = 0;
|
|
// vol+=(50000/ts.R*ts.R);
|
|
|
|
if (vol > 0.001)
|
|
{
|
|
rscurve.Play(2 * vol, DSBPLAY_LOOPING, MechInside, GetPosition());
|
|
}
|
|
else
|
|
rscurve.Stop();
|
|
|
|
// McZapkie-280302 - pisk mocno zacisnietych hamulcow - trzeba jeszcze
|
|
// zabezpieczyc przed
|
|
// brakiem deklaracji w mmedia.dta
|
|
if (rsPisk.AM != 0)
|
|
{
|
|
if ((MoverParameters->Vel > (rsPisk.GetStatus() != 0 ? 0.01 : 0.5)) &&
|
|
(!MoverParameters->SlippingWheels) && (MoverParameters->UnitBrakeForce > rsPisk.AM))
|
|
{
|
|
vol = MoverParameters->UnitBrakeForce / (rsPisk.AM + 1) + rsPisk.AA;
|
|
rsPisk.Play(vol, DSBPLAY_LOOPING, MechInside, GetPosition());
|
|
}
|
|
else
|
|
rsPisk.Stop();
|
|
}
|
|
|
|
// if ((MoverParameters->ConverterFlag==false) &&
|
|
// (MoverParameters->TrainType!=dt_ET22))
|
|
// if
|
|
// ((MoverParameters->ConverterFlag==false)&&(MoverParameters->CompressorPower!=0))
|
|
// MoverParameters->CompressorFlag=false; //Ra: wywalić to stąd, tu tylko dla
|
|
// wyświetlanych!
|
|
// Ra: no to już wiemy, dlaczego pociągi jeżdżą lepiej, gdy się na nie patrzy!
|
|
// if (MoverParameters->CompressorPower==2)
|
|
// MoverParameters->CompressorAllow=MoverParameters->ConverterFlag;
|
|
|
|
// McZapkie! - dzwiek compressor.wav tylko gdy dziala sprezarka
|
|
if (MoverParameters->VeselVolume != 0)
|
|
{
|
|
if (MoverParameters->CompressorFlag)
|
|
sCompressor.TurnOn(MechInside, GetPosition());
|
|
else
|
|
sCompressor.TurnOff(MechInside, GetPosition());
|
|
sCompressor.Update(MechInside, GetPosition());
|
|
}
|
|
if (MoverParameters->PantCompFlag) // Winger 160404 - dzwiek malej sprezarki
|
|
sSmallCompressor.TurnOn(MechInside, GetPosition());
|
|
else
|
|
sSmallCompressor.TurnOff(MechInside, GetPosition());
|
|
sSmallCompressor.Update(MechInside, GetPosition());
|
|
|
|
// youBy - przenioslem, bo diesel tez moze miec turbo
|
|
if ((MoverParameters->MainCtrlPos) >=
|
|
(MoverParameters->TurboTest)) // hunter-250312: dlaczego zakomentowane?
|
|
// Ra: bo nie działało dobrze
|
|
{
|
|
// udawanie turbo: (6.66*(eng_vol-0.85))
|
|
if (eng_turbo > 6.66 * (enginevolume - 0.8) + 0.2 * dt)
|
|
eng_turbo = eng_turbo - 0.2 * dt; // 0.125
|
|
else if (eng_turbo < 6.66 * (enginevolume - 0.8) - 0.4 * dt)
|
|
eng_turbo = eng_turbo + 0.4 * dt; // 0.333
|
|
else
|
|
eng_turbo = 6.66 * (enginevolume - 0.8);
|
|
|
|
sTurbo.TurnOn(MechInside, GetPosition());
|
|
// sTurbo.UpdateAF(eng_turbo,0.7+(eng_turbo*0.6),MechInside,GetPosition());
|
|
sTurbo.UpdateAF(3 * eng_turbo - 1, 0.4 + eng_turbo * 0.4, MechInside, GetPosition());
|
|
// eng_vol_act=enginevolume;
|
|
// eng_frq_act=eng_frq;
|
|
}
|
|
else
|
|
sTurbo.TurnOff(MechInside, GetPosition());
|
|
|
|
if (MoverParameters->TrainType == dt_PseudoDiesel)
|
|
{
|
|
// ABu: udawanie woodwarda dla lok. spalinowych
|
|
// jesli silnik jest podpiety pod dzwiek przetwornicy
|
|
if (MoverParameters->ConverterFlag) // NBMX dzwiek przetwornicy
|
|
{
|
|
sConverter.TurnOn(MechInside, GetPosition());
|
|
}
|
|
else
|
|
sConverter.TurnOff(MechInside, GetPosition());
|
|
|
|
// glosnosc zalezy od stosunku mocy silnika el. do mocy max
|
|
double eng_vol;
|
|
if (MoverParameters->Power > 1)
|
|
// 0.85+0.000015*(...)
|
|
eng_vol = 0.8 + 0.00002 * (MoverParameters->EnginePower / MoverParameters->Power);
|
|
else
|
|
eng_vol = 1;
|
|
|
|
eng_dfrq = eng_dfrq + (eng_vol_act - eng_vol);
|
|
if (eng_dfrq > 0)
|
|
{
|
|
eng_dfrq = eng_dfrq - 0.025 * dt;
|
|
if (eng_dfrq < 0.025 * dt)
|
|
eng_dfrq = 0;
|
|
}
|
|
else if (eng_dfrq < 0)
|
|
{
|
|
eng_dfrq = eng_dfrq + 0.025 * dt;
|
|
if (eng_dfrq > -0.025 * dt)
|
|
eng_dfrq = 0;
|
|
}
|
|
double defrot;
|
|
if (MoverParameters->MainCtrlPos != 0)
|
|
{
|
|
double CtrlPos = MoverParameters->MainCtrlPos;
|
|
double CtrlPosNo = MoverParameters->MainCtrlPosNo;
|
|
// defrot=1+0.4*(CtrlPos/CtrlPosNo);
|
|
defrot = 1 + 0.5 * (CtrlPos / CtrlPosNo);
|
|
}
|
|
else
|
|
defrot = 1;
|
|
|
|
if (eng_frq_act < defrot)
|
|
{
|
|
// if (MoverParameters->MainCtrlPos==1) eng_frq_act=eng_frq_act+0.1*dt;
|
|
eng_frq_act = eng_frq_act + 0.4 * dt; // 0.05
|
|
if (eng_frq_act > defrot - 0.4 * dt)
|
|
eng_frq_act = defrot;
|
|
}
|
|
else if (eng_frq_act > defrot)
|
|
{
|
|
eng_frq_act = eng_frq_act - 0.1 * dt; // 0.05
|
|
if (eng_frq_act < defrot + 0.1 * dt)
|
|
eng_frq_act = defrot;
|
|
}
|
|
sConverter.UpdateAF(eng_vol_act, eng_frq_act + eng_dfrq, MechInside, GetPosition());
|
|
// udawanie turbo: (6.66*(eng_vol-0.85))
|
|
if (eng_turbo > 6.66 * (eng_vol - 0.8) + 0.2 * dt)
|
|
eng_turbo = eng_turbo - 0.2 * dt; // 0.125
|
|
else if (eng_turbo < 6.66 * (eng_vol - 0.8) - 0.4 * dt)
|
|
eng_turbo = eng_turbo + 0.4 * dt; // 0.333
|
|
else
|
|
eng_turbo = 6.66 * (eng_vol - 0.8);
|
|
|
|
sTurbo.TurnOn(MechInside, GetPosition());
|
|
// sTurbo.UpdateAF(eng_turbo,0.7+(eng_turbo*0.6),MechInside,GetPosition());
|
|
sTurbo.UpdateAF(3 * eng_turbo - 1, 0.4 + eng_turbo * 0.4, MechInside, GetPosition());
|
|
eng_vol_act = eng_vol;
|
|
// eng_frq_act=eng_frq;
|
|
}
|
|
else
|
|
{
|
|
if (MoverParameters->ConverterFlag) // NBMX dzwiek przetwornicy
|
|
sConverter.TurnOn(MechInside, GetPosition());
|
|
else
|
|
sConverter.TurnOff(MechInside, GetPosition());
|
|
sConverter.Update(MechInside, GetPosition());
|
|
}
|
|
if (MoverParameters->WarningSignal > 0)
|
|
{
|
|
if (TestFlag(MoverParameters->WarningSignal, 1))
|
|
sHorn1.TurnOn(MechInside, GetPosition());
|
|
else
|
|
sHorn1.TurnOff(MechInside, GetPosition());
|
|
if (TestFlag(MoverParameters->WarningSignal, 2))
|
|
sHorn2.TurnOn(MechInside, GetPosition());
|
|
else
|
|
sHorn2.TurnOff(MechInside, GetPosition());
|
|
}
|
|
else
|
|
{
|
|
sHorn1.TurnOff(MechInside, GetPosition());
|
|
sHorn2.TurnOff(MechInside, GetPosition());
|
|
}
|
|
if (MoverParameters->DoorClosureWarning)
|
|
{
|
|
if (MoverParameters->DepartureSignal) // NBMX sygnal odjazdu, MC: pod warunkiem ze jest
|
|
// zdefiniowane w chk
|
|
sDepartureSignal.TurnOn(MechInside, GetPosition());
|
|
else
|
|
sDepartureSignal.TurnOff(MechInside, GetPosition());
|
|
sDepartureSignal.Update(MechInside, GetPosition());
|
|
}
|
|
sHorn1.Update(MechInside, GetPosition());
|
|
sHorn2.Update(MechInside, GetPosition());
|
|
// McZapkie: w razie wykolejenia
|
|
if (MoverParameters->EventFlag)
|
|
{
|
|
if (TestFlag(MoverParameters->DamageFlag, dtrain_out) && GetVelocity() > 0)
|
|
rsDerailment.Play(1, 0, true, GetPosition());
|
|
if (GetVelocity() == 0)
|
|
rsDerailment.Stop();
|
|
}
|
|
/* //Ra: dwa razy?
|
|
if (MoverParameters->EventFlag)
|
|
{
|
|
if (TestFlag(MoverParameters->DamageFlag,dtrain_out) && GetVelocity()>0)
|
|
rsDerailment.Play(1,0,true,GetPosition());
|
|
if (GetVelocity()==0)
|
|
rsDerailment.Stop();
|
|
}
|
|
*/
|
|
};
|
|
|
|
void TDynamicObject::RenderAlpha()
|
|
{ // rysowanie elementów półprzezroczystych
|
|
if (renderme)
|
|
{
|
|
TSubModel::iInstance = (int)this; //żeby nie robić cudzych animacji
|
|
double ObjSqrDist = SquareMagnitude(Global::pCameraPosition - vPosition);
|
|
ABuLittleUpdate(ObjSqrDist); // ustawianie zmiennych submodeli dla wspólnego modelu
|
|
glPushMatrix();
|
|
if (this == Global::pUserDynamic)
|
|
{ // specjalne ustawienie, aby nie trzęsło
|
|
if (Global::bSmudge)
|
|
{ // jak smuga, to rysować po smudze
|
|
glPopMatrix(); // to trzeba zebrać przed wyściem
|
|
return;
|
|
}
|
|
glLoadIdentity(); // zacząć od macierzy jedynkowej
|
|
Global::pCamera->SetCabMatrix(vPosition); // specjalne ustawienie kamery
|
|
}
|
|
else
|
|
glTranslated(vPosition.x, vPosition.y,
|
|
vPosition.z); // standardowe przesunięcie względem początku scenerii
|
|
glMultMatrixd(mMatrix.getArray());
|
|
if (fShade > 0.0)
|
|
{ // Ra: zmiana oswietlenia w tunelu, wykopie
|
|
GLfloat ambientLight[4] = {0.5f, 0.5f, 0.5f, 1.0f};
|
|
GLfloat diffuseLight[4] = {0.5f, 0.5f, 0.5f, 1.0f};
|
|
GLfloat specularLight[4] = {0.5f, 0.5f, 0.5f, 1.0f};
|
|
// trochę problem z ambientem w wykopie...
|
|
for (int li = 0; li < 3; li++)
|
|
{
|
|
ambientLight[li] = Global::ambientDayLight[li] * fShade;
|
|
diffuseLight[li] = Global::diffuseDayLight[li] * fShade;
|
|
specularLight[li] = Global::specularDayLight[li] * fShade;
|
|
}
|
|
glLightfv(GL_LIGHT0, GL_AMBIENT, ambientLight);
|
|
glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuseLight);
|
|
glLightfv(GL_LIGHT0, GL_SPECULAR, specularLight);
|
|
}
|
|
if (Global::bUseVBO)
|
|
{ // wersja VBO
|
|
if (mdLowPolyInt)
|
|
if (FreeFlyModeFlag ? true : !mdKabina || !bDisplayCab)
|
|
mdLowPolyInt->RaRenderAlpha(ObjSqrDist, ReplacableSkinID, iAlpha);
|
|
mdModel->RaRenderAlpha(ObjSqrDist, ReplacableSkinID, iAlpha);
|
|
if (mdLoad)
|
|
mdLoad->RaRenderAlpha(ObjSqrDist, ReplacableSkinID, iAlpha);
|
|
// if (mdPrzedsionek) //Ra: przedsionków tu wcześniej nie było - włączyć?
|
|
// mdPrzedsionek->RaRenderAlpha(ObjSqrDist,ReplacableSkinID,iAlpha);
|
|
}
|
|
else
|
|
{ // wersja Display Lists
|
|
if (mdLowPolyInt)
|
|
if (FreeFlyModeFlag ? true : !mdKabina || !bDisplayCab)
|
|
mdLowPolyInt->RenderAlpha(ObjSqrDist, ReplacableSkinID, iAlpha);
|
|
mdModel->RenderAlpha(ObjSqrDist, ReplacableSkinID, iAlpha);
|
|
if (mdLoad)
|
|
mdLoad->RenderAlpha(ObjSqrDist, ReplacableSkinID, iAlpha);
|
|
// if (mdPrzedsionek) //Ra: przedsionków tu wcześniej nie było - włączyć?
|
|
// mdPrzedsionek->RenderAlpha(ObjSqrDist,ReplacableSkinID,iAlpha);
|
|
}
|
|
/* skoro false to można wyciąc
|
|
//ABu: Tylko w trybie freefly
|
|
if (false)//((mdKabina!=mdModel) && bDisplayCab && FreeFlyModeFlag)
|
|
{
|
|
//oswietlenie kabiny
|
|
GLfloat ambientCabLight[4]= { 0.5f, 0.5f, 0.5f, 1.0f };
|
|
GLfloat diffuseCabLight[4]= { 0.5f, 0.5f, 0.5f, 1.0f };
|
|
GLfloat specularCabLight[4]= { 0.5f, 0.5f, 0.5f, 1.0f };
|
|
for (int li=0; li<3; li++)
|
|
{
|
|
ambientCabLight[li]= Global::ambientDayLight[li]*0.9;
|
|
diffuseCabLight[li]= Global::diffuseDayLight[li]*0.5;
|
|
specularCabLight[li]= Global::specularDayLight[li]*0.5;
|
|
}
|
|
switch (MyTrack->eEnvironment)
|
|
{
|
|
case e_canyon:
|
|
{
|
|
for (int li=0; li<3; li++)
|
|
{
|
|
diffuseCabLight[li]*= 0.6;
|
|
specularCabLight[li]*= 0.8;
|
|
}
|
|
}
|
|
break;
|
|
case e_tunnel:
|
|
{
|
|
for (int li=0; li<3; li++)
|
|
{
|
|
ambientCabLight[li]*= 0.3;
|
|
diffuseCabLight[li]*= 0.1;
|
|
specularCabLight[li]*= 0.2;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
// dorobic swiatlo od drugiej strony szyby
|
|
|
|
glLightfv(GL_LIGHT0,GL_AMBIENT,ambientCabLight);
|
|
glLightfv(GL_LIGHT0,GL_DIFFUSE,diffuseCabLight);
|
|
glLightfv(GL_LIGHT0,GL_SPECULAR,specularCabLight);
|
|
|
|
mdKabina->RenderAlpha(ObjSqrDist,0);
|
|
//smierdzi
|
|
// mdModel->RenderAlpha(SquareMagnitude(Global::pCameraPosition-pos),0);
|
|
|
|
glLightfv(GL_LIGHT0,GL_AMBIENT,Global::ambientDayLight);
|
|
glLightfv(GL_LIGHT0,GL_DIFFUSE,Global::diffuseDayLight);
|
|
glLightfv(GL_LIGHT0,GL_SPECULAR,Global::specularDayLight);
|
|
}
|
|
*/
|
|
if (fShade != 0.0) // tylko jeśli było zmieniane
|
|
{ // przywrócenie standardowego oświetlenia
|
|
glLightfv(GL_LIGHT0, GL_AMBIENT, Global::ambientDayLight);
|
|
glLightfv(GL_LIGHT0, GL_DIFFUSE, Global::diffuseDayLight);
|
|
glLightfv(GL_LIGHT0, GL_SPECULAR, Global::specularDayLight);
|
|
}
|
|
glPopMatrix();
|
|
if (btnOn)
|
|
TurnOff(); // przywrócenie domyślnych pozycji submodeli
|
|
}
|
|
return;
|
|
} // koniec renderalpha
|
|
|
|
// McZapkie-250202
|
|
// wczytywanie pliku z danymi multimedialnymi (dzwieki)
|
|
void TDynamicObject::LoadMMediaFile(AnsiString BaseDir, AnsiString TypeName,
|
|
AnsiString ReplacableSkin)
|
|
{
|
|
double dSDist;
|
|
TFileStream *fs;
|
|
// asBaseDir=BaseDir;
|
|
Global::asCurrentDynamicPath = BaseDir;
|
|
AnsiString asFileName = BaseDir + TypeName + ".mmd";
|
|
AnsiString asLoadName = BaseDir + MoverParameters->LoadType + ".t3d";
|
|
if (!FileExists(asFileName))
|
|
{
|
|
ErrorLog("Missed file: " + asFileName); // brak MMD
|
|
return;
|
|
}
|
|
fs = new TFileStream(asFileName, fmOpenRead | fmShareCompat);
|
|
if (!fs)
|
|
return;
|
|
int size = fs->Size;
|
|
if (!size)
|
|
{
|
|
return delete fs;
|
|
};
|
|
AnsiString asAnimName;
|
|
bool Stop_InternalData = false;
|
|
char *buf = new char[size + 1]; // ciąg bajtów o długości równej rozmiwarowi pliku
|
|
buf[size] = '\0'; // zakończony zerem na wszelki wypadek
|
|
fs->Read(buf, size);
|
|
delete fs;
|
|
TQueryParserComp *Parser;
|
|
Parser = new TQueryParserComp(NULL);
|
|
Parser->TextToParse = AnsiString(buf);
|
|
delete[] buf;
|
|
AnsiString str;
|
|
// Parser->LoadStringToParse(asFile);
|
|
Parser->First();
|
|
// DecimalSeparator= '.';
|
|
pants = NULL; // wskaźnik pierwszego obiektu animującego dla pantografów
|
|
int i;
|
|
while (!Parser->EndOfFile && !Stop_InternalData)
|
|
{
|
|
str = Parser->GetNextSymbol().LowerCase();
|
|
if (str == AnsiString("models:")) // modele i podmodele
|
|
{
|
|
iMultiTex = 0; // czy jest wiele tekstur wymiennych?
|
|
asModel = Parser->GetNextSymbol().LowerCase();
|
|
if (asModel.Pos("#") == asModel.Length()) // Ra 2015-01: nie podoba mi się to
|
|
{ // model wymaga wielu tekstur wymiennych
|
|
iMultiTex = 1;
|
|
asModel = asModel.SubString(1, asModel.Length() - 1);
|
|
}
|
|
if ((i = asModel.Pos(",")) > 0)
|
|
{ // Ra 2015-01: może szukać przecinka w
|
|
// nazwie modelu, a po przecinku była by
|
|
// liczba
|
|
// tekstur?
|
|
if (i < asModel.Length())
|
|
iMultiTex = asModel[i + 1] - '0';
|
|
if (iMultiTex < 0)
|
|
iMultiTex = 0;
|
|
else if (iMultiTex > 1)
|
|
iMultiTex = 1; // na razie ustawiamy na 1
|
|
}
|
|
asModel = BaseDir + asModel; // McZapkie 2002-07-20: dynamics maja swoje
|
|
// modele w dynamics/basedir
|
|
Global::asCurrentTexturePath = BaseDir; // biezaca sciezka do tekstur to dynamic/...
|
|
mdModel = TModelsManager::GetModel(asModel.c_str(), true);
|
|
if (ReplacableSkin != AnsiString("none"))
|
|
{ // tekstura wymienna jest raczej jedynie w "dynamic\"
|
|
ReplacableSkin =
|
|
Global::asCurrentTexturePath + ReplacableSkin; // skory tez z dynamic/...
|
|
if ((i = ReplacableSkin.Pos("|")) > 0) // replacable dzielone
|
|
{
|
|
iMultiTex = -1;
|
|
ReplacableSkinID[-iMultiTex] = TTexturesManager::GetTextureID(
|
|
NULL, NULL, ReplacableSkin.SubString(1, i - 1).c_str(),
|
|
Global::iDynamicFiltering);
|
|
ReplacableSkin.Delete(1, i); // usunięcie razem z pionową kreską
|
|
ReplacableSkin = Global::asCurrentTexturePath +
|
|
ReplacableSkin; // odtworzenie początku ścieżki
|
|
// sprawdzić, ile jest i ustawić iMultiTex na liczbę podanych tekstur
|
|
if (!ReplacableSkin.IsEmpty())
|
|
{ // próba wycięcia drugiej nazwy
|
|
iMultiTex = -2; // skoro zostało coś po kresce, to są co najmniej dwie
|
|
if ((i = ReplacableSkin.Pos("|")) == 0) // gdy nie ma już kreski
|
|
ReplacableSkinID[-iMultiTex] = TTexturesManager::GetTextureID(
|
|
NULL, NULL, ReplacableSkin.SubString(1, i - 1).c_str(),
|
|
Global::iDynamicFiltering);
|
|
else
|
|
{ // jak jest kreska, to wczytać drugą i próbować trzecią
|
|
ReplacableSkinID[-iMultiTex] = TTexturesManager::GetTextureID(
|
|
NULL, NULL, ReplacableSkin.SubString(1, i - 1).c_str(),
|
|
Global::iDynamicFiltering);
|
|
ReplacableSkin.Delete(1, i); // usunięcie razem z pionową kreską
|
|
ReplacableSkin = Global::asCurrentTexturePath +
|
|
ReplacableSkin; // odtworzenie początku ścieżki
|
|
if (!ReplacableSkin.IsEmpty())
|
|
{ // próba wycięcia trzeciej nazwy
|
|
iMultiTex =
|
|
-3; // skoro zostało coś po kresce, to są co najmniej trzy
|
|
if ((i = ReplacableSkin.Pos("|")) == 0) // gdy nie ma już kreski
|
|
ReplacableSkinID[-iMultiTex] = TTexturesManager::GetTextureID(
|
|
NULL, NULL, ReplacableSkin.SubString(1, i - 1).c_str(),
|
|
Global::iDynamicFiltering);
|
|
else
|
|
{ // jak jest kreska, to wczytać trzecią i próbować czwartą
|
|
ReplacableSkinID[-iMultiTex] = TTexturesManager::GetTextureID(
|
|
NULL, NULL, ReplacableSkin.SubString(1, i - 1).c_str(),
|
|
Global::iDynamicFiltering);
|
|
ReplacableSkin.Delete(1, i); // usunięcie razem z pionową kreską
|
|
ReplacableSkin = Global::asCurrentTexturePath +
|
|
ReplacableSkin; // odtworzenie początku ścieżki
|
|
if (!ReplacableSkin.IsEmpty())
|
|
{ // próba wycięcia trzeciej nazwy
|
|
iMultiTex = -4; // skoro zostało coś po kresce, to są co
|
|
// najmniej cztery
|
|
ReplacableSkinID[-iMultiTex] =
|
|
TTexturesManager::GetTextureID(
|
|
NULL, NULL,
|
|
ReplacableSkin.SubString(1, i - 1).c_str(),
|
|
Global::iDynamicFiltering);
|
|
// więcej na razie nie zadziała, a u tak trzeba to do modeli
|
|
// przenieść
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (iMultiTex > 0)
|
|
{ // jeśli model ma 4 tekstury
|
|
ReplacableSkinID[1] = TTexturesManager::GetTextureID(
|
|
NULL, NULL, (ReplacableSkin + ",1").c_str(), Global::iDynamicFiltering);
|
|
if (ReplacableSkinID[1])
|
|
{ // pierwsza z zestawu znaleziona
|
|
ReplacableSkinID[2] = TTexturesManager::GetTextureID(
|
|
NULL, NULL, (ReplacableSkin + ",2").c_str(), Global::iDynamicFiltering);
|
|
if (ReplacableSkinID[2])
|
|
{
|
|
iMultiTex = 2; // już są dwie
|
|
ReplacableSkinID[3] = TTexturesManager::GetTextureID(
|
|
NULL, NULL, (ReplacableSkin + ",3").c_str(),
|
|
Global::iDynamicFiltering);
|
|
if (ReplacableSkinID[3])
|
|
{
|
|
iMultiTex = 3; // a teraz nawet trzy
|
|
ReplacableSkinID[4] = TTexturesManager::GetTextureID(
|
|
NULL, NULL, (ReplacableSkin + ",4").c_str(),
|
|
Global::iDynamicFiltering);
|
|
if (ReplacableSkinID[4])
|
|
iMultiTex = 4; // jak są cztery, to blokujemy podmianę tekstury
|
|
// rozkładem
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{ // zestaw nie zadziałał, próbujemy normanie
|
|
iMultiTex = 0;
|
|
ReplacableSkinID[1] = TTexturesManager::GetTextureID(
|
|
NULL, NULL, ReplacableSkin.c_str(), Global::iDynamicFiltering);
|
|
}
|
|
}
|
|
else
|
|
ReplacableSkinID[1] = TTexturesManager::GetTextureID(
|
|
NULL, NULL, ReplacableSkin.c_str(), Global::iDynamicFiltering);
|
|
if (TTexturesManager::GetAlpha(ReplacableSkinID[1]))
|
|
iAlpha = 0x31310031; // tekstura -1 z kanałem alfa - nie renderować w cyklu
|
|
// nieprzezroczystych
|
|
else
|
|
iAlpha = 0x30300030; // wszystkie tekstury nieprzezroczyste - nie
|
|
// renderować w
|
|
// cyklu przezroczystych
|
|
if (ReplacableSkinID[2])
|
|
if (TTexturesManager::GetAlpha(ReplacableSkinID[2]))
|
|
iAlpha |= 0x02020002; // tekstura -2 z kanałem alfa - nie renderować
|
|
// w cyklu
|
|
// nieprzezroczystych
|
|
if (ReplacableSkinID[3])
|
|
if (TTexturesManager::GetAlpha(ReplacableSkinID[3]))
|
|
iAlpha |= 0x04040004; // tekstura -3 z kanałem alfa - nie renderować
|
|
// w cyklu
|
|
// nieprzezroczystych
|
|
if (ReplacableSkinID[4])
|
|
if (TTexturesManager::GetAlpha(ReplacableSkinID[4]))
|
|
iAlpha |= 0x08080008; // tekstura -4 z kanałem alfa - nie renderować
|
|
// w cyklu
|
|
// nieprzezroczystych
|
|
}
|
|
// Winger 040304 - ladowanie przedsionkow dla EZT
|
|
if (MoverParameters->TrainType == dt_EZT)
|
|
{
|
|
asModel = "przedsionki.t3d";
|
|
asModel = BaseDir + asModel;
|
|
mdPrzedsionek = TModelsManager::GetModel(asModel.c_str(), true);
|
|
}
|
|
if (!MoverParameters->LoadAccepted.IsEmpty())
|
|
// if (MoverParameters->LoadAccepted!=AnsiString("")); // &&
|
|
// MoverParameters->LoadType!=AnsiString("passengers"))
|
|
if (MoverParameters->EnginePowerSource.SourceType == CurrentCollector)
|
|
{ // wartość niby "pantstate" - nazwa dla
|
|
// formalności, ważna jest ilość
|
|
if (MoverParameters->Load == 1)
|
|
MoverParameters->PantFront(true);
|
|
else if (MoverParameters->Load == 2)
|
|
MoverParameters->PantRear(true);
|
|
else if (MoverParameters->Load == 3)
|
|
{
|
|
MoverParameters->PantFront(true);
|
|
MoverParameters->PantRear(true);
|
|
}
|
|
else if (MoverParameters->Load == 4)
|
|
MoverParameters->DoubleTr = -1;
|
|
else if (MoverParameters->Load == 5)
|
|
{
|
|
MoverParameters->DoubleTr = -1;
|
|
MoverParameters->PantRear(true);
|
|
}
|
|
else if (MoverParameters->Load == 6)
|
|
{
|
|
MoverParameters->DoubleTr = -1;
|
|
MoverParameters->PantFront(true);
|
|
}
|
|
else if (MoverParameters->Load == 7)
|
|
{
|
|
MoverParameters->DoubleTr = -1;
|
|
MoverParameters->PantFront(true);
|
|
MoverParameters->PantRear(true);
|
|
}
|
|
}
|
|
else // Ra: tu wczytywanie modelu ładunku jest w porządku
|
|
mdLoad = TModelsManager::GetModel(asLoadName.c_str(), true); // ladunek
|
|
Global::asCurrentTexturePath =
|
|
AnsiString(szTexturePath); // z powrotem defaultowa sciezka do tekstur
|
|
while (!Parser->EndOfFile && str != AnsiString("endmodels"))
|
|
{
|
|
str = Parser->GetNextSymbol().LowerCase();
|
|
if (str == AnsiString("animations:"))
|
|
{ // Ra: ustawienie ilości
|
|
// poszczególnych animacji -
|
|
// musi być jako pierwsze,
|
|
// inaczej
|
|
// ilości będą domyślne
|
|
if (!pAnimations)
|
|
{ // jeśli nie ma jeszcze tabeli animacji, można
|
|
// odczytać nowe ilości
|
|
int co = 0, ile;
|
|
iAnimations = 0;
|
|
do
|
|
{ // kolejne liczby to ilość animacj, -1 to znacznik końca
|
|
ile = Parser->GetNextSymbol().ToIntDef(-1); // ilość danego typu
|
|
// animacji
|
|
// if (co==ANIM_PANTS)
|
|
// if (!Global::bLoadTraction)
|
|
// if (!DebugModeFlag) //w debugmode pantografy mają "niby
|
|
// działać"
|
|
// ile=0; //wyłączenie animacji pantografów
|
|
if (co < ANIM_TYPES)
|
|
if (ile >= 0)
|
|
{
|
|
iAnimType[co] = ile; // zapamiętanie
|
|
iAnimations += ile; // ogólna ilość animacji
|
|
}
|
|
++co;
|
|
} while (ile >= 0); //-1 to znacznik końca
|
|
while (co < ANIM_TYPES)
|
|
iAnimType[co++] = 0; // zerowanie pozostałych
|
|
str = Parser->GetNextSymbol().LowerCase();
|
|
}
|
|
// WriteLog("Total animations: "+AnsiString(iAnimations));
|
|
}
|
|
if (!pAnimations)
|
|
{ // Ra: tworzenie tabeli animacji, jeśli jeszcze nie
|
|
// było
|
|
if (!iAnimations) // jeśli nie podano jawnie, ile ma być animacji
|
|
iAnimations = 28; // tyle było kiedyś w każdym pojeździe (2 wiązary wypadły)
|
|
/* //pojazd może mieć pantograf do innych celów niż napęd
|
|
if (MoverParameters->EnginePowerSource.SourceType!=CurrentCollector)
|
|
{//nie będzie pantografów, to się trochę uprości
|
|
iAnimations-=iAnimType[ANIM_PANTS]; //domyślnie były 2 pantografy
|
|
iAnimType[ANIM_PANTS]=0;
|
|
}
|
|
*/
|
|
pAnimations = new TAnim[iAnimations];
|
|
int i, j, k = 0, sm = 0;
|
|
for (j = 0; j < ANIM_TYPES; ++j)
|
|
for (i = 0; i < iAnimType[j]; ++i)
|
|
{
|
|
if (j == ANIM_PANTS) // zliczamy poprzednie animacje
|
|
if (!pants)
|
|
if (iAnimType[ANIM_PANTS]) // o ile jakieś pantografy są (a
|
|
// domyślnie są)
|
|
pants = pAnimations +
|
|
k; // zapamiętanie na potrzeby wyszukania submodeli
|
|
pAnimations[k].iShift = sm; // przesunięcie do przydzielenia wskaźnika
|
|
sm += pAnimations[k++].TypeSet(j); // ustawienie typu animacji i
|
|
// zliczanie tablicowanych
|
|
// submodeli
|
|
}
|
|
if (sm) // o ile są bardziej złożone animacje
|
|
{
|
|
pAnimated = new TSubModel *[sm]; // tabela na animowane submodele
|
|
for (k = 0; k < iAnimations; ++k)
|
|
pAnimations[k].smElement =
|
|
pAnimated +
|
|
pAnimations[k].iShift; // przydzielenie wskaźnika do tabelki
|
|
}
|
|
}
|
|
if (str == AnsiString("lowpolyinterior:")) // ABu: wnetrze lowpoly
|
|
{
|
|
asModel = Parser->GetNextSymbol().LowerCase();
|
|
asModel = BaseDir + asModel; // McZapkie-200702 - dynamics maja swoje
|
|
// modele w dynamic/basedir
|
|
Global::asCurrentTexturePath =
|
|
BaseDir; // biezaca sciezka do tekstur to dynamic/...
|
|
mdLowPolyInt = TModelsManager::GetModel(asModel.c_str(), true);
|
|
// Global::asCurrentTexturePath=AnsiString(szTexturePath); //kiedyś
|
|
// uproszczone
|
|
// wnętrze mieszało tekstury nieba
|
|
}
|
|
if (str == AnsiString("brakemode:"))
|
|
{ // Ra 15-01: gałka nastawy hamulca
|
|
asAnimName = Parser->GetNextSymbol().LowerCase();
|
|
smBrakeMode = mdModel->GetFromName(asAnimName.c_str());
|
|
// jeszcze wczytać kąty obrotu dla poszczególnych ustawień
|
|
}
|
|
if (str == AnsiString("loadmode:"))
|
|
{ // Ra 15-01: gałka nastawy hamulca
|
|
asAnimName = Parser->GetNextSymbol().LowerCase();
|
|
smLoadMode = mdModel->GetFromName(asAnimName.c_str());
|
|
// jeszcze wczytać kąty obrotu dla poszczególnych ustawień
|
|
}
|
|
else if (str == AnsiString("animwheelprefix:"))
|
|
{ // prefiks kręcących się kół
|
|
int i, j, k, m;
|
|
str = Parser->GetNextSymbol();
|
|
for (i = 0; i < iAnimType[ANIM_WHEELS]; ++i) // liczba osi
|
|
{ // McZapkie-050402: wyszukiwanie kol o nazwie str*
|
|
asAnimName = str + AnsiString(i + 1);
|
|
pAnimations[i].smAnimated =
|
|
mdModel->GetFromName(asAnimName.c_str()); // ustalenie submodelu
|
|
if (pAnimations[i].smAnimated)
|
|
{ //++iAnimatedAxles;
|
|
pAnimations[i].smAnimated->WillBeAnimated(); // wyłączenie optymalizacji
|
|
// transformu
|
|
pAnimations[i].yUpdate = UpdateAxle; // animacja osi
|
|
pAnimations[i].fMaxDist =
|
|
50 *
|
|
MoverParameters->WheelDiameter; // nie kręcić w większej odległości
|
|
pAnimations[i].fMaxDist *=
|
|
pAnimations[i].fMaxDist *
|
|
MoverParameters->WheelDiameter; // 50m do kwadratu, a średnica
|
|
// do trzeciej
|
|
pAnimations[i].fMaxDist *= Global::fDistanceFactor; // współczynnik
|
|
// przeliczeniowy
|
|
// jakości ekranu
|
|
}
|
|
}
|
|
// Ra: ustawianie indeksów osi
|
|
for (i = 0; i < iAnimType[ANIM_WHEELS];
|
|
++i) // ilość osi (zabezpieczenie przed błędami w CHK)
|
|
pAnimations[i].dWheelAngle =
|
|
dWheelAngle + 1; // domyślnie wskaźnik na napędzające
|
|
i = 0;
|
|
j = 1;
|
|
k = 0;
|
|
m = 0; // numer osi; kolejny znak; ile osi danego typu; która średnica
|
|
if ((MoverParameters->WheelDiameterL != MoverParameters->WheelDiameter) ||
|
|
(MoverParameters->WheelDiameterT != MoverParameters->WheelDiameter))
|
|
{ // obsługa różnych średnic, o
|
|
// ile występują
|
|
while ((i < iAnimType[ANIM_WHEELS]) &&
|
|
(j <= MoverParameters->AxleArangement.Length()))
|
|
{ // wersja ze wskaźnikami jest
|
|
// bardziej elastyczna na nietypowe
|
|
// układy
|
|
if ((k >= 'A') && (k <= 'J')) // 10 chyba maksimum?
|
|
{
|
|
pAnimations[i++].dWheelAngle =
|
|
dWheelAngle + 1; // obrót osi napędzających
|
|
--k; // następna będzie albo taka sama, albo bierzemy kolejny
|
|
// znak
|
|
m = 2; // następujące toczne będą miały inną średnicę
|
|
}
|
|
else if ((k >= '1') && (k <= '9'))
|
|
{
|
|
pAnimations[i++].dWheelAngle = dWheelAngle + m; // obrót osi
|
|
// tocznych
|
|
--k; // następna będzie albo taka sama, albo bierzemy kolejny
|
|
// znak
|
|
}
|
|
else
|
|
k = MoverParameters->AxleArangement[j++]; // pobranie kolejnego
|
|
// znaku
|
|
}
|
|
}
|
|
}
|
|
// else if (str==AnsiString("animrodprefix:")) //prefiks wiazarow dwoch
|
|
// {
|
|
// str= Parser->GetNextSymbol();
|
|
// for (int i=1; i<=2; i++)
|
|
// {//McZapkie-050402: wyszukiwanie max 2 wiazarow o nazwie str*
|
|
// asAnimName=str+i;
|
|
// smWiazary[i-1]=mdModel->GetFromName(asAnimName.c_str());
|
|
// smWiazary[i-1]->WillBeAnimated();
|
|
// }
|
|
// }
|
|
else if (str == AnsiString("animpantprefix:"))
|
|
{ // Ra: pantografy po
|
|
// nowemu mają literki
|
|
// i numerki
|
|
}
|
|
// Pantografy - Winger 160204
|
|
if (str == AnsiString("animpantrd1prefix:"))
|
|
{ // prefiks ramion dolnych 1
|
|
str = Parser->GetNextSymbol();
|
|
float4x4 m; // macierz do wyliczenia pozycji i wektora ruchu pantografu
|
|
TSubModel *sm;
|
|
if (pants)
|
|
for (int i = 0; i < iAnimType[ANIM_PANTS]; i++)
|
|
{ // Winger 160204: wyszukiwanie max 2 patykow o nazwie
|
|
// str*
|
|
asAnimName = str + AnsiString(i + 1);
|
|
sm = mdModel->GetFromName(asAnimName.c_str());
|
|
pants[i].smElement[0] = sm; // jak NULL, to nie będzie animowany
|
|
if (sm)
|
|
{ // w EP09 wywalało się tu z powodu NULL
|
|
sm->WillBeAnimated();
|
|
sm->ParentMatrix(&m); // pobranie macierzy transformacji
|
|
// m(3)[1]=m[3][1]+0.054; //w górę o wysokość ślizgu (na razie
|
|
// tak)
|
|
if ((mdModel->Flags() & 0x8000) == 0) // jeśli wczytano z T3D
|
|
m.InitialRotate(); // może być potrzebny dodatkowy obrót,
|
|
// jeśli
|
|
// wczytano z T3D, tzn. przed wykonaniem
|
|
// Init()
|
|
pants[i].fParamPants->vPos.z =
|
|
m[3][0]; // przesunięcie w bok (asymetria)
|
|
pants[i].fParamPants->vPos.y =
|
|
m[3][1]; // przesunięcie w górę odczytane z modelu
|
|
if ((sm = pants[i].smElement[0]->ChildGet()) != NULL)
|
|
{ // jeśli ma potomny, można policzyć długość
|
|
// (odległość potomnego
|
|
// od osi obrotu)
|
|
m = float4x4(*sm->GetMatrix()); // wystarczyłby wskaźnik, nie
|
|
// trzeba kopiować
|
|
// może trzeba: pobrać macierz dolnego ramienia, wyzerować
|
|
// przesunięcie, przemnożyć przez macierz górnego
|
|
pants[i].fParamPants->fHoriz = -fabs(m[3][1]);
|
|
pants[i].fParamPants->fLenL1 =
|
|
hypot(m[3][1], m[3][2]); // po osi OX nie potrzeba
|
|
pants[i].fParamPants->fAngleL0 =
|
|
atan2(fabs(m[3][2]), fabs(m[3][1]));
|
|
// if (pants[i].fParamPants->fAngleL0<M_PI_2)
|
|
// pants[i].fParamPants->fAngleL0+=M_PI; //gdyby w odwrotną
|
|
// stronę wyszło
|
|
// if
|
|
// ((pants[i].fParamPants->fAngleL0<0.03)||(pants[i].fParamPants->fAngleL0>0.09))
|
|
// //normalnie ok. 0.05
|
|
// pants[i].fParamPants->fAngleL0=pants[i].fParamPants->fAngleL;
|
|
pants[i].fParamPants->fAngleL =
|
|
pants[i].fParamPants->fAngleL0; // początkowy kąt dolnego
|
|
// ramienia
|
|
if ((sm = sm->ChildGet()) != NULL)
|
|
{ // jeśli dalej jest
|
|
// ślizg, można policzyć
|
|
// długość górnego
|
|
// ramienia
|
|
m = float4x4(*sm->GetMatrix()); // wystarczyłby wskaźnik,
|
|
// nie trzeba kopiować
|
|
// trzeba by uwzględnić macierz dolnego ramienia, żeby
|
|
// uzyskać kąt do poziomu...
|
|
pants[i].fParamPants->fHoriz +=
|
|
fabs(m(3)[1]); // różnica długości rzutów ramion na
|
|
// płaszczyznę podstawy (jedna dodatnia,
|
|
// druga ujemna)
|
|
pants[i].fParamPants->fLenU1 =
|
|
hypot(m[3][1], m[3][2]); // po osi OX nie potrzeba
|
|
// pants[i].fParamPants->pantu=acos((1.22*cos(pants[i].fParamPants->fAngleL)+0.535)/1.755);
|
|
// //górne ramię
|
|
// pants[i].fParamPants->fAngleU0=acos((1.176289*cos(pants[i].fParamPants->fAngleL)+0.54555075)/1.724482197);
|
|
// //górne ramię
|
|
pants[i].fParamPants->fAngleU0 =
|
|
atan2(fabs(m[3][2]),
|
|
fabs(m[3][1])); // początkowy kąt górnego
|
|
// ramienia, odczytany z modelu
|
|
// if (pants[i].fParamPants->fAngleU0<M_PI_2)
|
|
// pants[i].fParamPants->fAngleU0+=M_PI; //gdyby w odwrotną
|
|
// stronę wyszło
|
|
// if (pants[i].fParamPants->fAngleU0<0)
|
|
// pants[i].fParamPants->fAngleU0=-pants[i].fParamPants->fAngleU0;
|
|
// if
|
|
// ((pants[i].fParamPants->fAngleU0<0.00)||(pants[i].fParamPants->fAngleU0>0.09))
|
|
// //normalnie ok. 0.07
|
|
// pants[i].fParamPants->fAngleU0=acos((pants[i].fParamPants->fLenL1*cos(pants[i].fParamPants->fAngleL)+pants[i].fParamPants->fHoriz)/pants[i].fParamPants->fLenU1);
|
|
pants[i].fParamPants->fAngleU =
|
|
pants[i].fParamPants->fAngleU0; // początkowy kąt
|
|
// Ra: ze względu na to, że niektóre modele pantografów są
|
|
// zrąbane, ich mierzenie ma obecnie ograniczony sens
|
|
sm->ParentMatrix(&m); // pobranie macierzy transformacji
|
|
// pivota ślizgu względem wstawienia
|
|
// pojazdu
|
|
if ((mdModel->Flags() & 0x8000) == 0) // jeśli wczytano z
|
|
// T3D
|
|
m.InitialRotate(); // może być potrzebny dodatkowy
|
|
// obrót, jeśli wczytano z T3D, tzn.
|
|
// przed wykonaniem Init()
|
|
float det = Det(m);
|
|
if (fabs(det - 1.0) < 0.001) // dopuszczamy 1 promil błędu
|
|
// na skalowaniu ślizgu
|
|
{ // skalowanie jest w normie, można pobrać wymiary z modelu
|
|
pants[i].fParamPants->fHeight =
|
|
sm->MaxY(m); // przeliczenie maksimum wysokości
|
|
// wierzchołków względem macierzy
|
|
pants[i].fParamPants->fHeight -=
|
|
m[3][1]; // odjęcie wysokości pivota ślizgu
|
|
pants[i].fParamPants->vPos.x =
|
|
m[3][2]; // przy okazji odczytać z modelu pozycję w
|
|
// długości
|
|
// ErrorLog("Model OK: "+asModel+",
|
|
// height="+pants[i].fParamPants->fHeight);
|
|
// ErrorLog("Model OK: "+asModel+",
|
|
// pos.x="+pants[i].fParamPants->vPos.x);
|
|
}
|
|
else
|
|
{ // gdy ktoś przesadził ze skalowaniem
|
|
pants[i].fParamPants->fHeight =
|
|
0.0; // niech będzie odczyt z pantfactors:
|
|
ErrorLog("Bad model: " + asModel + ", scale of " +
|
|
AnsiString(sm->pName) + " is " +
|
|
AnsiString(100.0 * det) + "%");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
ErrorLog("Bad model: " + asFileName + " - missed submodel " +
|
|
asAnimName); // brak ramienia
|
|
}
|
|
}
|
|
else if (str == AnsiString("animpantrd2prefix:"))
|
|
{ // prefiks ramion dolnych 2
|
|
str = Parser->GetNextSymbol();
|
|
float4x4 m; // macierz do wyliczenia pozycji i wektora ruchu pantografu
|
|
TSubModel *sm;
|
|
if (pants)
|
|
for (int i = 0; i < iAnimType[ANIM_PANTS]; i++)
|
|
{ // Winger 160204: wyszukiwanie max 2 patykow o nazwie
|
|
// str*
|
|
asAnimName = str + AnsiString(i + 1);
|
|
sm = mdModel->GetFromName(asAnimName.c_str());
|
|
pants[i].smElement[1] = sm; // jak NULL, to nie będzie animowany
|
|
if (sm)
|
|
{ // w EP09 wywalało się tu z powodu NULL
|
|
sm->WillBeAnimated();
|
|
if (pants[i].fParamPants->vPos.y == 0.0)
|
|
{ // jeśli pierwsze ramię nie ustawiło tej wartości,
|
|
// próbować drugim
|
|
//!!!! docelowo zrobić niezależną animację ramion z każdej
|
|
// strony
|
|
m = float4x4(
|
|
*sm->GetMatrix()); // skopiowanie, bo będziemy mnożyć
|
|
m(3)[1] =
|
|
m[3][1] + 0.054; // w górę o wysokość ślizgu (na razie tak)
|
|
while (sm->Parent)
|
|
{
|
|
if (sm->Parent->GetMatrix())
|
|
m = *sm->Parent->GetMatrix() * m;
|
|
sm = sm->Parent;
|
|
}
|
|
pants[i].fParamPants->vPos.z =
|
|
m[3][0]; // przesunięcie w bok (asymetria)
|
|
pants[i].fParamPants->vPos.y =
|
|
m[3][1]; // przesunięcie w górę odczytane z modelu
|
|
}
|
|
}
|
|
else
|
|
ErrorLog("Bad model: " + asFileName + " - missed submodel " +
|
|
asAnimName); // brak ramienia
|
|
}
|
|
}
|
|
else if (str == AnsiString("animpantrg1prefix:"))
|
|
{ // prefiks ramion górnych 1
|
|
str = Parser->GetNextSymbol();
|
|
if (pants)
|
|
for (int i = 0; i < iAnimType[ANIM_PANTS]; i++)
|
|
{ // Winger 160204: wyszukiwanie max 2 patykow o nazwie
|
|
// str*
|
|
asAnimName = str + AnsiString(i + 1);
|
|
pants[i].smElement[2] = mdModel->GetFromName(asAnimName.c_str());
|
|
pants[i].smElement[2]->WillBeAnimated();
|
|
}
|
|
}
|
|
else if (str == AnsiString("animpantrg2prefix:"))
|
|
{ // prefiks ramion górnych 2
|
|
str = Parser->GetNextSymbol();
|
|
if (pants)
|
|
for (int i = 0; i < iAnimType[ANIM_PANTS]; i++)
|
|
{ // Winger 160204: wyszukiwanie max 2 patykow o nazwie
|
|
// str*
|
|
asAnimName = str + AnsiString(i + 1);
|
|
pants[i].smElement[3] = mdModel->GetFromName(asAnimName.c_str());
|
|
pants[i].smElement[3]->WillBeAnimated();
|
|
}
|
|
}
|
|
else if (str == AnsiString("animpantslprefix:"))
|
|
{ // prefiks ślizgaczy
|
|
str = Parser->GetNextSymbol();
|
|
if (pants)
|
|
for (int i = 0; i < iAnimType[ANIM_PANTS]; i++)
|
|
{ // Winger 160204: wyszukiwanie max 2 patykow o nazwie
|
|
// str*
|
|
asAnimName = str + AnsiString(i + 1);
|
|
pants[i].smElement[4] = mdModel->GetFromName(asAnimName.c_str());
|
|
pants[i].smElement[4]->WillBeAnimated();
|
|
pants[i].yUpdate = UpdatePant;
|
|
pants[i].fMaxDist = 300 * 300; // nie podnosić w większej odległości
|
|
pants[i].iNumber = i;
|
|
}
|
|
}
|
|
else if (str == AnsiString("pantfactors:"))
|
|
{ // Winger 010304:
|
|
// parametry pantografow
|
|
double pant1x = Parser->GetNextSymbol().ToDouble();
|
|
double pant2x = Parser->GetNextSymbol().ToDouble();
|
|
double pant1h = Parser->GetNextSymbol().ToDouble(); // wysokość pierwszego
|
|
// ślizgu
|
|
double pant2h = Parser->GetNextSymbol().ToDouble(); // wysokość drugiego ślizgu
|
|
if (pant1h > 0.5)
|
|
pant1h = pant2h; // tu może być zbyt duża wartość
|
|
if ((pant1x < 0) &&
|
|
(pant2x > 0)) // pierwsza powinna być dodatnia, a druga ujemna
|
|
{
|
|
pant1x = -pant1x;
|
|
pant2x = -pant2x;
|
|
}
|
|
if (pants)
|
|
for (int i = 0; i < iAnimType[ANIM_PANTS]; ++i)
|
|
{ // przepisanie współczynników do pantografów (na razie
|
|
// nie będzie lepiej)
|
|
pants[i].fParamPants->fAngleL =
|
|
pants[i].fParamPants->fAngleL0; // początkowy kąt dolnego ramienia
|
|
pants[i].fParamPants->fAngleU =
|
|
pants[i].fParamPants->fAngleU0; // początkowy kąt
|
|
// pants[i].fParamPants->PantWys=1.22*sin(pants[i].fParamPants->fAngleL)+1.755*sin(pants[i].fParamPants->fAngleU);
|
|
// //wysokość początkowa
|
|
// pants[i].fParamPants->PantWys=1.176289*sin(pants[i].fParamPants->fAngleL)+1.724482197*sin(pants[i].fParamPants->fAngleU);
|
|
// //wysokość początkowa
|
|
if (pants[i].fParamPants->fHeight == 0.0) // gdy jest
|
|
// nieprawdopodobna
|
|
// wartość (np. nie
|
|
// znaleziony ślizg)
|
|
{ // gdy pomiary modelu nie udały się, odczyt podanych parametrów
|
|
// z MMD
|
|
pants[i].fParamPants->vPos.x = (i & 1) ? pant2x : pant1x;
|
|
pants[i].fParamPants->fHeight =
|
|
(i & 1) ? pant2h :
|
|
pant1h; // wysokość ślizgu jest zapisana w MMD
|
|
}
|
|
pants[i].fParamPants->PantWys =
|
|
pants[i].fParamPants->fLenL1 * sin(pants[i].fParamPants->fAngleL) +
|
|
pants[i].fParamPants->fLenU1 * sin(pants[i].fParamPants->fAngleU) +
|
|
pants[i].fParamPants->fHeight; // wysokość początkowa
|
|
// pants[i].fParamPants->vPos.y=panty-panth-pants[i].fParamPants->PantWys;
|
|
// //np. 4.429-0.097=4.332=~4.335
|
|
// pants[i].fParamPants->vPos.z=0; //niezerowe dla pantografów
|
|
// asymetrycznych
|
|
pants[i].fParamPants->PantTraction = pants[i].fParamPants->PantWys;
|
|
pants[i].fParamPants->fWidth =
|
|
0.5 *
|
|
MoverParameters->EnginePowerSource.CollectorParameters
|
|
.CSW; // połowa szerokości ślizgu; jest w "Power: CSW="
|
|
}
|
|
}
|
|
else if (str == AnsiString("animpistonprefix:"))
|
|
{ // prefiks tłoczysk
|
|
// - na razie
|
|
// używamy modeli
|
|
// pantografów
|
|
str = Parser->GetNextSymbol();
|
|
for (int i = 1; i <= 2; i++)
|
|
{
|
|
// asAnimName=str+i;
|
|
// smPatykird1[i-1]=mdModel->GetFromName(asAnimName.c_str());
|
|
// smPatykird1[i-1]->WillBeAnimated();
|
|
}
|
|
}
|
|
else if (str == AnsiString("animconrodprefix:"))
|
|
{ // prefiks korbowodów - na
|
|
// razie używamy modeli
|
|
// pantografów
|
|
str = Parser->GetNextSymbol();
|
|
for (int i = 1; i <= 2; i++)
|
|
{
|
|
// asAnimName=str+i;
|
|
// smPatykirg1[i-1]=mdModel->GetFromName(asAnimName.c_str());
|
|
// smPatykirg1[i-1]->WillBeAnimated();
|
|
}
|
|
}
|
|
else if (str == AnsiString("pistonfactors:"))
|
|
{ // Ra: parametry
|
|
// silnika parowego
|
|
// (tłoka)
|
|
/* //Ra: tymczasowo wyłączone ze względu na porządkowanie animacji
|
|
pantografów
|
|
pant1x=Parser->GetNextSymbol().ToDouble(); //kąt przesunięcia
|
|
dla
|
|
pierwszego tłoka
|
|
pant2x=Parser->GetNextSymbol().ToDouble(); //kąt przesunięcia
|
|
dla
|
|
drugiego tłoka
|
|
panty=Parser->GetNextSymbol().ToDouble(); //długość korby (r)
|
|
panth=Parser->GetNextSymbol().ToDouble(); //długoś korbowodu
|
|
(k)
|
|
*/
|
|
MoverParameters->EnginePowerSource.PowerType =
|
|
SteamPower; // Ra: po chamsku, ale z CHK nie działa
|
|
}
|
|
else if (str == AnsiString("animreturnprefix:"))
|
|
{ // prefiks drążka
|
|
// mimośrodowego -
|
|
// na razie używamy
|
|
// modeli
|
|
// pantografów
|
|
str = Parser->GetNextSymbol();
|
|
for (int i = 1; i <= 2; i++)
|
|
{
|
|
// asAnimName=str+i;
|
|
// smPatykird2[i-1]=mdModel->GetFromName(asAnimName.c_str());
|
|
// smPatykird2[i-1]->WillBeAnimated();
|
|
}
|
|
}
|
|
else if (str == AnsiString("animexplinkprefix:")) // animreturnprefix:
|
|
{ // prefiks jarzma - na razie używamy modeli pantografów
|
|
str = Parser->GetNextSymbol();
|
|
for (int i = 1; i <= 2; i++)
|
|
{
|
|
// asAnimName=str+i;
|
|
// smPatykirg2[i-1]=mdModel->GetFromName(asAnimName.c_str());
|
|
// smPatykirg2[i-1]->WillBeAnimated();
|
|
}
|
|
}
|
|
else if (str == AnsiString("animpendulumprefix:"))
|
|
{ // prefiks wahaczy
|
|
str = Parser->GetNextSymbol();
|
|
asAnimName = "";
|
|
for (int i = 1; i <= 4; i++)
|
|
{ // McZapkie-050402: wyszukiwanie max 4
|
|
// wahaczy o nazwie str*
|
|
asAnimName = str + AnsiString(i);
|
|
smWahacze[i - 1] = mdModel->GetFromName(asAnimName.c_str());
|
|
smWahacze[i - 1]->WillBeAnimated();
|
|
}
|
|
str = Parser->GetNextSymbol().LowerCase();
|
|
if (str == AnsiString("pendulumamplitude:"))
|
|
fWahaczeAmp = Parser->GetNextSymbol().ToDouble();
|
|
}
|
|
else if (str == AnsiString("engineer:"))
|
|
{ // nazwa submodelu maszynisty
|
|
str = Parser->GetNextSymbol();
|
|
smMechanik0 = mdModel->GetFromName(str.c_str());
|
|
if (!smMechanik0)
|
|
{ // jak nie ma bez numerka, to może jest z
|
|
// numerkiem?
|
|
smMechanik0 = mdModel->GetFromName(AnsiString(str + "1").c_str());
|
|
smMechanik1 = mdModel->GetFromName(AnsiString(str + "2").c_str());
|
|
}
|
|
// aby dało się go obracać, musi mieć włączoną animację w T3D!
|
|
// if (!smMechanik1) //jeśli drugiego nie ma
|
|
// if (smMechanik0) //a jest pierwszy
|
|
// smMechanik0->WillBeAnimated(); //to będziemy go obracać
|
|
}
|
|
else if (str == AnsiString("animdoorprefix:"))
|
|
{ // nazwa animowanych drzwi
|
|
int i, j, k, m;
|
|
str = Parser->GetNextSymbol();
|
|
for (i = 0, j = 0; i < ANIM_DOORS; ++i)
|
|
j += iAnimType[i]; // zliczanie wcześniejszych animacji
|
|
for (i = 0; i < iAnimType[ANIM_DOORS]; ++i) // liczba drzwi
|
|
{ // NBMX wrzesien 2003: wyszukiwanie drzwi o nazwie str*
|
|
asAnimName = str + AnsiString(i + 1);
|
|
pAnimations[i + j].smAnimated =
|
|
mdModel->GetFromName(asAnimName.c_str()); // ustalenie submodelu
|
|
if (pAnimations[i + j].smAnimated)
|
|
{ //++iAnimatedDoors;
|
|
pAnimations[i + j].smAnimated->WillBeAnimated(); // wyłączenie
|
|
// optymalizacji
|
|
// transformu
|
|
switch (MoverParameters->DoorOpenMethod)
|
|
{ // od razu zapinamy
|
|
// potrzebny typ
|
|
// animacji
|
|
case 1:
|
|
pAnimations[i + j].yUpdate = UpdateDoorTranslate;
|
|
break;
|
|
case 2:
|
|
pAnimations[i + j].yUpdate = UpdateDoorRotate;
|
|
break;
|
|
case 3:
|
|
pAnimations[i + j].yUpdate = UpdateDoorFold;
|
|
break; // obrót 3 kolejnych submodeli
|
|
case 4:
|
|
pAnimations[i + j].yUpdate = UpdateDoorPlug;
|
|
break;
|
|
}
|
|
pAnimations[i + j].iNumber =
|
|
i; // parzyste działają inaczej niż nieparzyste
|
|
pAnimations[i + j].fMaxDist = 300 * 300; // drzwi to z daleka widać
|
|
pAnimations[i + j].fSpeed =
|
|
random(150); // oryginalny koncept z DoorSpeedFactor
|
|
pAnimations[i + j].fSpeed = (pAnimations[i + j].fSpeed + 100) / 100;
|
|
// Ra: te współczynniki są bez sensu, bo modyfikują wektor
|
|
// przesunięcia
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (str == AnsiString("sounds:")) // dzwieki
|
|
while (!Parser->EndOfFile && str != AnsiString("endsounds"))
|
|
{
|
|
str = Parser->GetNextSymbol().LowerCase();
|
|
if (str == AnsiString("wheel_clatter:")) // polozenia osi w/m srodka pojazdu
|
|
{
|
|
dSDist = Parser->GetNextSymbol().ToDouble();
|
|
for (int i = 0; i < iAxles; i++)
|
|
{
|
|
str = Parser->GetNextSymbol();
|
|
dWheelsPosition[i] = str.ToDouble();
|
|
str = Parser->GetNextSymbol().LowerCase();
|
|
if (str != AnsiString("end"))
|
|
rsStukot[i].Init(str.c_str(), dSDist, GetPosition().x,
|
|
GetPosition().y + dWheelsPosition[i], GetPosition().z,
|
|
true);
|
|
}
|
|
if (str != AnsiString("end"))
|
|
str = Parser->GetNextSymbol();
|
|
}
|
|
else if ((str == AnsiString("engine:")) &&
|
|
(MoverParameters->Power > 0)) // plik z dzwiekiem silnika,
|
|
// mnozniki i ofsety amp. i
|
|
// czest.
|
|
{
|
|
str = Parser->GetNextSymbol();
|
|
rsSilnik.Init(str.c_str(), Parser->GetNextSymbol().ToDouble(), GetPosition().x,
|
|
GetPosition().y, GetPosition().z, true, true);
|
|
if (rsSilnik.GetWaveTime() == 0)
|
|
ErrorLog("Missed sound: \"" + str + "\" for " + asFileName);
|
|
if (MoverParameters->EngineType == DieselEngine)
|
|
rsSilnik.AM = Parser->GetNextSymbol().ToDouble() /
|
|
(MoverParameters->Power + MoverParameters->nmax * 60);
|
|
else if (MoverParameters->EngineType == DieselElectric)
|
|
rsSilnik.AM =
|
|
Parser->GetNextSymbol().ToDouble() / (MoverParameters->Power * 3);
|
|
else
|
|
rsSilnik.AM = Parser->GetNextSymbol().ToDouble() /
|
|
(MoverParameters->Power + MoverParameters->nmax * 60 +
|
|
MoverParameters->Power + MoverParameters->Power);
|
|
rsSilnik.AA = Parser->GetNextSymbol().ToDouble();
|
|
rsSilnik.FM = Parser->GetNextSymbol().ToDouble(); // MoverParameters->nmax;
|
|
rsSilnik.FA = Parser->GetNextSymbol().ToDouble();
|
|
}
|
|
else if (((str == AnsiString("ventilator:")) &&
|
|
((MoverParameters->EngineType == ElectricSeriesMotor) ||
|
|
(MoverParameters->EngineType ==
|
|
ElectricInductionMotor)))) // plik z dzwiekiem
|
|
// wentylatora, mnozniki i
|
|
// ofsety amp. i czest.
|
|
{
|
|
str = Parser->GetNextSymbol();
|
|
rsWentylator.Init(str.c_str(), Parser->GetNextSymbol().ToDouble(),
|
|
GetPosition().x, GetPosition().y, GetPosition().z, true,
|
|
true);
|
|
rsWentylator.AM =
|
|
Parser->GetNextSymbol().ToDouble() / MoverParameters->RVentnmax;
|
|
rsWentylator.AA = Parser->GetNextSymbol().ToDouble();
|
|
rsWentylator.FM =
|
|
Parser->GetNextSymbol().ToDouble() / MoverParameters->RVentnmax;
|
|
rsWentylator.FA = Parser->GetNextSymbol().ToDouble();
|
|
}
|
|
else if ((str == AnsiString("transmission:")) &&
|
|
(MoverParameters->EngineType ==
|
|
ElectricSeriesMotor)) // plik z dzwiekiem, mnozniki i ofsety
|
|
// amp. i czest.
|
|
{
|
|
str = Parser->GetNextSymbol();
|
|
rsPrzekladnia.Init(str.c_str(), Parser->GetNextSymbol().ToDouble(),
|
|
GetPosition().x, GetPosition().y, GetPosition().z, true);
|
|
rsPrzekladnia.AM = 0.029;
|
|
rsPrzekladnia.AA = 0.1;
|
|
rsPrzekladnia.FM = 0.005;
|
|
rsPrzekladnia.FA = 1.0;
|
|
}
|
|
else if (str == AnsiString("brake:")) // plik z piskiem hamulca,
|
|
// mnozniki i ofsety amplitudy.
|
|
{
|
|
str = Parser->GetNextSymbol();
|
|
rsPisk.Init(str.c_str(), Parser->GetNextSymbol().ToDouble(), GetPosition().x,
|
|
GetPosition().y, GetPosition().z, true);
|
|
rsPisk.AM = Parser->GetNextSymbol().ToDouble();
|
|
rsPisk.AA = Parser->GetNextSymbol().ToDouble() * (105 - random(10)) / 100;
|
|
rsPisk.FM = 1.0;
|
|
rsPisk.FA = 0.0;
|
|
}
|
|
else if (str == AnsiString("brakeacc:")) // plik z przyspieszaczem
|
|
// (upust po zlapaniu
|
|
// hamowania)
|
|
{
|
|
str = Parser->GetNextSymbol();
|
|
// sBrakeAcc.Init(str.c_str(),Parser->GetNextSymbol().ToDouble(),GetPosition().x,GetPosition().y,GetPosition().z,true);
|
|
sBrakeAcc = TSoundsManager::GetFromName(str.c_str(), true);
|
|
bBrakeAcc = true;
|
|
// sBrakeAcc.AM=1.0;
|
|
// sBrakeAcc.AA=0.0;
|
|
// sBrakeAcc.FM=1.0;
|
|
// sBrakeAcc.FA=0.0;
|
|
}
|
|
else if (str == AnsiString("unbrake:")) // plik z piskiem hamulca,
|
|
// mnozniki i ofsety
|
|
// amplitudy.
|
|
{
|
|
str = Parser->GetNextSymbol();
|
|
rsUnbrake.Init(str.c_str(), Parser->GetNextSymbol().ToDouble(), GetPosition().x,
|
|
GetPosition().y, GetPosition().z, true);
|
|
rsUnbrake.AM = 1.0;
|
|
rsUnbrake.AA = 0.0;
|
|
rsUnbrake.FM = 1.0;
|
|
rsUnbrake.FA = 0.0;
|
|
}
|
|
else if (str == AnsiString("derail:")) // dzwiek przy wykolejeniu
|
|
{
|
|
str = Parser->GetNextSymbol();
|
|
rsDerailment.Init(str.c_str(), Parser->GetNextSymbol().ToDouble(),
|
|
GetPosition().x, GetPosition().y, GetPosition().z, true);
|
|
rsDerailment.AM = 1.0;
|
|
rsDerailment.AA = 0.0;
|
|
rsDerailment.FM = 1.0;
|
|
rsDerailment.FA = 0.0;
|
|
}
|
|
else if (str == AnsiString("dieselinc:")) // dzwiek przy wlazeniu na obroty
|
|
// woodwarda
|
|
{
|
|
str = Parser->GetNextSymbol();
|
|
rsDiesielInc.Init(str.c_str(), Parser->GetNextSymbol().ToDouble(),
|
|
GetPosition().x, GetPosition().y, GetPosition().z, true);
|
|
rsDiesielInc.AM = 1.0;
|
|
rsDiesielInc.AA = 0.0;
|
|
rsDiesielInc.FM = 1.0;
|
|
rsDiesielInc.FA = 0.0;
|
|
}
|
|
else if (str == AnsiString("curve:"))
|
|
{
|
|
str = Parser->GetNextSymbol();
|
|
rscurve.Init(str.c_str(), Parser->GetNextSymbol().ToDouble(), GetPosition().x,
|
|
GetPosition().y, GetPosition().z, true);
|
|
rscurve.AM = 1.0;
|
|
rscurve.AA = 0.0;
|
|
rscurve.FM = 1.0;
|
|
rscurve.FA = 0.0;
|
|
}
|
|
else if (str == AnsiString("horn1:")) // pliki z trabieniem
|
|
{
|
|
sHorn1.Load(Parser, GetPosition());
|
|
}
|
|
if (str == AnsiString("horn2:")) // pliki z trabieniem wysokoton.
|
|
{
|
|
sHorn2.Load(Parser, GetPosition());
|
|
if (iHornWarning)
|
|
iHornWarning = 2; // numer syreny do użycia po otrzymaniu sygnału do jazdy
|
|
}
|
|
if (str == AnsiString("departuresignal:")) // pliki z sygnalem odjazdu
|
|
{
|
|
sDepartureSignal.Load(Parser, GetPosition());
|
|
}
|
|
if (str == AnsiString("pantographup:")) // pliki dzwiekow pantografow
|
|
{
|
|
str = Parser->GetNextSymbol();
|
|
sPantUp.Init(str.c_str(), 50, GetPosition().x, GetPosition().y, GetPosition().z,
|
|
true);
|
|
sPantUp.AM = 50000;
|
|
sPantUp.AA = -1 * (105 - random(10)) / 100;
|
|
sPantUp.FM = 1.0;
|
|
sPantUp.FA = 0.0;
|
|
}
|
|
if (str == AnsiString("pantographdown:")) // pliki dzwiekow pantografow
|
|
{
|
|
str = Parser->GetNextSymbol();
|
|
sPantDown.Init(str.c_str(), 50, GetPosition().x, GetPosition().y,
|
|
GetPosition().z, true);
|
|
sPantDown.AM = 50000;
|
|
sPantDown.AA = -1 * (105 - random(10)) / 100;
|
|
sPantDown.FM = 1.0;
|
|
sPantDown.FA = 0.0;
|
|
}
|
|
else if (str == AnsiString("compressor:")) // pliki ze sprezarka
|
|
{
|
|
sCompressor.Load(Parser, GetPosition());
|
|
}
|
|
else if (str == AnsiString("converter:")) // pliki z przetwornica
|
|
{
|
|
// if (MoverParameters->EngineType==DieselElectric) //będzie
|
|
// modulowany?
|
|
sConverter.Load(Parser, GetPosition());
|
|
}
|
|
else if (str == AnsiString("turbo:")) // pliki z turbogeneratorem
|
|
{
|
|
sTurbo.Load(Parser, GetPosition());
|
|
}
|
|
else if (str == AnsiString("small-compressor:")) // pliki z przetwornica
|
|
{
|
|
sSmallCompressor.Load(Parser, GetPosition());
|
|
}
|
|
else if (str == AnsiString("dooropen:"))
|
|
{
|
|
str = Parser->GetNextSymbol();
|
|
rsDoorOpen.Init(str.c_str(), 50, GetPosition().x, GetPosition().y,
|
|
GetPosition().z, true);
|
|
rsDoorOpen.AM = 50000;
|
|
rsDoorOpen.AA = -1 * (105 - random(10)) / 100;
|
|
rsDoorOpen.FM = 1.0;
|
|
rsDoorOpen.FA = 0.0;
|
|
}
|
|
else if (str == AnsiString("doorclose:"))
|
|
{
|
|
str = Parser->GetNextSymbol();
|
|
rsDoorClose.Init(str.c_str(), 50, GetPosition().x, GetPosition().y,
|
|
GetPosition().z, true);
|
|
rsDoorClose.AM = 50000;
|
|
rsDoorClose.AA = -1 * (105 - random(10)) / 100;
|
|
rsDoorClose.FM = 1.0;
|
|
rsDoorClose.FA = 0.0;
|
|
}
|
|
}
|
|
else if (str == AnsiString("internaldata:")) // dalej nie czytaj
|
|
{
|
|
while (!Parser->EndOfFile)
|
|
{ // zbieranie informacji o kabinach
|
|
str = Parser->GetNextSymbol().LowerCase();
|
|
if (str == "cab0model:")
|
|
{
|
|
str = Parser->GetNextSymbol();
|
|
if (str != "none")
|
|
iCabs = 2;
|
|
}
|
|
else if (str == "cab1model:")
|
|
{
|
|
str = Parser->GetNextSymbol();
|
|
if (str != "none")
|
|
iCabs = 1;
|
|
}
|
|
else if (str == "cab2model:")
|
|
{
|
|
str = Parser->GetNextSymbol();
|
|
if (str != "none")
|
|
iCabs = 4;
|
|
}
|
|
}
|
|
Stop_InternalData = true;
|
|
}
|
|
}
|
|
// ABu 050205 - tego wczesniej nie bylo i uciekala pamiec:
|
|
delete Parser;
|
|
if (mdModel)
|
|
mdModel->Init(); // obrócenie modelu oraz optymalizacja, również zapisanie
|
|
// binarnego
|
|
if (mdLoad)
|
|
mdLoad->Init();
|
|
if (mdPrzedsionek)
|
|
mdPrzedsionek->Init();
|
|
if (mdLowPolyInt)
|
|
mdLowPolyInt->Init();
|
|
// sHorn2.CopyIfEmpty(sHorn1); //żeby jednak trąbił też drugim
|
|
Global::asCurrentTexturePath =
|
|
AnsiString(szTexturePath); // kiedyś uproszczone wnętrze mieszało tekstury nieba
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
void TDynamicObject::RadioStop()
|
|
{ // zatrzymanie pojazdu
|
|
if (Mechanik) // o ile ktoś go prowadzi
|
|
if (MoverParameters->SecuritySystem.RadioStop &&
|
|
MoverParameters->Radio) // jeśli pojazd ma RadioStop i jest on aktywny
|
|
Mechanik->PutCommand("Emergency_brake", 1.0, 1.0, &vPosition, stopRadio);
|
|
};
|
|
|
|
//---------------------------------------------------------------------------
|
|
void TDynamicObject::Damage(char flag)
|
|
{
|
|
if (flag & 1) //różnicówka nie robi nic
|
|
{
|
|
MoverParameters->MainSwitch(false);
|
|
MoverParameters->FuseOff();
|
|
}
|
|
else
|
|
{
|
|
}
|
|
|
|
if (flag & 2) //usterka sterowania
|
|
{
|
|
MoverParameters->StLinFlag = false;
|
|
if (MoverParameters->InitialCtrlDelay<100000000)
|
|
MoverParameters->InitialCtrlDelay += 100000001;
|
|
}
|
|
else
|
|
{
|
|
if (MoverParameters->InitialCtrlDelay>100000000)
|
|
MoverParameters->InitialCtrlDelay -= 100000001;
|
|
}
|
|
|
|
if (flag & 4) //blokada przetwornicy
|
|
{
|
|
MoverParameters->ConvOvldFlag = true;
|
|
}
|
|
else
|
|
{
|
|
}
|
|
|
|
if (flag & 8) //blokada sprezarki
|
|
{
|
|
if (MoverParameters->MinCompressor>0)
|
|
MoverParameters->MinCompressor -= 100000001;
|
|
if (MoverParameters->MaxCompressor>0)
|
|
MoverParameters->MaxCompressor -= 100000001;
|
|
}
|
|
else
|
|
{
|
|
if (MoverParameters->MinCompressor<0)
|
|
MoverParameters->MinCompressor += 100000001;
|
|
if (MoverParameters->MaxCompressor<0)
|
|
MoverParameters->MaxCompressor += 100000001;
|
|
}
|
|
|
|
if (flag & 16) //blokada wału
|
|
{
|
|
if (MoverParameters->CtrlDelay<100000000)
|
|
MoverParameters->CtrlDelay += 100000001;
|
|
if (MoverParameters->CtrlDownDelay<100000000)
|
|
MoverParameters->CtrlDownDelay += 100000001;
|
|
}
|
|
else
|
|
{
|
|
if (MoverParameters->CtrlDelay>100000000)
|
|
MoverParameters->CtrlDelay -= 100000001;
|
|
if (MoverParameters->CtrlDownDelay>100000000)
|
|
MoverParameters->CtrlDownDelay -= 100000001;
|
|
}
|
|
|
|
if (flag & 32) //hamowanie nagŁe
|
|
{
|
|
}
|
|
else
|
|
{
|
|
}
|
|
|
|
MoverParameters->EngDmgFlag = flag;
|
|
};
|
|
|
|
void TDynamicObject::RaLightsSet(int head, int rear)
|
|
{ // zapalenie świateł z przodu i z
|
|
// tyłu, zależne od kierunku
|
|
// pojazdu
|
|
if (!MoverParameters)
|
|
return; // może tego nie być na początku
|
|
if (rear == 2 + 32 + 64)
|
|
{ // jeśli koniec pociągu, to trzeba ustalić, czy
|
|
// jest tam czynna lokomotywa
|
|
// EN57 może nie mieć końcówek od środka członu
|
|
if (MoverParameters->Power > 1.0) // jeśli ma moc napędową
|
|
if (!MoverParameters->ActiveDir) // jeśli nie ma ustawionego kierunku
|
|
{ // jeśli ma zarówno światła jak i końcówki, ustalić, czy jest w stanie
|
|
// aktywnym
|
|
// np. lokomotywa na zimno będzie mieć końcówki a nie światła
|
|
rear = 64; // tablice blaszane
|
|
// trzeba to uzależnić od "załączenia baterii" w pojeździe
|
|
}
|
|
if (rear == 2 + 32 + 64) // jeśli nadal obydwie możliwości
|
|
if (iInventory &
|
|
(iDirection ? 0x2A : 0x15)) // czy ma jakieś światła czerowone od danej strony
|
|
rear = 2 + 32; // dwa światła czerwone
|
|
else
|
|
rear = 64; // tablice blaszane
|
|
}
|
|
if (iDirection) // w zależności od kierunku pojazdu w składzie
|
|
{ // jesli pojazd stoi sprzęgiem 0 w stronę czoła
|
|
if (head >= 0)
|
|
iLights[0] = head;
|
|
if (rear >= 0)
|
|
iLights[1] = rear;
|
|
}
|
|
else
|
|
{ // jak jest odwrócony w składzie (-1), to zapalamy odwrotnie
|
|
if (head >= 0)
|
|
iLights[1] = head;
|
|
if (rear >= 0)
|
|
iLights[0] = rear;
|
|
}
|
|
};
|
|
|
|
int TDynamicObject::DirectionSet(int d)
|
|
{ // ustawienie kierunku w składzie (wykonuje AI)
|
|
iDirection = d > 0 ? 1 : 0; // d:1=zgodny,-1=przeciwny; iDirection:1=zgodny,0=przeciwny;
|
|
CouplCounter = 20; //żeby normalnie skanować kolizje, to musi ruszyć z miejsca
|
|
if (MyTrack)
|
|
{ // podczas wczytywania wstawiane jest AI, ale może jeszcze nie
|
|
// być toru
|
|
// AI ustawi kierunek ponownie po uruchomieniu silnika
|
|
if (iDirection) // jeśli w kierunku Coupler 0
|
|
{
|
|
if (MoverParameters->Couplers[0].CouplingFlag ==
|
|
ctrain_virtual) // brak pojazdu podpiętego?
|
|
ABuScanObjects(1, 300); // szukanie czegoś do podłączenia
|
|
}
|
|
else if (MoverParameters->Couplers[1].CouplingFlag ==
|
|
ctrain_virtual) // brak pojazdu podpiętego?
|
|
ABuScanObjects(-1, 300);
|
|
}
|
|
return 1 - (iDirection ? NextConnectedNo : PrevConnectedNo); // informacja o położeniu
|
|
// następnego
|
|
};
|
|
|
|
TDynamicObject * TDynamicObject::PrevAny()
|
|
{ // wskaźnik na poprzedni,
|
|
// nawet wirtualny
|
|
return iDirection ? PrevConnected : NextConnected;
|
|
};
|
|
TDynamicObject * TDynamicObject::Prev()
|
|
{
|
|
if (MoverParameters->Couplers[iDirection ^ 1].CouplingFlag)
|
|
return iDirection ? PrevConnected : NextConnected;
|
|
return NULL; // gdy sprzęg wirtualny, to jakby nic nie było
|
|
};
|
|
TDynamicObject * TDynamicObject::Next()
|
|
{
|
|
if (MoverParameters->Couplers[iDirection].CouplingFlag)
|
|
return iDirection ? NextConnected : PrevConnected;
|
|
return NULL; // gdy sprzęg wirtualny, to jakby nic nie było
|
|
};
|
|
TDynamicObject * TDynamicObject::PrevC(int C)
|
|
{
|
|
if (MoverParameters->Couplers[iDirection ^ 1].CouplingFlag & C)
|
|
return iDirection ? PrevConnected : NextConnected;
|
|
return NULL; // gdy sprzęg wirtualny, to jakby nic nie było
|
|
};
|
|
TDynamicObject * TDynamicObject::NextC(int C)
|
|
{
|
|
if (MoverParameters->Couplers[iDirection].CouplingFlag & C)
|
|
return iDirection ? NextConnected : PrevConnected;
|
|
return NULL; // gdy sprzęg inny, to jakby nic nie było
|
|
};
|
|
double TDynamicObject::NextDistance(double d)
|
|
{ // ustalenie odległości do
|
|
// następnego pojazdu, potrzebne
|
|
// do wstecznego skanowania
|
|
if (!MoverParameters->Couplers[iDirection].Connected)
|
|
return d; // jeśli nic nie ma, zwrócenie domyślnej wartości
|
|
if ((d <= 0.0) || (MoverParameters->Couplers[iDirection].CoupleDist < d))
|
|
return MoverParameters->Couplers[iDirection].Dist;
|
|
else
|
|
return d;
|
|
};
|
|
|
|
TDynamicObject * TDynamicObject::Neightbour(int &dir)
|
|
{ // ustalenie następnego (1) albo poprzedniego (0) w składzie bez
|
|
// względu na prawidłowość
|
|
// iDirection
|
|
int d = dir; // zapamiętanie kierunku
|
|
dir = 1 - (dir ? NextConnectedNo : PrevConnectedNo); // nowa wartość
|
|
return (d ? (MoverParameters->Couplers[1].CouplingFlag ? NextConnected : NULL) :
|
|
(MoverParameters->Couplers[0].CouplingFlag ? PrevConnected : NULL));
|
|
};
|
|
|
|
void TDynamicObject::CoupleDist()
|
|
{ // obliczenie odległości sprzęgów
|
|
if (MyTrack ? (MyTrack->iCategoryFlag & 1) :
|
|
true) // jeśli nie ma przypisanego toru, to liczyć jak dla kolei
|
|
{ // jeśli jedzie po szynach (również unimog), liczenie kul wystarczy
|
|
MoverParameters->SetCoupleDist();
|
|
}
|
|
else
|
|
{ // na drodze trzeba uwzględnić wektory ruchu
|
|
double d0 = MoverParameters->Couplers[0].CoupleDist;
|
|
// double d1=MoverParameters->Couplers[1].CoupleDist; //sprzęg z tyłu
|
|
// samochodu można olać,
|
|
// dopóki nie jeździ na wstecznym
|
|
vector3 p1, p2;
|
|
double d, w; // dopuszczalny dystans w poprzek
|
|
MoverParameters->SetCoupleDist(); // liczenie standardowe
|
|
if (MoverParameters->Couplers[0].Connected) // jeśli cokolwiek podłączone
|
|
if (MoverParameters->Couplers[0].CouplingFlag == 0) // jeśli wirtualny
|
|
if (MoverParameters->Couplers[0].CoupleDist < 300.0) // i mniej niż 300m
|
|
{ // przez MoverParameters->Couplers[0].Connected nie da się dostać do
|
|
// DynObj, stąd
|
|
// prowizorka
|
|
// WriteLog("Collision of
|
|
// "+AnsiString(MoverParameters->Couplers[0].CoupleDist)+"m detected
|
|
// by
|
|
// "+asName+":0.");
|
|
w = 0.5 * (MoverParameters->Couplers[0].Connected->Dim.W +
|
|
MoverParameters->Dim.W); // minimalna odległość minięcia
|
|
d = -DotProduct(vLeft, vCoulpler[0]); // odległość prostej ruchu od początku
|
|
// układu współrzędnych
|
|
d = fabs(
|
|
DotProduct(vLeft,
|
|
((TMoverParameters *)(MoverParameters->Couplers[0].Connected))
|
|
->vCoulpler[MoverParameters->Couplers[0].ConnectedNr]) +
|
|
d);
|
|
// WriteLog("Distance "+AnsiString(d)+"m from "+asName+":0.");
|
|
if (d > w)
|
|
MoverParameters->Couplers[0].CoupleDist =
|
|
(d0 < 10 ? 50 : d0); // przywrócenie poprzedniej
|
|
}
|
|
if (MoverParameters->Couplers[1].Connected) // jeśli cokolwiek podłączone
|
|
if (MoverParameters->Couplers[1].CouplingFlag == 0) // jeśli wirtualny
|
|
if (MoverParameters->Couplers[1].CoupleDist < 300.0) // i mniej niż 300m
|
|
{
|
|
// WriteLog("Collision of
|
|
// "+AnsiString(MoverParameters->Couplers[1].CoupleDist)+"m detected
|
|
// by
|
|
// "+asName+":1.");
|
|
w = 0.5 * (MoverParameters->Couplers[1].Connected->Dim.W +
|
|
MoverParameters->Dim.W); // minimalna odległość minięcia
|
|
d = -DotProduct(vLeft, vCoulpler[1]); // odległość prostej ruchu od początku
|
|
// układu współrzędnych
|
|
d = fabs(
|
|
DotProduct(vLeft,
|
|
((TMoverParameters *)(MoverParameters->Couplers[1].Connected))
|
|
->vCoulpler[MoverParameters->Couplers[1].ConnectedNr]) +
|
|
d);
|
|
// WriteLog("Distance "+AnsiString(d)+"m from "+asName+":1.");
|
|
if (d > w)
|
|
MoverParameters->Couplers[0].CoupleDist =
|
|
(d0 < 10 ? 50 : d0); // przywrócenie poprzedniej
|
|
}
|
|
}
|
|
};
|
|
|
|
TDynamicObject * TDynamicObject::ControlledFind()
|
|
{ // taka proteza:
|
|
// chcę podłączyć
|
|
// kabinę EN57
|
|
// bezpośrednio z
|
|
// silnikowym, aby
|
|
// nie robić tego
|
|
// przez
|
|
// ukrotnienie
|
|
// drugi silnikowy i tak musi być ukrotniony, podobnie jak kolejna jednostka
|
|
// lepiej by było przesyłać komendy sterowania, co jednak wymaga przebudowy
|
|
// transmisji komend
|
|
// (LD)
|
|
// problem się robi ze światłami, które będą zapalane w silnikowym, ale muszą
|
|
// świecić się w
|
|
// rozrządczych
|
|
// dla EZT światłą czołowe będą "zapalane w silnikowym", ale widziane z
|
|
// rozrządczych
|
|
// również wczytywanie MMD powinno dotyczyć aktualnego członu
|
|
// problematyczna może być kwestia wybranej kabiny (w silnikowym...)
|
|
// jeśli silnikowy będzie zapięty odwrotnie (tzn. -1), to i tak powinno
|
|
// jeździć dobrze
|
|
// również hamowanie wykonuje się zaworem w członie, a nie w silnikowym...
|
|
TDynamicObject *d = this; // zaczynamy od aktualnego
|
|
if (d->MoverParameters->TrainType & dt_EZT) // na razie dotyczy to EZT
|
|
if (d->NextConnected ? d->MoverParameters->Couplers[1].AllowedFlag & ctrain_depot : false)
|
|
{ // gdy jest człon od sprzęgu 1, a sprzęg łączony
|
|
// warsztatowo (powiedzmy)
|
|
if ((d->MoverParameters->Power < 1.0) && (d->NextConnected->MoverParameters->Power >
|
|
1.0)) // my nie mamy mocy, ale ten drugi ma
|
|
d = d->NextConnected; // będziemy sterować tym z mocą
|
|
}
|
|
else if (d->PrevConnected ? d->MoverParameters->Couplers[0].AllowedFlag & ctrain_depot :
|
|
false)
|
|
{ // gdy jest człon od sprzęgu 0, a sprzęg łączony
|
|
// warsztatowo (powiedzmy)
|
|
if ((d->MoverParameters->Power < 1.0) && (d->PrevConnected->MoverParameters->Power >
|
|
1.0)) // my nie mamy mocy, ale ten drugi ma
|
|
d = d->PrevConnected; // będziemy sterować tym z mocą
|
|
}
|
|
return d;
|
|
};
|
|
//---------------------------------------------------------------------------
|
|
|
|
void TDynamicObject::ParamSet(int what, int into)
|
|
{ // ustawienie lokalnego parametru (what) na stan (into)
|
|
switch (what & 0xFF00)
|
|
{
|
|
case 0x0100: // to np. są drzwi, bity 0..7 określają numer 1..254 albo maskę
|
|
// dla 8 różnych
|
|
if (what & 1) // na razie mamy lewe oraz prawe, czyli używamy maskę 1=lewe,
|
|
// 2=prawe, 3=wszystkie
|
|
if (MoverParameters->DoorLeftOpened)
|
|
{ // są otwarte
|
|
if (!into) // jeśli zamykanie
|
|
{
|
|
// dźwięk zamykania
|
|
}
|
|
}
|
|
else
|
|
{ // są zamknięte
|
|
if (into) // jeśli otwieranie
|
|
{
|
|
// dźwięk otwierania
|
|
}
|
|
}
|
|
if (what & 2) // prawe działają niezależnie od lewych
|
|
if (MoverParameters->DoorRightOpened)
|
|
{ // są otwarte
|
|
if (!into) // jeśli zamykanie
|
|
{
|
|
// dźwięk zamykania
|
|
}
|
|
}
|
|
else
|
|
{ // są zamknięte
|
|
if (into) // jeśli otwieranie
|
|
{
|
|
// dźwięk otwierania
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
};
|
|
|
|
int TDynamicObject::RouteWish(TTrack *tr)
|
|
{ // zapytanie do AI, po którym
|
|
// segmencie (-6..6) jechać na
|
|
// skrzyżowaniu (tr)
|
|
return Mechanik ? Mechanik->CrossRoute(tr) : 0; // wg AI albo prosto
|
|
};
|
|
|
|
AnsiString TDynamicObject::TextureTest(AnsiString &name)
|
|
{ // Ra 2015-01: sprawdzenie dostępności tekstury o podanej nazwie
|
|
AnsiString x = name + ".dds"; // na razie prymitywnie
|
|
if (FileExists(x))
|
|
return x;
|
|
else
|
|
{
|
|
x = name + ".tga"; // w zasadzie to należałoby uwzględnić deklarowaną kolejność
|
|
if (FileExists(x))
|
|
return x;
|
|
else
|
|
{
|
|
x = name + ".bmp";
|
|
if (FileExists(x))
|
|
return x;
|
|
}
|
|
}
|
|
return ""; // nie znaleziona
|
|
};
|
|
|
|
void TDynamicObject::DestinationSet(AnsiString to)
|
|
{ // ustawienie stacji
|
|
// docelowej oraz wymiennej
|
|
// tekstury 4, jeśli
|
|
// istnieje plik
|
|
// w zasadzie, to każdy wagon mógłby mieć inną stację docelową
|
|
// zwłaszcza w towarowych, pod kątem zautomatyzowania maewrów albo pracy górki
|
|
// ale to jeszcze potrwa, zanim będzie możliwe, na razie można wpisać stację z
|
|
// rozkładu
|
|
if (abs(iMultiTex) >= 4)
|
|
return; // jak są 4 tekstury wymienne, to nie zmieniać rozkładem
|
|
asDestination = to;
|
|
to = Global::Bezogonkow(to); // do szukania pliku obcinamy ogonki
|
|
AnsiString x;
|
|
if (to.IsEmpty())
|
|
to = "nowhere";
|
|
x = TextureTest(asBaseDir + to + "@" +
|
|
MoverParameters->TypeName); // w pierwszej kolejności z nazwą FIZ/MMD
|
|
if (!x.IsEmpty())
|
|
{
|
|
ReplacableSkinID[4] = TTexturesManager::GetTextureID(
|
|
NULL, NULL, x.c_str(), 9); // rozmywania 0,1,4,5 nie nadają się
|
|
return;
|
|
}
|
|
x = TextureTest(asBaseDir + to); // na razie prymitywnie
|
|
if (!x.IsEmpty())
|
|
ReplacableSkinID[4] = TTexturesManager::GetTextureID(
|
|
NULL, NULL, x.c_str(), 9); // rozmywania 0,1,4,5 nie nadają się
|
|
else
|
|
ReplacableSkinID[4] = 0; // 0 to brak? -1 odpada, bo inaczej się będzie mapować
|
|
// Ra 2015-01: żeby zalogować błąd, trzeba by mieć pewność, że model używa
|
|
// tekstury nr 4
|
|
};
|
|
|
|
void TDynamicObject::OverheadTrack(float o)
|
|
{ // ewentualne wymuszanie jazdy
|
|
// bezprądowej z powodu informacji
|
|
// w torze
|
|
if (ctOwner) // jeśli ma obiekt nadzorujący
|
|
{ // trzeba zaktualizować mapę flag bitowych jazdy bezprądowej
|
|
if (o < 0.0)
|
|
{ // normalna jazda po tym torze
|
|
ctOwner->iOverheadZero &= ~iOverheadMask; // zerowanie bitu - może pobierać prąd
|
|
ctOwner->iOverheadDown &= ~iOverheadMask; // zerowanie bitu - może podnieść pantograf
|
|
}
|
|
else if (o > 0.0)
|
|
{ // opuszczenie pantografów
|
|
ctOwner->iOverheadZero |=
|
|
iOverheadMask; // ustawienie bitu - ma jechać bez pobierania prądu
|
|
ctOwner->iOverheadDown |= iOverheadMask; // ustawienie bitu - ma opuścić pantograf
|
|
}
|
|
else
|
|
{ // jazda bezprądowa z podniesionym pantografem
|
|
ctOwner->iOverheadZero |=
|
|
iOverheadMask; // ustawienie bitu - ma jechać bez pobierania prądu
|
|
ctOwner->iOverheadDown &= ~iOverheadMask; // zerowanie bitu - może podnieść pantograf
|
|
}
|
|
}
|
|
};
|