fixed to CAutomobile and friends

This commit is contained in:
aap 2020-05-31 17:45:26 +02:00
parent e6b9c20131
commit bae3a3e72f
8 changed files with 65 additions and 63 deletions

View File

@ -3859,8 +3859,7 @@ CCam::Process_Debug(const CVector&, float, float, float)
if(FindPlayerVehicle()) if(FindPlayerVehicle())
FindPlayerVehicle()->Teleport(Source); FindPlayerVehicle()->Teleport(Source);
else else
CWorld::Players[CWorld::PlayerInFocus].m_pPed->GetPosition() = Source; CWorld::Players[CWorld::PlayerInFocus].m_pPed->SetPosition(Source);
} }
// stay inside sectors // stay inside sectors

View File

@ -223,6 +223,7 @@ public:
void SetRotate(float xAngle, float yAngle, float zAngle); void SetRotate(float xAngle, float yAngle, float zAngle);
void Rotate(float x, float y, float z); void Rotate(float x, float y, float z);
void RotateX(float x); void RotateX(float x);
void RotateY(float y);
void RotateZ(float z); void RotateZ(float z);
void Reorthogonalise(void); void Reorthogonalise(void);

View File

@ -59,6 +59,12 @@ CMatrix::RotateX(float x)
Rotate(x, 0.0f, 0.0f); Rotate(x, 0.0f, 0.0f);
} }
void
CMatrix::RotateY(float y)
{
Rotate(0.0f, y, 0.0f);
}
void void
CMatrix::RotateZ(float z) CMatrix::RotateZ(float z)
{ {

View File

@ -157,6 +157,13 @@ CSimpleModelInfo::SetupBigBuilding(void)
if(related) if(related)
m_lodDistances[2] = related->GetLargestLodDistance()/TheCamera.LODDistMultiplier; m_lodDistances[2] = related->GetLargestLodDistance()/TheCamera.LODDistMultiplier;
else else
#ifdef FIX_BUGS
if(strnicmp(GetName(), "lod", 3) == 0)
m_lodDistances[2] = 100.0f; m_lodDistances[2] = 100.0f;
else
m_lodDistances[2] = 0.0f;
#else
m_lodDistances[2] = 100.0f;
#endif
} }
} }

View File

@ -11,7 +11,7 @@ public:
bool m_isMuddy; bool m_isMuddy;
uintptr m_id; uintptr m_id;
int16 m_last; int16 m_last;
uint32 m_lastUpdate;; uint32 m_lastUpdate;
uint32 m_fadeStart; uint32 m_fadeStart;
uint32 m_fadeEnd; uint32 m_fadeEnd;
CVector m_pos[SKIDMARK_LENGTH]; CVector m_pos[SKIDMARK_LENGTH];

View File

