Files
maszyna/DynObj.cpp
firleju c84bf7319b 1. Poprawione asynchrony.
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
2016-01-28 22:47:40 +01:00

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
}
}
};