mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-07-25 00:49:18 +02:00
minor ai logic enhancements, minor bug fixes
This commit is contained in:
158
Driver.cpp
158
Driver.cpp
@@ -2256,11 +2256,9 @@ double TController::BrakeAccFactor() const
|
|||||||
|
|
||||||
void TController::SetDriverPsyche()
|
void TController::SetDriverPsyche()
|
||||||
{
|
{
|
||||||
// double maxdist=0.5; //skalowanie dystansu od innego pojazdu, zmienic to!!!
|
|
||||||
if ((Psyche == Aggressive) && (OrderList[OrderPos] == Obey_train))
|
if ((Psyche == Aggressive) && (OrderList[OrderPos] == Obey_train))
|
||||||
{
|
{
|
||||||
ReactionTime = HardReactionTime; // w zaleznosci od charakteru maszynisty
|
ReactionTime = HardReactionTime; // w zaleznosci od charakteru maszynisty
|
||||||
// if (pOccupied)
|
|
||||||
if (mvOccupied->CategoryFlag & 2)
|
if (mvOccupied->CategoryFlag & 2)
|
||||||
{
|
{
|
||||||
WaitingExpireTime = 1; // tyle ma czekać samochód, zanim się ruszy
|
WaitingExpireTime = 1; // tyle ma czekać samochód, zanim się ruszy
|
||||||
@@ -2292,48 +2290,6 @@ void TController::SetDriverPsyche()
|
|||||||
ReactionTime = mvControlling->InitialCtrlDelay + ReactionTime;
|
ReactionTime = mvControlling->InitialCtrlDelay + ReactionTime;
|
||||||
if (mvOccupied->BrakeCtrlPos > 1)
|
if (mvOccupied->BrakeCtrlPos > 1)
|
||||||
ReactionTime = 0.5 * ReactionTime;
|
ReactionTime = 0.5 * ReactionTime;
|
||||||
/*
|
|
||||||
if (mvOccupied->Vel>0.1) //o ile jedziemy
|
|
||||||
{//sprawdzenie jazdy na widoczność
|
|
||||||
TCoupling
|
|
||||||
*c=pVehicles[0]->MoverParameters->Couplers+(pVehicles[0]->DirectionGet()>0?0:1); //sprzęg
|
|
||||||
z przodu składu
|
|
||||||
if (c->Connected) //a mamy coś z przodu
|
|
||||||
if (c->CouplingFlag==0) //jeśli to coś jest podłączone sprzęgiem wirtualnym
|
|
||||||
{//wyliczanie optymalnego przyspieszenia do jazdy na widoczność (Ra: na pewno tutaj?)
|
|
||||||
double k=c->Connected->Vel; //prędkość pojazdu z przodu (zakładając, że jedzie w tę
|
|
||||||
samą stronę!!!)
|
|
||||||
if (k<=mvOccupied->Vel) //porównanie modułów prędkości [km/h]
|
|
||||||
{if (pVehicles[0]->fTrackBlock<fMaxProximityDist) //porównianie z minimalną odległością
|
|
||||||
kolizyjną
|
|
||||||
k=-AccPreferred; //hamowanie maksymalne, bo jest za blisko
|
|
||||||
else
|
|
||||||
{//jeśli tamten jedzie szybciej, to nie potrzeba modyfikować przyspieszenia
|
|
||||||
double d=(pVehicles[0]->fTrackBlock-0.5*fabs(mvOccupied->V)-fMaxProximityDist);
|
|
||||||
//bezpieczna odległość za poprzednim
|
|
||||||
//a=(v2*v2-v1*v1)/(25.92*(d-0.5*v1))
|
|
||||||
//(v2*v2-v1*v1)/2 to różnica energii kinetycznych na jednostkę masy
|
|
||||||
//jeśli v2=50km/h,v1=60km/h,d=200m => k=(192.9-277.8)/(25.92*(200-0.5*16.7)=-0.0171
|
|
||||||
[m/s^2]
|
|
||||||
//jeśli v2=50km/h,v1=60km/h,d=100m => k=(192.9-277.8)/(25.92*(100-0.5*16.7)=-0.0357
|
|
||||||
[m/s^2]
|
|
||||||
//jeśli v2=50km/h,v1=60km/h,d=50m => k=(192.9-277.8)/(25.92*( 50-0.5*16.7)=-0.0786
|
|
||||||
[m/s^2]
|
|
||||||
//jeśli v2=50km/h,v1=60km/h,d=25m => k=(192.9-277.8)/(25.92*( 25-0.5*16.7)=-0.1967
|
|
||||||
[m/s^2]
|
|
||||||
if (d>0) //bo jak ujemne, to zacznie przyspieszać, aby się zderzyć
|
|
||||||
k=(k*k-mvOccupied->Vel*mvOccupied->Vel)/(25.92*d); //energia kinetyczna dzielona
|
|
||||||
przez masę i drogę daje przyspieszenie
|
|
||||||
else
|
|
||||||
k=0.0; //może lepiej nie przyspieszać -AccPreferred; //hamowanie
|
|
||||||
//WriteLog(pVehicle->asName+" "+AnsiString(k));
|
|
||||||
}
|
|
||||||
if (d<fBrakeDist) //bo z daleka nie ma co hamować
|
|
||||||
AccPreferred=Min0R(k,AccPreferred);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -2486,6 +2442,7 @@ bool TController::PrepareEngine()
|
|||||||
// jeśli dotychczas spał teraz nie ma powodu do stania
|
// jeśli dotychczas spał teraz nie ma powodu do stania
|
||||||
eStopReason = stopNone;
|
eStopReason = stopNone;
|
||||||
}
|
}
|
||||||
|
eAction = TAction::actUnknown;
|
||||||
iEngineActive = 1;
|
iEngineActive = 1;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -2495,56 +2452,84 @@ bool TController::PrepareEngine()
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
bool TController::ReleaseEngine()
|
// wyłączanie silnika (test wyłączenia, a część wykonawcza tylko jeśli steruje komputer)
|
||||||
{ // wyłączanie silnika (test wyłączenia, a część wykonawcza tylko jeśli steruje komputer)
|
bool TController::ReleaseEngine() {
|
||||||
|
|
||||||
bool OK = false;
|
bool OK = false;
|
||||||
LastReactionTime = 0.0;
|
|
||||||
ReactionTime = PrepareTime;
|
if( mvOccupied->Vel > 0.01 ) {
|
||||||
if (AIControllFlag)
|
// TBD, TODO: make a dedicated braking procedure out of it for potential reuse
|
||||||
{ // jeśli steruje komputer
|
VelDesired = 0.0;
|
||||||
if (mvOccupied->DoorCloseCtrl == control_t::driver)
|
AccDesired = std::min( AccDesired, -1.25 ); // hamuj solidnie
|
||||||
{ // zamykanie drzwi
|
ReactionTime = 0.1;
|
||||||
if (mvOccupied->DoorLeftOpened)
|
while( DecSpeed( true ) ) {
|
||||||
mvOccupied->DoorLeft(false);
|
; // zerowanie nastawników
|
||||||
if (mvOccupied->DoorRightOpened)
|
|
||||||
mvOccupied->DoorRight(false);
|
|
||||||
}
|
}
|
||||||
if (mvOccupied->ActiveDir == 0)
|
IncBrake();
|
||||||
if (mvControlling->Mains)
|
return OK; // don't bother with the rest until we're standing still
|
||||||
{
|
|
||||||
mvControlling->CompressorSwitch(false);
|
|
||||||
mvControlling->ConverterSwitch(false);
|
|
||||||
if (mvControlling->EnginePowerSource.SourceType == TPowerSource::CurrentCollector)
|
|
||||||
{
|
|
||||||
mvControlling->PantFront(false);
|
|
||||||
mvControlling->PantRear(false);
|
|
||||||
}
|
|
||||||
// line breaker
|
|
||||||
OK = mvControlling->MainSwitch(false);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
OK = true;
|
|
||||||
}
|
|
||||||
else if( mvOccupied->ActiveDir == 0 ) {
|
|
||||||
// tylko to testujemy dla pojazdu człowieka
|
|
||||||
OK = mvControlling->Mains;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if( AIControllFlag ) {
|
LastReactionTime = 0.0;
|
||||||
|
ReactionTime = PrepareTime;
|
||||||
|
|
||||||
|
if( false == AIControllFlag ) {
|
||||||
|
// tylko to testujemy dla pojazdu człowieka
|
||||||
|
OK = ( ( mvOccupied->ActiveDir == 0 ) && ( mvControlling->Mains ) );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// jeśli steruje komputer
|
||||||
mvOccupied->BrakeReleaser( 0 );
|
mvOccupied->BrakeReleaser( 0 );
|
||||||
if( !mvOccupied->DecBrakeLevel() ) {
|
if( std::abs( fAccGravity ) < 0.01 ) {
|
||||||
|
// release train brake if on flats...
|
||||||
|
// TODO: check if we shouldn't leave it engaged instead
|
||||||
|
while( true == mvOccupied->DecBrakeLevel() ) {
|
||||||
// tu moze zmieniać na -2, ale to bez znaczenia
|
// tu moze zmieniać na -2, ale to bez znaczenia
|
||||||
if( !mvOccupied->IncLocalBrakeLevel( 1 ) ) {
|
;
|
||||||
while( DecSpeed( true ) )
|
}
|
||||||
|
// ...and engage independent brake
|
||||||
|
while( true == mvOccupied->IncLocalBrakeLevel( 1 ) ) {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// on slopes engage train brake
|
||||||
|
AccDesired = std::min( AccDesired, -0.9 );
|
||||||
|
while( true == IncBrake() ) {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while( DecSpeed( true ) ) {
|
||||||
; // zerowanie nastawników
|
; // zerowanie nastawników
|
||||||
while( mvOccupied->ActiveDir > 0 )
|
}
|
||||||
mvOccupied->DirectionBackward();
|
// set direction to neutral
|
||||||
while( mvOccupied->ActiveDir < 0 )
|
while( ( mvOccupied->ActiveDir > 0 ) && ( mvOccupied->DirectionBackward() ) ) { ; }
|
||||||
mvOccupied->DirectionForward();
|
while( ( mvOccupied->ActiveDir < 0 ) && ( mvOccupied->DirectionForward() ) ) { ; }
|
||||||
|
|
||||||
|
if( mvOccupied->DoorCloseCtrl == control_t::driver ) {
|
||||||
|
// zamykanie drzwi
|
||||||
|
if( mvOccupied->DoorLeftOpened ) {
|
||||||
|
mvOccupied->DoorLeft( false );
|
||||||
|
}
|
||||||
|
if( mvOccupied->DoorRightOpened ) {
|
||||||
|
mvOccupied->DoorRight( false );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if( mvControlling->Mains ) {
|
||||||
|
mvControlling->CompressorSwitch( false );
|
||||||
|
mvControlling->ConverterSwitch( false );
|
||||||
|
// line breaker/engine
|
||||||
|
OK = mvControlling->MainSwitch( false );
|
||||||
|
if( mvControlling->EnginePowerSource.SourceType == TPowerSource::CurrentCollector ) {
|
||||||
|
mvControlling->PantFront( false );
|
||||||
|
mvControlling->PantRear( false );
|
||||||
}
|
}
|
||||||
OK = OK && (mvOccupied->Vel < 0.01);
|
}
|
||||||
|
else {
|
||||||
|
OK = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (OK)
|
if (OK)
|
||||||
{ // jeśli się zatrzymał
|
{ // jeśli się zatrzymał
|
||||||
iEngineActive = 0;
|
iEngineActive = 0;
|
||||||
@@ -4823,6 +4808,11 @@ TController::UpdateSituation(double dt) {
|
|||||||
VelDesired = 0.0;
|
VelDesired = 0.0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if( ( OrderCurrentGet() & Wait_for_orders ) != 0 ) {
|
||||||
|
// wait means sit and wait
|
||||||
|
VelDesired = 0.0;
|
||||||
|
}
|
||||||
// end of speed caps checks
|
// end of speed caps checks
|
||||||
|
|
||||||
if( ( ( OrderCurrentGet() & Obey_train ) != 0 )
|
if( ( ( OrderCurrentGet() & Obey_train ) != 0 )
|
||||||
|
|||||||
@@ -970,7 +970,8 @@ void TDynamicObject::ABuLittleUpdate(double ObjSqrDist)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if( ( Mechanik != nullptr )
|
if( ( Mechanik != nullptr )
|
||||||
&& ( Mechanik->GetAction() != TAction::actSleep ) ) {
|
&& ( ( Mechanik->GetAction() != TAction::actSleep )
|
||||||
|
|| ( MoverParameters->Battery ) ) ) {
|
||||||
// rysowanie figurki mechanika
|
// rysowanie figurki mechanika
|
||||||
btMechanik1.Turn( MoverParameters->ActiveCab > 0 );
|
btMechanik1.Turn( MoverParameters->ActiveCab > 0 );
|
||||||
btMechanik2.Turn( MoverParameters->ActiveCab < 0 );
|
btMechanik2.Turn( MoverParameters->ActiveCab < 0 );
|
||||||
|
|||||||
@@ -740,8 +740,7 @@ void TMoverParameters::UpdatePantVolume(double dt)
|
|||||||
// Ra 2013-12: Niebugocław mówi, że w EZT nie ma potrzeby odcinać kurkiem
|
// Ra 2013-12: Niebugocław mówi, że w EZT nie ma potrzeby odcinać kurkiem
|
||||||
PantPress = ScndPipePress;
|
PantPress = ScndPipePress;
|
||||||
// ograniczenie ciśnienia do MaxPress (tylko w pantografach!)
|
// ograniczenie ciśnienia do MaxPress (tylko w pantografach!)
|
||||||
PantPress = std::min( PantPress, EnginePowerSource.CollectorParameters.MaxPress );
|
PantPress = clamp( ScndPipePress, 0.0, EnginePowerSource.CollectorParameters.MaxPress );
|
||||||
PantPress = std::max( PantPress, 0.0 );
|
|
||||||
PantVolume = (PantPress + 1.0) * 0.1; // objętość, na wypadek odcięcia kurkiem
|
PantVolume = (PantPress + 1.0) * 0.1; // objętość, na wypadek odcięcia kurkiem
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -754,8 +753,7 @@ void TMoverParameters::UpdatePantVolume(double dt)
|
|||||||
* ( TrainType == dt_EZT ? 0.003 : 0.005 ) / std::max( 1.0, PantPress )
|
* ( TrainType == dt_EZT ? 0.003 : 0.005 ) / std::max( 1.0, PantPress )
|
||||||
* ( 0.45 - ( ( 0.1 / PantVolume / 10 ) - 0.1 ) ) / 0.45;
|
* ( 0.45 - ( ( 0.1 / PantVolume / 10 ) - 0.1 ) ) / 0.45;
|
||||||
}
|
}
|
||||||
PantPress = std::min( (10.0 * PantVolume) - 1.0, EnginePowerSource.CollectorParameters.MaxPress ); // tu by się przydała objętość zbiornika
|
PantPress = clamp( ( 10.0 * PantVolume ) - 1.0, 0.0, EnginePowerSource.CollectorParameters.MaxPress ); // tu by się przydała objętość zbiornika
|
||||||
PantPress = std::max( PantPress, 0.0 );
|
|
||||||
}
|
}
|
||||||
if( !PantCompFlag && ( PantVolume > 0.1 ) )
|
if( !PantCompFlag && ( PantVolume > 0.1 ) )
|
||||||
PantVolume -= dt * 0.0003 * std::max( 1.0, PantPress * 0.5 ); // nieszczelności: 0.0003=0.3l/s
|
PantVolume -= dt * 0.0003 * std::max( 1.0, PantPress * 0.5 ); // nieszczelności: 0.0003=0.3l/s
|
||||||
@@ -8512,8 +8510,8 @@ void TMoverParameters::LoadFIZ_PowerParamsDecode( TPowerParameters &Powerparamet
|
|||||||
//ciśnienie rozłączające WS
|
//ciśnienie rozłączające WS
|
||||||
extract_value( collectorparameters.MinPress, "MinPress", Line, "3.5" ); //domyślnie 2 bary do załączenia WS
|
extract_value( collectorparameters.MinPress, "MinPress", Line, "3.5" ); //domyślnie 2 bary do załączenia WS
|
||||||
//maksymalne ciśnienie za reduktorem
|
//maksymalne ciśnienie za reduktorem
|
||||||
collectorparameters.MaxPress = 5.0 + 0.001 * ( Random( 50 ) - Random( 50 ) );
|
// collectorparameters.MaxPress = 5.0 + 0.001 * ( Random( 50 ) - Random( 50 ) );
|
||||||
extract_value( collectorparameters.MaxPress, "MaxPress", Line, "" );
|
extract_value( collectorparameters.MaxPress, "MaxPress", Line, "5.0" );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case TPowerSource::PowerCable: {
|
case TPowerSource::PowerCable: {
|
||||||
|
|||||||
10
Model3d.cpp
10
Model3d.cpp
@@ -431,6 +431,12 @@ int TSubModel::Load( cParser &parser, TModel3d *Model, /*int Pos,*/ bool dynamic
|
|||||||
>> Vertices[i].normal.x
|
>> Vertices[i].normal.x
|
||||||
>> Vertices[i].normal.y
|
>> Vertices[i].normal.y
|
||||||
>> Vertices[i].normal.z;
|
>> Vertices[i].normal.z;
|
||||||
|
if( glm::length2( Vertices[ i ].normal ) > 0.0f ) {
|
||||||
|
glm::normalize( Vertices[ i ].normal );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
WriteLog( "Bad model: zero length normal vector specified in: \"" + pName + "\", vertex " + std::to_string(i), logtype::model );
|
||||||
|
}
|
||||||
wsp[i] = i; // wektory normalne "są już policzone"
|
wsp[i] = i; // wektory normalne "są już policzone"
|
||||||
}
|
}
|
||||||
parser.getTokens(2, false);
|
parser.getTokens(2, false);
|
||||||
@@ -464,7 +470,7 @@ int TSubModel::Load( cParser &parser, TModel3d *Model, /*int Pos,*/ bool dynamic
|
|||||||
/*
|
/*
|
||||||
glm::vec3 *n = new glm::vec3[iNumFaces]; // tablica wektorów normalnych dla trójkątów
|
glm::vec3 *n = new glm::vec3[iNumFaces]; // tablica wektorów normalnych dla trójkątów
|
||||||
*/
|
*/
|
||||||
std::vector<glm::vec3> facenormals;
|
std::vector<glm::vec3> facenormals; facenormals.reserve( facecount );
|
||||||
for( int i = 0; i < facecount; ++i ) {
|
for( int i = 0; i < facecount; ++i ) {
|
||||||
// pętla po trójkątach - będzie szybciej, jak wstępnie przeliczymy normalne trójkątów
|
// pętla po trójkątach - będzie szybciej, jak wstępnie przeliczymy normalne trójkątów
|
||||||
auto facenormal =
|
auto facenormal =
|
||||||
@@ -1127,7 +1133,7 @@ TSubModel *TModel3d::AddToNamed(const char *Name, TSubModel *SubModel)
|
|||||||
TSubModel *sm = Name ? GetFromName(Name) : nullptr;
|
TSubModel *sm = Name ? GetFromName(Name) : nullptr;
|
||||||
if( ( sm == nullptr )
|
if( ( sm == nullptr )
|
||||||
&& ( Name != nullptr ) && ( std::strcmp( Name, "none" ) != 0 ) ) {
|
&& ( Name != nullptr ) && ( std::strcmp( Name, "none" ) != 0 ) ) {
|
||||||
ErrorLog( "Bad model: parent for sub-model \"" + SubModel->pName +"\" doesn't exist or is located after in the model data", logtype::model );
|
ErrorLog( "Bad model: parent for sub-model \"" + SubModel->pName +"\" doesn't exist or is located later in the model data", logtype::model );
|
||||||
}
|
}
|
||||||
AddTo(sm, SubModel); // szukanie nadrzędnego
|
AddTo(sm, SubModel); // szukanie nadrzędnego
|
||||||
return sm; // zwracamy wskaźnik do nadrzędnego submodelu
|
return sm; // zwracamy wskaźnik do nadrzędnego submodelu
|
||||||
|
|||||||
@@ -571,12 +571,13 @@ driver_mode::OnKeyDown(int cKey) {
|
|||||||
simulation::Events.AddToQuery( KeyEvents[ i ], NULL );
|
simulation::Events.AddToQuery( KeyEvents[ i ], NULL );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else // zapamiętywanie kamery może działać podczas pauzy
|
else if( Global.ctrlState ) {
|
||||||
|
// zapamiętywanie kamery może działać podczas pauzy
|
||||||
if( FreeFlyModeFlag ) {
|
if( FreeFlyModeFlag ) {
|
||||||
// w trybie latania można przeskakiwać do ustawionych kamer
|
// w trybie latania można przeskakiwać do ustawionych kamer
|
||||||
if( ( !Global.FreeCameraInit[ i ].x )
|
if( ( Global.FreeCameraInit[ i ].x == 0.0 )
|
||||||
&& ( !Global.FreeCameraInit[ i ].y )
|
&& ( Global.FreeCameraInit[ i ].y == 0.0 )
|
||||||
&& ( !Global.FreeCameraInit[ i ].z ) ) {
|
&& ( Global.FreeCameraInit[ i ].z == 0.0 ) ) {
|
||||||
// jeśli kamera jest w punkcie zerowym, zapamiętanie współrzędnych i kątów
|
// jeśli kamera jest w punkcie zerowym, zapamiętanie współrzędnych i kątów
|
||||||
Global.FreeCameraInit[ i ] = Camera.Pos;
|
Global.FreeCameraInit[ i ] = Camera.Pos;
|
||||||
Global.FreeCameraInitAngle[ i ].x = Camera.Pitch;
|
Global.FreeCameraInitAngle[ i ].x = Camera.Pitch;
|
||||||
@@ -598,7 +599,7 @@ driver_mode::OnKeyDown(int cKey) {
|
|||||||
Camera.Init( Global.FreeCameraInit[ i ], Global.FreeCameraInitAngle[ i ], TCameraType::tp_Free ); // przestawienie
|
Camera.Init( Global.FreeCameraInit[ i ], Global.FreeCameraInitAngle[ i ], TCameraType::tp_Free ); // przestawienie
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// będzie jeszcze załączanie sprzęgów z [Ctrl]
|
}
|
||||||
}
|
}
|
||||||
else if( ( cKey >= GLFW_KEY_F1 ) && ( cKey <= GLFW_KEY_F12 ) )
|
else if( ( cKey >= GLFW_KEY_F1 ) && ( cKey <= GLFW_KEY_F12 ) )
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -38,7 +38,8 @@ opengl_material::deserialize_mapping( cParser &Input, int const Priority, bool c
|
|||||||
|
|
||||||
if( ( true == key.empty() ) || ( key == "}" ) ) { return false; }
|
if( ( true == key.empty() ) || ( key == "}" ) ) { return false; }
|
||||||
|
|
||||||
auto const value { Input.getToken<std::string>( true, "\n\r\t ,;" ) };
|
// NOTE: comma can be part of legacy file names, so we don't treat it as a separator here
|
||||||
|
auto const value { Input.getToken<std::string>( true, "\n\r\t ;" ) };
|
||||||
|
|
||||||
if( Priority != -1 ) {
|
if( Priority != -1 ) {
|
||||||
// regular attribute processing mode
|
// regular attribute processing mode
|
||||||
|
|||||||
Reference in New Issue
Block a user