@ -67,7 +67,7 @@ CAutomobile::CAutomobile(int32 id, uint8 CreatedBy)
CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(id); CVehicleModelInfo *mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(id);
m_fFireBlowUpTimer = 0.0f; m_fFireBlowUpTimer = 0.0f;
field_4E0 = 0; m_auto_unk1 = 0;
bTaxiLight = m_sAllTaxiLights; bTaxiLight = m_sAllTaxiLights;
bFixedColour = false; bFixedColour = false;
bBigWheels = false; bBigWheels = false;
@ -77,8 +77,8 @@ CAutomobile::CAutomobile(int32 id, uint8 CreatedBy)
pHandling = mod_HandlingManager.GetHandlingData((eHandlingId)mi->m_handlingId); pHandling = mod_HandlingManager.GetHandlingData((eHandlingId)mi->m_handlingId);
field_49C = 20.0f; m_auto_unused1 = 20.0f;
field_4D8 = 0; m_auto_unused2 = 0;
mi->ChooseVehicleColour(m_currentColour1, m_currentColour2); mi->ChooseVehicleColour(m_currentColour1, m_currentColour2);
@ -236,25 +236,18 @@ CAutomobile::ProcessControl(void)
// Improve grip of vehicles in certain cases // Improve grip of vehicles in certain cases
bool strongGrip1 = false; bool strongGrip1 = false;
bool strongGrip2 = false; bool strongGrip2 = false;
if(FindPlayerVehicle() && this != FindPlayerVehicle()){ if(FindPlayerVehicle() && this != FindPlayerVehicle() &&
switch(AutoPilot.m_nCarMission){ (AutoPilot.m_nCarMission == MISSION_RAMPLAYER_FARAWAY || AutoPilot.m_nCarMission == MISSION_RAMPLAYER_CLOSE ||
case MISSION_RAMPLAYER_FARAWAY: AutoPilot.m_nCarMission == MISSION_BLOCKPLAYER_FARAWAY || AutoPilot.m_nCarMission == MISSION_BLOCKPLAYER_CLOSE)){
case MISSION_RAMPLAYER_CLOSE:
case MISSION_BLOCKPLAYER_FARAWAY:
case MISSION_BLOCKPLAYER_CLOSE:
if(FindPlayerSpeed().Magnitude() > 0.3f){ if(FindPlayerSpeed().Magnitude() > 0.3f){
strongGrip1 = true; strongGrip1 = true;
if(FindPlayerSpeed().Magnitude() > 0.4f){ if(FindPlayerSpeed().Magnitude() > 0.4f &&
if(m_vecMoveSpeed.Magnitude() < 0.3f) m_vecMoveSpeed.Magnitude() < 0.3f)
strongGrip2 = true; strongGrip2 = true;
}else{ else if((GetPosition() - FindPlayerCoors()).Magnitude() > 50.0f)
if((GetPosition() - FindPlayerCoors()).Magnitude() > 50.0f)
strongGrip2 = true; strongGrip2 = true;
} }
} }
default: break;
}
}
if(bIsBus) if(bIsBus)
ProcessAutoBusDoors(); ProcessAutoBusDoors();
@ -596,7 +589,7 @@ CAutomobile::ProcessControl(void)
m_aSuspensionSpringRatio[i] = (m_aSuspensionSpringRatio[i]-wheelRadius)/(1.0f-wheelRadius); m_aSuspensionSpringRatio[i] = (m_aSuspensionSpringRatio[i]-wheelRadius)/(1.0f-wheelRadius);
} }
float fwdSpeed = DotProduct(m_vecMoveSpeed, GetForward()); float fwdSpeed = Abs(DotProduct(m_vecMoveSpeed, GetForward()));
CVector contactPoints[4]; // relative to model CVector contactPoints[4]; // relative to model
CVector contactSpeeds[4]; // speed at contact points CVector contactSpeeds[4]; // speed at contact points
CVector springDirections[4]; // normalized, in model space CVector springDirections[4]; // normalized, in model space
@ -689,7 +682,7 @@ CAutomobile::ProcessControl(void)
float brakeBiasFront = neutralHandling ? 1.0f : 2.0f*pHandling->fBrakeBias; float brakeBiasFront = neutralHandling ? 1.0f : 2.0f*pHandling->fBrakeBias;
float brakeBiasRear = neutralHandling ? 1.0f : 2.0f*(1.0f-pHandling->fBrakeBias); float brakeBiasRear = neutralHandling ? 1.0f : 2.0f*(1.0f-pHandling->fBrakeBias);
float tractionBiasFront = neutralHandling ? 1.0f : 2.0f*pHandling->fTractionBias; float tractionBiasFront = neutralHandling ? 1.0f : 2.0f*pHandling->fTractionBias;
float tractionBiasRear = neutralHandling ? 1.0f : 2.0f*(1.0f-pHandling->fTractionBias); float tractionBiasRear = neutralHandling ? 1.0f : 2.0f-tractionBiasFront;
// Count how many wheels are touching the ground // Count how many wheels are touching the ground
@ -1160,7 +1153,7 @@ CAutomobile::ProcessControl(void)
uint8 freq = Min(200.0f*suspShake*speed*2000.0f/m_fMass + 100.0f, 250.0f); uint8 freq = Min(200.0f*suspShake*speed*2000.0f/m_fMass + 100.0f, 250.0f);
CPad::GetPad(0)->StartShake(20000.0f*CTimer::GetTimeStep()/freq, freq); CPad::GetPad(0)->StartShake(20000.0f*CTimer::GetTimeStep()/freq, freq);
}else{ }else{
uint8 freq = Min(200.0f*surfShake*speed*2000.0f/m_fMass + 40.0f, 145.0f); uint8 freq = Min(200.0f*surfShake*speed*2000.0f/m_fMass + 40.0f, 150.0f);
CPad::GetPad(0)->StartShake(5000.0f*CTimer::GetTimeStep()/freq, freq); CPad::GetPad(0)->StartShake(5000.0f*CTimer::GetTimeStep()/freq, freq);
} }
} }
@ -1434,7 +1427,7 @@ CAutomobile::PreRender(void)
n = 6.0f*CWeather::Rain; n = 6.0f*CWeather::Rain;
for(j = 0; j <= n; j++) for(j = 0; j <= n; j++)
CParticle::AddParticle(PARTICLE_RAIN_SPLASHUP, CParticle::AddParticle(PARTICLE_RAIN_SPLASHUP,
c + CVector(CGeneral::GetRandomNumberInRange(-.04f, 0.4f), CGeneral::GetRandomNumberInRange(-.04f, 0.4f), 0.0f), c + CVector(CGeneral::GetRandomNumberInRange(-0.4f, 0.4f), CGeneral::GetRandomNumberInRange(-0.4f, 0.4f), 0.0f),
CVector(0.0f, 0.0f, 0.0f), CVector(0.0f, 0.0f, 0.0f),
nil, 0.0f, 0, 0, CGeneral::GetRandomNumber() & 1); nil, 0.0f, 0, 0, CGeneral::GetRandomNumber() & 1);
} }
@ -1622,11 +1615,9 @@ CAutomobile::PreRender(void)
break; break;
} }
if(GetModelIndex() == MI_RCBANDIT || GetModelIndex() == MI_DODO || if(GetModelIndex() != MI_RCBANDIT && GetModelIndex() != MI_DODO &&
GetModelIndex() == MI_RHINO) { GetModelIndex() != MI_RHINO) {
CShadows::StoreShadowForCar(this); // Process lights
return;
}
// Turn lights on/off // Turn lights on/off
bool shouldLightsBeOn = bool shouldLightsBeOn =
@ -1873,14 +1864,9 @@ CAutomobile::PreRender(void)
CPointLights::FOG_NONE, false); CPointLights::FOG_NONE, false);
} }
} }
}else{ }else if(GetStatus() != STATUS_ABANDONED && GetStatus() != STATUS_WRECKED){
// Lights off // Lights off
if(GetStatus() == STATUS_ABANDONED || GetStatus() == STATUS_WRECKED) {
CShadows::StoreShadowForCar(this);
return;
}
CVector lightPos = mi->m_positions[CAR_POS_TAILLIGHTS]; CVector lightPos = mi->m_positions[CAR_POS_TAILLIGHTS];
CVector lightR = GetMatrix() * lightPos; CVector lightR = GetMatrix() * lightPos;
CVector lightL = lightR; CVector lightL = lightR;
@ -1937,6 +1923,8 @@ CAutomobile::PreRender(void)
CCoronas::UpdateCoronaCoors((uintptr)this + 3, lightR, 50.0f*TheCamera.LODDistMultiplier, 0.0f); CCoronas::UpdateCoronaCoors((uintptr)this + 3, lightR, 50.0f*TheCamera.LODDistMultiplier, 0.0f);
} }
} }
// end of lights
}
CShadows::StoreShadowForCar(this); CShadows::StoreShadowForCar(this);
} }
@ -2507,7 +2495,7 @@ CAutomobile::TankControl(void)
float f = i/15.0f; float f = i/15.0f;
CParticle::AddParticle(PARTICLE_GUNSMOKE2, point1, CParticle::AddParticle(PARTICLE_GUNSMOKE2, point1,
shotDir*CGeneral::GetRandomNumberInRange(0.3f, 1.0f)*f, shotDir*CGeneral::GetRandomNumberInRange(0.3f, 1.0f)*f,
nil, CGeneral::GetRandomNumberInRange(0.5f, 1.0f)*f, black); nil, CGeneral::GetRandomNumberInRange(0.5f, 1.5f)*f, black);
} }
// And some gun flashes near the gun // And some gun flashes near the gun
@ -2539,6 +2527,9 @@ CAutomobile::TankControl(void)
} }
} }
#define HYDRAULIC_UPPER_EXT (-0.12f)
#define HYDRAULIC_LOWER_EXT (0.14f)
void void
CAutomobile::HydraulicControl(void) CAutomobile::HydraulicControl(void)
{ {
@ -2601,8 +2592,8 @@ CAutomobile::HydraulicControl(void)
m_hydraulicState = 20; m_hydraulicState = 20;
else{ else{
m_hydraulicState = 0; m_hydraulicState = 0;
normalUpperLimit += -0.12f; normalUpperLimit += HYDRAULIC_UPPER_EXT;
normalSpringLength = normalUpperLimit - (normalLowerLimit+0.14f); normalSpringLength = normalUpperLimit - (normalLowerLimit+HYDRAULIC_LOWER_EXT);
DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_HYDRAULIC_2, 0.0f); DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_HYDRAULIC_2, 0.0f);
} }
@ -2635,7 +2626,7 @@ CAutomobile::HydraulicControl(void)
float radius = Max(specialColModel->boundingBox.min.Magnitude(), specialColModel->boundingBox.max.Magnitude()); float radius = Max(specialColModel->boundingBox.min.Magnitude(), specialColModel->boundingBox.max.Magnitude());
if(specialColModel->boundingSphere.radius < radius) if(specialColModel->boundingSphere.radius < radius)
specialColModel->boundingSphere.radius = radius; specialColModel->boundingSphere.radius = radius;
return;
} }
if(playerInfo->m_WBState != WBSTATE_PLAYING) if(playerInfo->m_WBState != WBSTATE_PLAYING)
@ -2645,8 +2636,6 @@ CAutomobile::HydraulicControl(void)
if(m_hydraulicState < 20 && m_fVelocityChangeForAudio > 0.2f){ if(m_hydraulicState < 20 && m_fVelocityChangeForAudio > 0.2f){
if(m_hydraulicState == 0){ if(m_hydraulicState == 0){
m_hydraulicState = 20; m_hydraulicState = 20;
for(i = 0; i < 4; i++)
m_aWheelPosition[i] -= 0.06f;
DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_HYDRAULIC_1, 0.0f); DMAudio.PlayOneShot(m_audioEntityId, SOUND_CAR_HYDRAULIC_1, 0.0f);
setPrevRatio = true; setPrevRatio = true;
}else{ }else{
@ -2674,8 +2663,8 @@ CAutomobile::HydraulicControl(void)
if(m_hydraulicState < 100){ if(m_hydraulicState < 100){
if(m_hydraulicState == 0){ if(m_hydraulicState == 0){
normalUpperLimit += -0.12f; normalUpperLimit += HYDRAULIC_UPPER_EXT;
normalLowerLimit += 0.14f; normalLowerLimit += HYDRAULIC_LOWER_EXT;
normalSpringLength = normalUpperLimit - normalLowerLimit; normalSpringLength = normalUpperLimit - normalLowerLimit;
} }
@ -2740,8 +2729,8 @@ CAutomobile::HydraulicControl(void)
// Lowered, move wheels up // Lowered, move wheels up
if(m_hydraulicState == 0){ if(m_hydraulicState == 0){
normalUpperLimit += -0.12f; normalUpperLimit += HYDRAULIC_UPPER_EXT;
normalLowerLimit += 0.14f; normalLowerLimit += HYDRAULIC_LOWER_EXT;
normalSpringLength = normalUpperLimit - normalLowerLimit; normalSpringLength = normalUpperLimit - normalLowerLimit;
} }
@ -2782,8 +2771,8 @@ CAutomobile::HydraulicControl(void)
} }
if(m_fVelocityChangeForAudio < 0.1f){ if(m_fVelocityChangeForAudio < 0.1f){
normalUpperLimit += -0.12f; normalUpperLimit += HYDRAULIC_UPPER_EXT;
normalLowerLimit += 0.14f; normalLowerLimit += HYDRAULIC_LOWER_EXT;
normalSpringLength = normalUpperLimit - normalLowerLimit; normalSpringLength = normalUpperLimit - normalLowerLimit;
} }
@ -2920,7 +2909,7 @@ CAutomobile::ProcessBuoyancy(void)
DMAudio.PlayOneShot(m_audioEntityId, SOUND_WATER_FALL, 0.0f); DMAudio.PlayOneShot(m_audioEntityId, SOUND_WATER_FALL, 0.0f);
} }
if(nGenerateWaterCircles > 0 && nGenerateWaterCircles < CTimer::GetTimeInMilliseconds()){ if(nGenerateWaterCircles > 0 && nGenerateWaterCircles <= CTimer::GetTimeInMilliseconds()){
CVector pos = GetPosition(); CVector pos = GetPosition();
float waterLevel = 0.0f; float waterLevel = 0.0f;
if(CWaterLevel::GetWaterLevel(pos.x, pos.y, pos.z, &waterLevel, false)) if(CWaterLevel::GetWaterLevel(pos.x, pos.y, pos.z, &waterLevel, false))
@ -2940,7 +2929,7 @@ CAutomobile::ProcessBuoyancy(void)
} }
} }
if(nGenerateRaindrops > 0 && nGenerateRaindrops < CTimer::GetTimeInMilliseconds()){ if(nGenerateRaindrops > 0 && nGenerateRaindrops <= CTimer::GetTimeInMilliseconds()){
CVector pos = GetPosition(); CVector pos = GetPosition();
float waterLevel = 0.0f; float waterLevel = 0.0f;
if(CWaterLevel::GetWaterLevel(pos.x, pos.y, pos.z, &waterLevel, false)) if(CWaterLevel::GetWaterLevel(pos.x, pos.y, pos.z, &waterLevel, false))

View File

@ -84,13 +84,13 @@ public:
float m_aSuspensionSpringRatio[4]; float m_aSuspensionSpringRatio[4];
float m_aSuspensionSpringRatioPrev[4]; float m_aSuspensionSpringRatioPrev[4];
float m_aWheelTimer[4]; // set to 4.0 when wheel is touching ground, then decremented float m_aWheelTimer[4]; // set to 4.0 when wheel is touching ground, then decremented
float field_49C; float m_auto_unused1;
bool m_aWheelSkidmarkMuddy[4]; bool m_aWheelSkidmarkMuddy[4];
bool m_aWheelSkidmarkBloody[4]; bool m_aWheelSkidmarkBloody[4];
float m_aWheelRotation[4]; float m_aWheelRotation[4];
float m_aWheelPosition[4]; float m_aWheelPosition[4];
float m_aWheelSpeed[4]; float m_aWheelSpeed[4];
uint8 field_4D8; uint8 m_auto_unused2;
uint8 m_bombType : 3; uint8 m_bombType : 3;
uint8 bTaxiLight : 1; uint8 bTaxiLight : 1;
uint8 bDriverLastFrame : 1; // for bombs uint8 bDriverLastFrame : 1; // for bombs
@ -100,7 +100,7 @@ public:
uint8 bNotDamagedUpsideDown : 1; uint8 bNotDamagedUpsideDown : 1;
uint8 bMoreResistantToDamage : 1; uint8 bMoreResistantToDamage : 1;
CEntity *m_pBombRigger; CEntity *m_pBombRigger;
int16 field_4E0; int16 m_auto_unk1;
uint16 m_hydraulicState; uint16 m_hydraulicState;
uint32 m_nBusDoorTimerEnd; uint32 m_nBusDoorTimerEnd;
uint32 m_nBusDoorTimerStart; uint32 m_nBusDoorTimerStart;

View File

@ -288,7 +288,7 @@ CVehicle::FlyingControl(eFlightModel flightModel)
{ {
// thrust // thrust
float fForwSpeed = DotProduct(GetMoveSpeed(), GetForward()); float fForwSpeed = DotProduct(GetMoveSpeed(), GetForward());
CVector vecWidthForward = GetColModel()->boundingBox.min.y * GetForward(); CVector vecTail = GetColModel()->boundingBox.min.y * GetForward();
float fThrust = (CPad::GetPad(0)->GetAccelerate() - CPad::GetPad(0)->GetBrake()) / 255.0f; float fThrust = (CPad::GetPad(0)->GetAccelerate() - CPad::GetPad(0)->GetBrake()) / 255.0f;
if (fForwSpeed > 0.1f || (flightModel == FLIGHT_MODEL_RCPLANE && fForwSpeed > 0.02f)) if (fForwSpeed > 0.1f || (flightModel == FLIGHT_MODEL_RCPLANE && fForwSpeed > 0.02f))
fThrust += 1.0f; fThrust += 1.0f;
@ -311,13 +311,13 @@ CVehicle::FlyingControl(eFlightModel flightModel)
fSideSlipAccel = Abs(fSideSpeed) * fSideSpeed * fSeaSideSlipMult; fSideSlipAccel = Abs(fSideSpeed) * fSideSpeed * fSeaSideSlipMult;
ApplyMoveForce(m_fMass * GetRight() * fSideSlipAccel * CTimer::GetTimeStep()); ApplyMoveForce(m_fMass * GetRight() * fSideSlipAccel * CTimer::GetTimeStep());
float fYaw = -DotProduct(GetSpeed(vecWidthForward), GetRight()); float fYaw = -DotProduct(GetSpeed(vecTail), GetRight());
float fYawAccel; float fYawAccel;
if (flightModel == FLIGHT_MODEL_RCPLANE) if (flightModel == FLIGHT_MODEL_RCPLANE)
fYawAccel = fRCRudderMult * fYaw * Abs(fYaw) + fRCYawMult * fSteerLR * fForwSpeed; fYawAccel = fRCRudderMult * fYaw * Abs(fYaw) + fRCYawMult * fSteerLR * fForwSpeed;
else else
fYawAccel = fSeaRudderMult * fYaw * Abs(fYaw) + fSeaYawMult * fSteerLR * fForwSpeed; fYawAccel = fSeaRudderMult * fYaw * Abs(fYaw) + fSeaYawMult * fSteerLR * fForwSpeed;
ApplyTurnForce(fYawAccel * GetRight() * m_fTurnMass * CTimer::GetTimeStep(), vecWidthForward); ApplyTurnForce(fYawAccel * GetRight() * m_fTurnMass * CTimer::GetTimeStep(), vecTail);
float fRollAccel; float fRollAccel;
if (flightModel == FLIGHT_MODEL_RCPLANE) { if (flightModel == FLIGHT_MODEL_RCPLANE) {
@ -341,14 +341,14 @@ CVehicle::FlyingControl(eFlightModel flightModel)
ApplyTurnForce(fStabiliseSpeed * m_fTurnMass * GetRight(), GetUp()); // no CTimer::GetTimeStep(), is it right? VC doesn't have it too ApplyTurnForce(fStabiliseSpeed * m_fTurnMass * GetRight(), GetUp()); // no CTimer::GetTimeStep(), is it right? VC doesn't have it too
// up/down // up/down
float fTail = -DotProduct(GetSpeed(vecWidthForward), GetUp()); float fTail = -DotProduct(GetSpeed(vecTail), GetUp());
float fSteerUD = -CPad::GetPad(0)->GetSteeringUpDown() / 128.0f; float fSteerUD = -CPad::GetPad(0)->GetSteeringUpDown() / 128.0f;
float fPitchAccel; float fPitchAccel;
if (flightModel == FLIGHT_MODEL_RCPLANE) if (flightModel == FLIGHT_MODEL_RCPLANE)
fPitchAccel = fRCTailMult * fTail * Abs(fTail) + fRCPitchMult * fSteerUD * fForwSpeed; fPitchAccel = fRCTailMult * fTail * Abs(fTail) + fRCPitchMult * fSteerUD * fForwSpeed;
else else
fPitchAccel = fSeaTailMult * fTail * Abs(fTail) + fSeaPitchMult * fSteerUD * fForwSpeed; fPitchAccel = fSeaTailMult * fTail * Abs(fTail) + fSeaPitchMult * fSteerUD * fForwSpeed;
ApplyTurnForce(fPitchAccel * m_fTurnMass * GetUp() * CTimer::GetTimeStep(), vecWidthForward); ApplyTurnForce(fPitchAccel * m_fTurnMass * GetUp() * CTimer::GetTimeStep(), vecTail);
float fLift = -DotProduct(GetMoveSpeed(), GetUp()) / Max(0.01f, GetMoveSpeed().Magnitude()); float fLift = -DotProduct(GetMoveSpeed(), GetUp()) / Max(0.01f, GetMoveSpeed().Magnitude());
float fLiftAccel; float fLiftAccel;