mirror of
https://github.com/TombEngine/TombEngine.git
synced 2025-04-29 00:07:58 +03:00
Merge branch 'develop' into DisplayString_Scale
This commit is contained in:
commit
fad4cb0255
159 changed files with 6017 additions and 3337 deletions
|
@ -1,4 +1,4 @@
|
||||||
Version 1.1.1
|
Version 1.2
|
||||||
==============
|
==============
|
||||||
|
|
||||||
* Fix burning torch not working properly if there are more than 256 objects in a level.
|
* Fix burning torch not working properly if there are more than 256 objects in a level.
|
||||||
|
@ -13,8 +13,15 @@ Version 1.1.1
|
||||||
* Fix TR2 doberman crashing the game when killed by explosive weapons.
|
* Fix TR2 doberman crashing the game when killed by explosive weapons.
|
||||||
* Fix volume change in settings not affecting voice track.
|
* Fix volume change in settings not affecting voice track.
|
||||||
* Fix several lighting bugs.
|
* Fix several lighting bugs.
|
||||||
|
* Fix double drawing additive faces.
|
||||||
* Fix savegame count not properly increasing.
|
* Fix savegame count not properly increasing.
|
||||||
* Fix regeneration of non-ammo pickups with OCB 128.
|
* Fix regeneration of non-ammo pickups with OCB 128.
|
||||||
|
* Overhaul Pushable blocks:
|
||||||
|
- Separate climbable and normal pushable blocks slots.
|
||||||
|
- Add new pushable OCB to manipulate pushable block properties.
|
||||||
|
- Add new Lara animations for pushing over the block off the edge (both TR1-3 and TR4-5 versions).
|
||||||
|
- Fix pushables not working with raising block.
|
||||||
|
- Fix miscellaneous pushable bugs.
|
||||||
* Overhaul look-around feature:
|
* Overhaul look-around feature:
|
||||||
- Allow for more consistent and wider viewing angles while crawling, crouching, and hanging.
|
- Allow for more consistent and wider viewing angles while crawling, crouching, and hanging.
|
||||||
- Improve look camera movement and control.
|
- Improve look camera movement and control.
|
||||||
|
@ -28,18 +35,23 @@ Version 1.1.1
|
||||||
- OCB > 0: When the player steps on the platform, it will crumble after the number of frames set in the OCB.
|
- OCB > 0: When the player steps on the platform, it will crumble after the number of frames set in the OCB.
|
||||||
- A positive value results in activation via player collision.
|
- A positive value results in activation via player collision.
|
||||||
- A negative value requires a trigger to activate.
|
- A negative value requires a trigger to activate.
|
||||||
|
* Add basic mouse input handling. Allows for binding of mouse inputs in control settings.
|
||||||
|
* Add settings for Mouse Sensitivity and Mouse Smoothing (not used in-game yet).
|
||||||
|
|
||||||
Lua API changes:
|
Lua API changes:
|
||||||
* Add DisplaySprite object.
|
* Add DisplaySprite object.
|
||||||
* Add Flow::EnableLoadSave() function to disable savegames.
|
* Add Flow:EnableLoadSave() function to disable savegames.
|
||||||
* Add Flow::EnablePointFilter() function to disable bilinear filtering.
|
* Add Flow:EnablePointFilter() function to disable bilinear filtering.
|
||||||
* Add Lara::GetAmmoType() function to read the ammo that Lara is using.
|
* Add InputGetCursorDisplayPosition() function to get the position of the cursor.
|
||||||
* Add Lara::GetControlLock() and Lara::SetControlLock() functions to handle controls locking.
|
* Add Lara:GetAmmoType() function to read the ammo that Lara is using.
|
||||||
|
* Add Lara:GetControlLock() and Lara:SetControlLock() functions to handle controls locking.
|
||||||
* Add Logic.HandleEvent() function to call node events.
|
* Add Logic.HandleEvent() function to call node events.
|
||||||
* Add functions to load, save, delete and check existence of savegames.
|
* Add functions to load, save, delete and check existence of savegames.
|
||||||
* Add DisplayStringOption.RIGHT and DisplayStringOption.BLINK flags for DisplayString.
|
* Add DisplayStringOption.RIGHT and DisplayStringOption.BLINK flags for DisplayString.
|
||||||
* Make Vec2 object float-based instead of integer-based.
|
* Make Vec2 and Vec3 objects float-based instead of integer-based.
|
||||||
* Add Vec2 arithmetic for division with a number and multiplication with another Vec2.
|
* Split and organise functions in `Misc` namespace to appropriate new namespaces.
|
||||||
|
* Add Vec2/Vec3 arithmetic for division with a number and multiplication with another Vec2/Vec3.
|
||||||
|
* Add various Vec2/Vec3 operations such as Normalize() or Lerp().
|
||||||
* Add log messages warnings to functions AddCallback and RemoveCallback.
|
* Add log messages warnings to functions AddCallback and RemoveCallback.
|
||||||
* Fix InventoryItem constructor, now it will accept compound values in Item Action parameter.
|
* Fix InventoryItem constructor, now it will accept compound values in Item Action parameter.
|
||||||
* Fix Moveable constructor forcing initial animation to 0 and hit points to 10, even if not specified.
|
* Fix Moveable constructor forcing initial animation to 0 and hit points to 10, even if not specified.
|
||||||
|
|
|
@ -16,7 +16,7 @@ Flow.SetTitleScreenImagePath("Screens\\main.jpg")
|
||||||
|
|
||||||
Flow.SetTotalSecretCount(5)
|
Flow.SetTotalSecretCount(5)
|
||||||
|
|
||||||
-- Enable/Disable bilinear filtering.
|
-- Enable/Disable Point Filter (Square, unsmoothed pixels).
|
||||||
|
|
||||||
Flow.EnablePointFilter(false)
|
Flow.EnablePointFilter(false)
|
||||||
|
|
||||||
|
|
|
@ -91,7 +91,7 @@ namespace TEN::Hud
|
||||||
float life = round(DisplayPickup::LIFE_MAX * FPS);
|
float life = round(DisplayPickup::LIFE_MAX * FPS);
|
||||||
|
|
||||||
// Increment count of existing display pickup if it exists.
|
// Increment count of existing display pickup if it exists.
|
||||||
for (auto& pickup : DisplayPickups)
|
for (auto& pickup : _displayPickups)
|
||||||
{
|
{
|
||||||
// Ignore already disappearing display pickups.
|
// Ignore already disappearing display pickups.
|
||||||
if (pickup.Life <= 0.0f)
|
if (pickup.Life <= 0.0f)
|
||||||
|
@ -132,17 +132,17 @@ namespace TEN::Hud
|
||||||
|
|
||||||
void PickupSummaryController::Update()
|
void PickupSummaryController::Update()
|
||||||
{
|
{
|
||||||
if (DisplayPickups.empty())
|
if (_displayPickups.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Get and apply stack positions as targets.
|
// Get and apply stack positions as targets.
|
||||||
auto stackPositions = GetStackPositions();
|
auto stackPositions = GetStackPositions();
|
||||||
for (int i = 0; i < stackPositions.size(); i++)
|
for (int i = 0; i < stackPositions.size(); i++)
|
||||||
DisplayPickups[i].Target = std::move(stackPositions[i]);
|
_displayPickups[i].Target = std::move(stackPositions[i]);
|
||||||
|
|
||||||
// Update display pickups.
|
// Update display pickups.
|
||||||
bool isHead = true;
|
bool isHead = true;
|
||||||
for (auto& pickup : DisplayPickups)
|
for (auto& pickup : _displayPickups)
|
||||||
{
|
{
|
||||||
pickup.Update(isHead);
|
pickup.Update(isHead);
|
||||||
isHead = false;
|
isHead = false;
|
||||||
|
@ -155,11 +155,11 @@ namespace TEN::Hud
|
||||||
{
|
{
|
||||||
//DrawDebug();
|
//DrawDebug();
|
||||||
|
|
||||||
if (DisplayPickups.empty())
|
if (_displayPickups.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Draw display pickups.
|
// Draw display pickups.
|
||||||
for (const auto& pickup : DisplayPickups)
|
for (const auto& pickup : _displayPickups)
|
||||||
{
|
{
|
||||||
if (pickup.IsOffscreen())
|
if (pickup.IsOffscreen())
|
||||||
continue;
|
continue;
|
||||||
|
@ -170,7 +170,7 @@ namespace TEN::Hud
|
||||||
|
|
||||||
void PickupSummaryController::Clear()
|
void PickupSummaryController::Clear()
|
||||||
{
|
{
|
||||||
DisplayPickups.clear();
|
_displayPickups.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Vector2> PickupSummaryController::GetStackPositions() const
|
std::vector<Vector2> PickupSummaryController::GetStackPositions() const
|
||||||
|
@ -183,8 +183,8 @@ namespace TEN::Hud
|
||||||
|
|
||||||
// Calculate stack positions.
|
// Calculate stack positions.
|
||||||
auto stackPositions = std::vector<Vector2>{};
|
auto stackPositions = std::vector<Vector2>{};
|
||||||
stackPositions.resize(DisplayPickups.size());
|
stackPositions.resize(_displayPickups.size());
|
||||||
for (int i = 0; i < DisplayPickups.size(); i++)
|
for (int i = 0; i < _displayPickups.size(); i++)
|
||||||
{
|
{
|
||||||
auto relPos = (i < STACK_HEIGHT_MAX) ? (Vector2(0.0f, i) * SCREEN_SCALE) : Vector2(0.0f, SCREEN_SPACE_RES.y);
|
auto relPos = (i < STACK_HEIGHT_MAX) ? (Vector2(0.0f, i) * SCREEN_SCALE) : Vector2(0.0f, SCREEN_SPACE_RES.y);
|
||||||
auto pos = (SCREEN_SPACE_RES - relPos) - SCREEN_OFFSET;
|
auto pos = (SCREEN_SPACE_RES - relPos) - SCREEN_OFFSET;
|
||||||
|
@ -196,29 +196,29 @@ namespace TEN::Hud
|
||||||
|
|
||||||
DisplayPickup& PickupSummaryController::GetNewDisplayPickup()
|
DisplayPickup& PickupSummaryController::GetNewDisplayPickup()
|
||||||
{
|
{
|
||||||
assertion(DisplayPickups.size() <= DISPLAY_PICKUP_COUNT_MAX, "Display pickup overflow.");
|
assertion(_displayPickups.size() <= DISPLAY_PICKUP_COUNT_MAX, "Display pickup overflow.");
|
||||||
|
|
||||||
// Add and return new display pickup.
|
// Add and return new display pickup.
|
||||||
if (DisplayPickups.size() < DISPLAY_PICKUP_COUNT_MAX)
|
if (_displayPickups.size() < DISPLAY_PICKUP_COUNT_MAX)
|
||||||
return DisplayPickups.emplace_back();
|
return _displayPickups.emplace_back();
|
||||||
|
|
||||||
// Clear and return most recent display pickup.
|
// Clear and return most recent display pickup.
|
||||||
auto& pickup = DisplayPickups.back();
|
auto& pickup = _displayPickups.back();
|
||||||
pickup = {};
|
pickup = {};
|
||||||
return pickup;
|
return pickup;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PickupSummaryController::ClearInactiveDisplayPickups()
|
void PickupSummaryController::ClearInactiveDisplayPickups()
|
||||||
{
|
{
|
||||||
DisplayPickups.erase(
|
_displayPickups.erase(
|
||||||
std::remove_if(
|
std::remove_if(
|
||||||
DisplayPickups.begin(), DisplayPickups.end(),
|
_displayPickups.begin(), _displayPickups.end(),
|
||||||
[](const DisplayPickup& pickup) { return ((pickup.Life <= 0.0f) && pickup.IsOffscreen()); }),
|
[](const DisplayPickup& pickup) { return ((pickup.Life <= 0.0f) && pickup.IsOffscreen()); }),
|
||||||
DisplayPickups.end());
|
_displayPickups.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
void PickupSummaryController::DrawDebug() const
|
void PickupSummaryController::DrawDebug() const
|
||||||
{
|
{
|
||||||
g_Renderer.PrintDebugMessage("Display pickups in summary: %d", DisplayPickups.size());
|
g_Renderer.PrintDebugMessage("Display pickups in summary: %d", _displayPickups.size());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,7 @@ namespace TEN::Hud
|
||||||
static constexpr auto DISPLAY_PICKUP_COUNT_ARG_DEFAULT = 1;
|
static constexpr auto DISPLAY_PICKUP_COUNT_ARG_DEFAULT = 1;
|
||||||
|
|
||||||
// Members
|
// Members
|
||||||
std::vector<DisplayPickup> DisplayPickups = {};
|
std::vector<DisplayPickup> _displayPickups = {};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Utilities
|
// Utilities
|
||||||
|
|
|
@ -52,10 +52,10 @@ namespace TEN::Hud
|
||||||
const auto& player = GetLaraInfo(item);
|
const auto& player = GetLaraInfo(item);
|
||||||
|
|
||||||
// Initialize bar values.
|
// Initialize bar values.
|
||||||
AirBar.Initialize(player.Status.Air / LARA_AIR_MAX);
|
_airBar.Initialize(player.Status.Air / LARA_AIR_MAX);
|
||||||
ExposureBar.Initialize(player.Status.Exposure / LARA_EXPOSURE_MAX);
|
_exposureBar.Initialize(player.Status.Exposure / LARA_EXPOSURE_MAX);
|
||||||
HealthBar.Initialize(item.HitPoints / LARA_HEALTH_MAX);
|
_healthBar.Initialize(item.HitPoints / LARA_HEALTH_MAX);
|
||||||
StaminaBar.Initialize(player.Status.Stamina / LARA_STAMINA_MAX);
|
_staminaBar.Initialize(player.Status.Stamina / LARA_STAMINA_MAX);
|
||||||
}
|
}
|
||||||
|
|
||||||
void StatusBarsController::Update(const ItemInfo& item)
|
void StatusBarsController::Update(const ItemInfo& item)
|
||||||
|
@ -64,7 +64,7 @@ namespace TEN::Hud
|
||||||
|
|
||||||
// Update flash.
|
// Update flash.
|
||||||
if ((GameTimer % (int)round(FLASH_INTERVAL * FPS)) == 0)
|
if ((GameTimer % (int)round(FLASH_INTERVAL * FPS)) == 0)
|
||||||
DoFlash = !DoFlash;
|
_doFlash = !_doFlash;
|
||||||
|
|
||||||
// Update bars.
|
// Update bars.
|
||||||
UpdateAirBar(item);
|
UpdateAirBar(item);
|
||||||
|
@ -99,13 +99,13 @@ namespace TEN::Hud
|
||||||
const auto& player = GetLaraInfo(item);
|
const auto& player = GetLaraInfo(item);
|
||||||
|
|
||||||
// Update generic data.
|
// Update generic data.
|
||||||
AirBar.Update(player.Status.Air / LARA_AIR_MAX);
|
_airBar.Update(player.Status.Air / LARA_AIR_MAX);
|
||||||
|
|
||||||
// Update life.
|
// Update life.
|
||||||
if (AirBar.Value != AirBar.TargetValue ||
|
if (_airBar.Value != _airBar.TargetValue ||
|
||||||
player.Control.WaterStatus == WaterStatus::Underwater)
|
player.Control.WaterStatus == WaterStatus::Underwater)
|
||||||
{
|
{
|
||||||
AirBar.Life = round(StatusBar::LIFE_MAX * FPS);
|
_airBar.Life = round(StatusBar::LIFE_MAX * FPS);
|
||||||
}
|
}
|
||||||
|
|
||||||
// HACK: Special case for UPV as it sets player.Control.WaterStatus to WaterStatus::Dry.
|
// HACK: Special case for UPV as it sets player.Control.WaterStatus to WaterStatus::Dry.
|
||||||
|
@ -113,7 +113,7 @@ namespace TEN::Hud
|
||||||
{
|
{
|
||||||
const auto& vehicleItem = g_Level.Items[player.Context.Vehicle];
|
const auto& vehicleItem = g_Level.Items[player.Context.Vehicle];
|
||||||
if (vehicleItem.ObjectNumber == ID_UPV)
|
if (vehicleItem.ObjectNumber == ID_UPV)
|
||||||
AirBar.Life = round(StatusBar::LIFE_MAX * FPS);
|
_airBar.Life = round(StatusBar::LIFE_MAX * FPS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,13 +122,13 @@ namespace TEN::Hud
|
||||||
const auto& player = GetLaraInfo(item);
|
const auto& player = GetLaraInfo(item);
|
||||||
|
|
||||||
// Update generic data.
|
// Update generic data.
|
||||||
ExposureBar.Update(player.Status.Exposure / LARA_EXPOSURE_MAX);
|
_exposureBar.Update(player.Status.Exposure / LARA_EXPOSURE_MAX);
|
||||||
|
|
||||||
// Update life.
|
// Update life.
|
||||||
if (ExposureBar.Value != ExposureBar.TargetValue ||
|
if (_exposureBar.Value != _exposureBar.TargetValue ||
|
||||||
(TestEnvironment(ENV_FLAG_WATER, item.RoomNumber) && TestEnvironment(ENV_FLAG_COLD, item.RoomNumber)))
|
(TestEnvironment(ENV_FLAG_WATER, item.RoomNumber) && TestEnvironment(ENV_FLAG_COLD, item.RoomNumber)))
|
||||||
{
|
{
|
||||||
ExposureBar.Life = round(StatusBar::LIFE_MAX * FPS);
|
_exposureBar.Life = round(StatusBar::LIFE_MAX * FPS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,25 +137,25 @@ namespace TEN::Hud
|
||||||
const auto& player = GetLaraInfo(item);
|
const auto& player = GetLaraInfo(item);
|
||||||
|
|
||||||
// Update generic data.
|
// Update generic data.
|
||||||
HealthBar.Update(item.HitPoints / LARA_HEALTH_MAX);
|
_healthBar.Update(item.HitPoints / LARA_HEALTH_MAX);
|
||||||
|
|
||||||
// Update life.
|
// Update life.
|
||||||
if (HealthBar.Value != HealthBar.TargetValue ||
|
if (_healthBar.Value != _healthBar.TargetValue ||
|
||||||
item.HitPoints <= LARA_HEALTH_CRITICAL || player.Status.Poison != 0 ||
|
item.HitPoints <= LARA_HEALTH_CRITICAL || player.Status.Poison != 0 ||
|
||||||
(player.Control.HandStatus == HandStatus::WeaponDraw &&
|
(player.Control.HandStatus == HandStatus::WeaponDraw &&
|
||||||
player.Control.Weapon.GunType != LaraWeaponType::Flare) || // HACK: Exclude flare.
|
player.Control.Weapon.GunType != LaraWeaponType::Flare) || // HACK: Exclude flare.
|
||||||
(player.Control.HandStatus == HandStatus::WeaponReady &&
|
(player.Control.HandStatus == HandStatus::WeaponReady &&
|
||||||
player.Control.Weapon.GunType != LaraWeaponType::Torch)) // HACK: Exclude torch.
|
player.Control.Weapon.GunType != LaraWeaponType::Torch)) // HACK: Exclude torch.
|
||||||
{
|
{
|
||||||
HealthBar.Life = round(StatusBar::LIFE_MAX * FPS);
|
_healthBar.Life = round(StatusBar::LIFE_MAX * FPS);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Special case for weapon undraw.
|
// Special case for weapon undraw.
|
||||||
if (HealthBar.Value == HealthBar.TargetValue &&
|
if (_healthBar.Value == _healthBar.TargetValue &&
|
||||||
item.HitPoints > LARA_HEALTH_CRITICAL && player.Status.Poison == 0 &&
|
item.HitPoints > LARA_HEALTH_CRITICAL && player.Status.Poison == 0 &&
|
||||||
player.Control.HandStatus == HandStatus::WeaponUndraw)
|
player.Control.HandStatus == HandStatus::WeaponUndraw)
|
||||||
{
|
{
|
||||||
HealthBar.Life = 0.0f;
|
_healthBar.Life = 0.0f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -164,20 +164,20 @@ namespace TEN::Hud
|
||||||
const auto& player = GetLaraInfo(item);
|
const auto& player = GetLaraInfo(item);
|
||||||
|
|
||||||
// Update generic data.
|
// Update generic data.
|
||||||
StaminaBar.Update(player.Status.Stamina / LARA_STAMINA_MAX);
|
_staminaBar.Update(player.Status.Stamina / LARA_STAMINA_MAX);
|
||||||
|
|
||||||
// Update life.
|
// Update life.
|
||||||
if (StaminaBar.Value != StaminaBar.TargetValue ||
|
if (_staminaBar.Value != _staminaBar.TargetValue ||
|
||||||
StaminaBar.Value != 1.0f)
|
_staminaBar.Value != 1.0f)
|
||||||
{
|
{
|
||||||
StaminaBar.Life = round(StatusBar::LIFE_MAX * FPS);
|
_staminaBar.Life = round(StatusBar::LIFE_MAX * FPS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void StatusBarsController::DrawStatusBar(float value, float criticalValue, const RendererHudBar& rHudBar, GAME_OBJECT_ID textureID, int frame, bool isPoisoned) const
|
void StatusBarsController::DrawStatusBar(float value, float criticalValue, const RendererHudBar& rHudBar, GAME_OBJECT_ID textureID, int frame, bool isPoisoned) const
|
||||||
{
|
{
|
||||||
if (value <= criticalValue)
|
if (value <= criticalValue)
|
||||||
value = DoFlash ? value : 0.0f;
|
value = _doFlash ? value : 0.0f;
|
||||||
|
|
||||||
g_Renderer.DrawBar(value, rHudBar, textureID, frame, isPoisoned);
|
g_Renderer.DrawBar(value, rHudBar, textureID, frame, isPoisoned);
|
||||||
}
|
}
|
||||||
|
@ -187,10 +187,10 @@ namespace TEN::Hud
|
||||||
constexpr auto TEXTURE_ID = ID_AIR_BAR_TEXTURE;
|
constexpr auto TEXTURE_ID = ID_AIR_BAR_TEXTURE;
|
||||||
constexpr auto CRITICAL_VALUE = LARA_AIR_CRITICAL / LARA_AIR_MAX;
|
constexpr auto CRITICAL_VALUE = LARA_AIR_CRITICAL / LARA_AIR_MAX;
|
||||||
|
|
||||||
if (AirBar.Life <= 0.0f)
|
if (_airBar.Life <= 0.0f)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
DrawStatusBar(AirBar.Value, CRITICAL_VALUE, *g_AirBar, TEXTURE_ID, 0, false);
|
DrawStatusBar(_airBar.Value, CRITICAL_VALUE, *g_AirBar, TEXTURE_ID, 0, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void StatusBarsController::DrawExposureBar() const
|
void StatusBarsController::DrawExposureBar() const
|
||||||
|
@ -198,10 +198,10 @@ namespace TEN::Hud
|
||||||
constexpr auto TEXTURE_ID = ID_SFX_BAR_TEXTURE;
|
constexpr auto TEXTURE_ID = ID_SFX_BAR_TEXTURE;
|
||||||
constexpr auto CRITICAL_VALUE = LARA_EXPOSURE_CRITICAL / LARA_EXPOSURE_MAX;
|
constexpr auto CRITICAL_VALUE = LARA_EXPOSURE_CRITICAL / LARA_EXPOSURE_MAX;
|
||||||
|
|
||||||
if (ExposureBar.Life <= 0.0f)
|
if (_exposureBar.Life <= 0.0f)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
DrawStatusBar(ExposureBar.Value, CRITICAL_VALUE, *g_ExposureBar, TEXTURE_ID, 0, false);
|
DrawStatusBar(_exposureBar.Value, CRITICAL_VALUE, *g_ExposureBar, TEXTURE_ID, 0, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void StatusBarsController::DrawHealthBar(bool isPoisoned) const
|
void StatusBarsController::DrawHealthBar(bool isPoisoned) const
|
||||||
|
@ -209,10 +209,10 @@ namespace TEN::Hud
|
||||||
constexpr auto TEXTURE_ID = ID_HEALTH_BAR_TEXTURE;
|
constexpr auto TEXTURE_ID = ID_HEALTH_BAR_TEXTURE;
|
||||||
constexpr auto CRITICAL_VALUE = LARA_HEALTH_CRITICAL / LARA_HEALTH_MAX;
|
constexpr auto CRITICAL_VALUE = LARA_HEALTH_CRITICAL / LARA_HEALTH_MAX;
|
||||||
|
|
||||||
if (HealthBar.Life <= 0.0f)
|
if (_healthBar.Life <= 0.0f)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
DrawStatusBar(HealthBar.Value, CRITICAL_VALUE, *g_HealthBar, TEXTURE_ID, GlobalCounter, isPoisoned);
|
DrawStatusBar(_healthBar.Value, CRITICAL_VALUE, *g_HealthBar, TEXTURE_ID, GlobalCounter, isPoisoned);
|
||||||
}
|
}
|
||||||
|
|
||||||
void StatusBarsController::DrawStaminaBar() const
|
void StatusBarsController::DrawStaminaBar() const
|
||||||
|
@ -220,9 +220,9 @@ namespace TEN::Hud
|
||||||
constexpr auto TEXTURE_ID = ID_DASH_BAR_TEXTURE;
|
constexpr auto TEXTURE_ID = ID_DASH_BAR_TEXTURE;
|
||||||
constexpr auto CRITICAL_VALUE = 0;
|
constexpr auto CRITICAL_VALUE = 0;
|
||||||
|
|
||||||
if (StaminaBar.Life <= 0.0f)
|
if (_staminaBar.Life <= 0.0f)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
DrawStatusBar(StaminaBar.Value, CRITICAL_VALUE, *g_StaminaBar, TEXTURE_ID, 0, false);
|
DrawStatusBar(_staminaBar.Value, CRITICAL_VALUE, *g_StaminaBar, TEXTURE_ID, 0, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,12 +25,12 @@ namespace TEN::Hud
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
// Members
|
// Members
|
||||||
StatusBar AirBar = {};
|
StatusBar _airBar = {};
|
||||||
StatusBar ExposureBar = {};
|
StatusBar _exposureBar = {};
|
||||||
StatusBar HealthBar = {};
|
StatusBar _healthBar = {};
|
||||||
StatusBar StaminaBar = {};
|
StatusBar _staminaBar = {};
|
||||||
|
|
||||||
bool DoFlash = false;
|
bool _doFlash = false;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Utilities
|
// Utilities
|
||||||
|
|
|
@ -96,7 +96,7 @@ namespace TEN::Hud
|
||||||
// Update orientation.
|
// Update orientation.
|
||||||
if (IsPrimary)
|
if (IsPrimary)
|
||||||
{
|
{
|
||||||
Orientation += ROT;
|
Orientation += IsActive ? ROT : (ROT * 2);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -114,7 +114,7 @@ namespace TEN::Hud
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Scale = Lerp(Scale, 0.0f, MORPH_LERP_ALPHA / 2);
|
Scale = Lerp(Scale, 0.0f, MORPH_LERP_ALPHA / 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update color.
|
// Update color.
|
||||||
|
@ -178,8 +178,8 @@ namespace TEN::Hud
|
||||||
// Check if target highlighter is enabled.
|
// Check if target highlighter is enabled.
|
||||||
if (!g_Configuration.EnableTargetHighlighter)
|
if (!g_Configuration.EnableTargetHighlighter)
|
||||||
{
|
{
|
||||||
if (!Crosshairs.empty())
|
if (!_crosshairs.empty())
|
||||||
Crosshairs.clear();
|
_crosshairs.clear();
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -197,8 +197,8 @@ namespace TEN::Hud
|
||||||
itemNumbers.push_back(itemPtr->Index);
|
itemNumbers.push_back(itemPtr->Index);
|
||||||
|
|
||||||
// Find crosshair at item number key.
|
// Find crosshair at item number key.
|
||||||
auto it = Crosshairs.find(itemPtr->Index);
|
auto it = _crosshairs.find(itemPtr->Index);
|
||||||
if (it == Crosshairs.end())
|
if (it == _crosshairs.end())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Set crosshair as primary or peripheral.
|
// Set crosshair as primary or peripheral.
|
||||||
|
@ -222,10 +222,10 @@ namespace TEN::Hud
|
||||||
{
|
{
|
||||||
//DrawDebug();
|
//DrawDebug();
|
||||||
|
|
||||||
if (Crosshairs.empty())
|
if (_crosshairs.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (const auto& [itemNumber, crosshair] : Crosshairs)
|
for (const auto& [itemNumber, crosshair] : _crosshairs)
|
||||||
crosshair.Draw();
|
crosshair.Draw();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -239,7 +239,7 @@ namespace TEN::Hud
|
||||||
constexpr auto TARGET_BONE_ID = 0;
|
constexpr auto TARGET_BONE_ID = 0;
|
||||||
|
|
||||||
// No crosshairs to update; return early.
|
// No crosshairs to update; return early.
|
||||||
if (Crosshairs.empty() && itemNumbers.empty())
|
if (_crosshairs.empty() && itemNumbers.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Update active crosshairs.
|
// Update active crosshairs.
|
||||||
|
@ -249,8 +249,8 @@ namespace TEN::Hud
|
||||||
auto targetPos = GetJointPosition(item, TARGET_BONE_ID).ToVector3();
|
auto targetPos = GetJointPosition(item, TARGET_BONE_ID).ToVector3();
|
||||||
|
|
||||||
// Update existing active crosshair.
|
// Update existing active crosshair.
|
||||||
auto it = Crosshairs.find(itemNumber);
|
auto it = _crosshairs.find(itemNumber);
|
||||||
if (it != Crosshairs.end())
|
if (it != _crosshairs.end())
|
||||||
{
|
{
|
||||||
auto& crosshair = it->second;
|
auto& crosshair = it->second;
|
||||||
if (crosshair.IsActive)
|
if (crosshair.IsActive)
|
||||||
|
@ -265,7 +265,7 @@ namespace TEN::Hud
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update inactive crosshairs.
|
// Update inactive crosshairs.
|
||||||
for (auto& [itemNumber, crosshair] : Crosshairs)
|
for (auto& [itemNumber, crosshair] : _crosshairs)
|
||||||
{
|
{
|
||||||
// Find crosshairs at absent item number keys.
|
// Find crosshairs at absent item number keys.
|
||||||
if (Contains(itemNumbers, itemNumber))
|
if (Contains(itemNumbers, itemNumber))
|
||||||
|
@ -286,12 +286,12 @@ namespace TEN::Hud
|
||||||
constexpr auto CROSSHAIR_COUNT_MAX = 16;
|
constexpr auto CROSSHAIR_COUNT_MAX = 16;
|
||||||
|
|
||||||
// Map is full; clear smallest crosshair.
|
// Map is full; clear smallest crosshair.
|
||||||
if (Crosshairs.size() >= CROSSHAIR_COUNT_MAX)
|
if (_crosshairs.size() >= CROSSHAIR_COUNT_MAX)
|
||||||
{
|
{
|
||||||
int key = 0;
|
int key = 0;
|
||||||
float smallestScale = INFINITY;
|
float smallestScale = INFINITY;
|
||||||
|
|
||||||
for (auto& [itemNumber, crosshair] : Crosshairs)
|
for (auto& [itemNumber, crosshair] : _crosshairs)
|
||||||
{
|
{
|
||||||
if (crosshair.Scale < smallestScale)
|
if (crosshair.Scale < smallestScale)
|
||||||
{
|
{
|
||||||
|
@ -300,12 +300,12 @@ namespace TEN::Hud
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Crosshairs.erase(key);
|
_crosshairs.erase(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return new crosshair.
|
// Return new crosshair.
|
||||||
Crosshairs.insert({ itemNumber, {} });
|
_crosshairs.insert({ itemNumber, {} });
|
||||||
auto& crosshair = Crosshairs.at(itemNumber);
|
auto& crosshair = _crosshairs.at(itemNumber);
|
||||||
crosshair = {};
|
crosshair = {};
|
||||||
return crosshair;
|
return crosshair;
|
||||||
}
|
}
|
||||||
|
@ -348,11 +348,11 @@ namespace TEN::Hud
|
||||||
|
|
||||||
void TargetHighlighterController::ClearInactiveCrosshairs()
|
void TargetHighlighterController::ClearInactiveCrosshairs()
|
||||||
{
|
{
|
||||||
for (auto it = Crosshairs.begin(); it != Crosshairs.end();)
|
for (auto it = _crosshairs.begin(); it != _crosshairs.end();)
|
||||||
{
|
{
|
||||||
const auto& crosshair = it->second;
|
const auto& crosshair = it->second;
|
||||||
(!crosshair.IsActive && crosshair.Scale <= EPSILON) ?
|
(!crosshair.IsActive && crosshair.Scale <= EPSILON) ?
|
||||||
(it = Crosshairs.erase(it)) : ++it;
|
(it = _crosshairs.erase(it)) : ++it;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -362,7 +362,7 @@ namespace TEN::Hud
|
||||||
unsigned int primaryCount = 0;
|
unsigned int primaryCount = 0;
|
||||||
unsigned int peripheralCount = 0;
|
unsigned int peripheralCount = 0;
|
||||||
|
|
||||||
for (const auto& [itemNumber, crosshair] : Crosshairs)
|
for (const auto& [itemNumber, crosshair] : _crosshairs)
|
||||||
{
|
{
|
||||||
crosshair.IsPrimary ? primaryCount++ : peripheralCount++;
|
crosshair.IsPrimary ? primaryCount++ : peripheralCount++;
|
||||||
|
|
||||||
|
|
|
@ -55,7 +55,7 @@ namespace TEN::Hud
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
// Members
|
// Members
|
||||||
std::unordered_map<int, CrosshairData> Crosshairs = {}; // Key = item number.
|
std::unordered_map<int, CrosshairData> _crosshairs = {}; // Key = item number.
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Utilities
|
// Utilities
|
||||||
|
|
|
@ -38,22 +38,25 @@
|
||||||
#include "Scripting/Include/Flow/ScriptInterfaceFlowHandler.h"
|
#include "Scripting/Include/Flow/ScriptInterfaceFlowHandler.h"
|
||||||
#include "Scripting/Include/ScriptInterfaceLevel.h"
|
#include "Scripting/Include/ScriptInterfaceLevel.h"
|
||||||
#include "Sound/sound.h"
|
#include "Sound/sound.h"
|
||||||
|
#include "Specific/Input/Input.h"
|
||||||
#include "Specific/winmain.h"
|
#include "Specific/winmain.h"
|
||||||
|
|
||||||
|
using namespace TEN::Collision::Floordata;
|
||||||
using namespace TEN::Control::Volumes;
|
using namespace TEN::Control::Volumes;
|
||||||
using namespace TEN::Effects::Hair;
|
using namespace TEN::Effects::Hair;
|
||||||
using namespace TEN::Effects::Items;
|
using namespace TEN::Effects::Items;
|
||||||
using namespace TEN::Collision::Floordata;
|
|
||||||
using namespace TEN::Input;
|
using namespace TEN::Input;
|
||||||
using namespace TEN::Math;
|
using namespace TEN::Math;
|
||||||
|
|
||||||
using TEN::Renderer::g_Renderer;
|
using TEN::Renderer::g_Renderer;
|
||||||
|
|
||||||
|
using PlayerStateRoutine = std::function<void(ItemInfo* item, CollisionInfo* coll)>;
|
||||||
|
|
||||||
LaraInfo Lara = {};
|
LaraInfo Lara = {};
|
||||||
ItemInfo* LaraItem;
|
ItemInfo* LaraItem;
|
||||||
CollisionInfo LaraCollision = {};
|
CollisionInfo LaraCollision = {};
|
||||||
|
|
||||||
std::function<LaraRoutineFunction> lara_control_routines[NUM_LARA_STATES + 1] =
|
auto PlayerStateControlRoutines = std::array<PlayerStateRoutine, NUM_LARA_STATES + 1>
|
||||||
{
|
{
|
||||||
lara_as_walk_forward,
|
lara_as_walk_forward,
|
||||||
lara_as_run_forward,
|
lara_as_run_forward,
|
||||||
|
@ -245,11 +248,11 @@ std::function<LaraRoutineFunction> lara_control_routines[NUM_LARA_STATES + 1] =
|
||||||
lara_as_null,
|
lara_as_null,
|
||||||
lara_as_null,
|
lara_as_null,
|
||||||
lara_as_use_puzzle,//189
|
lara_as_use_puzzle,//189
|
||||||
lara_as_null,//190
|
lara_as_pushable_edge_slip,//190
|
||||||
lara_as_sprint_slide,//191
|
lara_as_sprint_slide,//191
|
||||||
};
|
};
|
||||||
|
|
||||||
std::function<LaraRoutineFunction> lara_collision_routines[NUM_LARA_STATES + 1] =
|
auto PlayerStateCollisionRoutines = std::array<PlayerStateRoutine, NUM_LARA_STATES + 1>
|
||||||
{
|
{
|
||||||
lara_col_walk_forward,
|
lara_col_walk_forward,
|
||||||
lara_col_run_forward,
|
lara_col_run_forward,
|
||||||
|
@ -287,8 +290,8 @@ std::function<LaraRoutineFunction> lara_collision_routines[NUM_LARA_STATES + 1]
|
||||||
lara_col_surface_idle,//33
|
lara_col_surface_idle,//33
|
||||||
lara_col_surface_swim_forward,//34
|
lara_col_surface_swim_forward,//34
|
||||||
lara_col_surface_dive,//35
|
lara_col_surface_dive,//35
|
||||||
lara_default_col,
|
lara_void_func,//36
|
||||||
lara_default_col,
|
lara_void_func,//37
|
||||||
lara_default_col,
|
lara_default_col,
|
||||||
lara_default_col,
|
lara_default_col,
|
||||||
lara_default_col,
|
lara_default_col,
|
||||||
|
@ -916,7 +919,7 @@ void LaraAboveWater(ItemInfo* item, CollisionInfo* coll)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Handle player state.
|
// Handle player state.
|
||||||
lara_control_routines[item->Animation.ActiveState](item, coll);
|
PlayerStateControlRoutines[item->Animation.ActiveState](item, coll);
|
||||||
|
|
||||||
HandleLaraMovementParameters(item, coll);
|
HandleLaraMovementParameters(item, coll);
|
||||||
AnimateItem(item);
|
AnimateItem(item);
|
||||||
|
@ -928,7 +931,7 @@ void LaraAboveWater(ItemInfo* item, CollisionInfo* coll)
|
||||||
|
|
||||||
// Handle player state collision.
|
// Handle player state collision.
|
||||||
if (lara->Context.Vehicle == NO_ITEM)
|
if (lara->Context.Vehicle == NO_ITEM)
|
||||||
lara_collision_routines[item->Animation.ActiveState](item, coll);
|
PlayerStateCollisionRoutines[item->Animation.ActiveState](item, coll);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle weapons.
|
// Handle weapons.
|
||||||
|
@ -964,7 +967,7 @@ void LaraWaterSurface(ItemInfo* item, CollisionInfo* coll)
|
||||||
coll->Setup.BlockCeilingSlope = false;
|
coll->Setup.BlockCeilingSlope = false;
|
||||||
coll->Setup.BlockDeathFloorDown = false;
|
coll->Setup.BlockDeathFloorDown = false;
|
||||||
coll->Setup.BlockMonkeySwingEdge = false;
|
coll->Setup.BlockMonkeySwingEdge = false;
|
||||||
coll->Setup.EnableObjectPush = false;
|
coll->Setup.EnableObjectPush = true;
|
||||||
coll->Setup.EnableSpasm = false;
|
coll->Setup.EnableSpasm = false;
|
||||||
coll->Setup.PrevPosition = item->Pose.Position;
|
coll->Setup.PrevPosition = item->Pose.Position;
|
||||||
|
|
||||||
|
@ -979,7 +982,7 @@ void LaraWaterSurface(ItemInfo* item, CollisionInfo* coll)
|
||||||
|
|
||||||
lara->Control.Count.Pose = 0;
|
lara->Control.Count.Pose = 0;
|
||||||
|
|
||||||
lara_control_routines[item->Animation.ActiveState](item, coll);
|
PlayerStateControlRoutines[item->Animation.ActiveState](item, coll);
|
||||||
|
|
||||||
auto* level = g_GameFlow->GetLevel(CurrentLevel);
|
auto* level = g_GameFlow->GetLevel(CurrentLevel);
|
||||||
|
|
||||||
|
@ -1005,7 +1008,7 @@ void LaraWaterSurface(ItemInfo* item, CollisionInfo* coll)
|
||||||
DoObjectCollision(item, coll);
|
DoObjectCollision(item, coll);
|
||||||
|
|
||||||
if (lara->Context.Vehicle == NO_ITEM)
|
if (lara->Context.Vehicle == NO_ITEM)
|
||||||
lara_collision_routines[item->Animation.ActiveState](item, coll);
|
PlayerStateCollisionRoutines[item->Animation.ActiveState](item, coll);
|
||||||
|
|
||||||
UpdateLaraRoom(item, LARA_RADIUS);
|
UpdateLaraRoom(item, LARA_RADIUS);
|
||||||
|
|
||||||
|
@ -1050,7 +1053,7 @@ void LaraUnderwater(ItemInfo* item, CollisionInfo* coll)
|
||||||
|
|
||||||
lara->Control.Count.Pose = 0;
|
lara->Control.Count.Pose = 0;
|
||||||
|
|
||||||
lara_control_routines[item->Animation.ActiveState](item, coll);
|
PlayerStateControlRoutines[item->Animation.ActiveState](item, coll);
|
||||||
|
|
||||||
auto* level = g_GameFlow->GetLevel(CurrentLevel);
|
auto* level = g_GameFlow->GetLevel(CurrentLevel);
|
||||||
|
|
||||||
|
@ -1095,7 +1098,7 @@ void LaraUnderwater(ItemInfo* item, CollisionInfo* coll)
|
||||||
DoObjectCollision(item, coll);
|
DoObjectCollision(item, coll);
|
||||||
|
|
||||||
if (/*lara->ExtraAnim == -1 &&*/ lara->Context.Vehicle == NO_ITEM)
|
if (/*lara->ExtraAnim == -1 &&*/ lara->Context.Vehicle == NO_ITEM)
|
||||||
lara_collision_routines[item->Animation.ActiveState](item, coll);
|
PlayerStateCollisionRoutines[item->Animation.ActiveState](item, coll);
|
||||||
|
|
||||||
UpdateLaraRoom(item, 0);
|
UpdateLaraRoom(item, 0);
|
||||||
|
|
||||||
|
@ -1186,8 +1189,8 @@ bool UpdateLaraRoom(ItemInfo* item, int height, int xOffset, int zOffset)
|
||||||
auto point = Geometry::TranslatePoint(item->Pose.Position, item->Pose.Orientation.y, zOffset, height, xOffset);
|
auto point = Geometry::TranslatePoint(item->Pose.Position, item->Pose.Orientation.y, zOffset, height, xOffset);
|
||||||
|
|
||||||
// Hacky L-shaped Location traversal.
|
// Hacky L-shaped Location traversal.
|
||||||
item->Location = GetRoom(item->Location, point.x, point.y, point.z);
|
item->Location = GetRoom(item->Location, point);
|
||||||
item->Location = GetRoom(item->Location, item->Pose.Position.x, point.y, item->Pose.Position.z);
|
item->Location = GetRoom(item->Location, Vector3i(item->Pose.Position.x, point.y, item->Pose.Position.z));
|
||||||
item->Floor = GetFloorHeight(item->Location, item->Pose.Position.x, item->Pose.Position.z).value_or(NO_HEIGHT);
|
item->Floor = GetFloorHeight(item->Location, item->Pose.Position.x, item->Pose.Position.z).value_or(NO_HEIGHT);
|
||||||
|
|
||||||
if (item->RoomNumber != item->Location.roomNumber)
|
if (item->RoomNumber != item->Location.roomNumber)
|
||||||
|
|
|
@ -78,15 +78,15 @@ constexpr auto LARA_HEALTH_MAX = 1000.0f;
|
||||||
constexpr auto LARA_HEALTH_CRITICAL = LARA_HEALTH_MAX / 4;
|
constexpr auto LARA_HEALTH_CRITICAL = LARA_HEALTH_MAX / 4;
|
||||||
constexpr auto LARA_POISON_MAX = 128.0f;
|
constexpr auto LARA_POISON_MAX = 128.0f;
|
||||||
constexpr auto LARA_STAMINA_MAX = 120.0f;
|
constexpr auto LARA_STAMINA_MAX = 120.0f;
|
||||||
constexpr auto LARA_STAMINA_CRITICAL = LARA_STAMINA_MAX / 2;
|
|
||||||
constexpr auto LARA_STAMINA_MIN = LARA_STAMINA_MAX / 10;
|
constexpr auto LARA_STAMINA_MIN = LARA_STAMINA_MAX / 10;
|
||||||
|
constexpr auto LARA_STAMINA_CRITICAL = LARA_STAMINA_MAX / 2;
|
||||||
|
|
||||||
constexpr auto PLAYER_DRIP_NODE_MAX = 64.0f;
|
constexpr auto PLAYER_DRIP_NODE_MAX = 64.0f;
|
||||||
constexpr auto PLAYER_BUBBLE_NODE_MAX = 12.0f;
|
constexpr auto PLAYER_BUBBLE_NODE_MAX = 12.0f;
|
||||||
|
|
||||||
constexpr auto STEPUP_HEIGHT = (int)CLICK(3.0f / 2);
|
constexpr auto STEPUP_HEIGHT = (int)CLICK(3 / 2.0f);
|
||||||
constexpr auto BAD_JUMP_CEILING = (int)CLICK(6.0f / 8);
|
constexpr auto BAD_JUMP_CEILING = (int)CLICK(6 / 8.0f);
|
||||||
constexpr auto SHALLOW_WATER_DEPTH = (int)CLICK(1.0f / 2);
|
constexpr auto SHALLOW_WATER_DEPTH = (int)CLICK(1 / 2.0f);
|
||||||
constexpr auto WADE_DEPTH = STEPUP_HEIGHT;
|
constexpr auto WADE_DEPTH = STEPUP_HEIGHT;
|
||||||
constexpr auto SWIM_DEPTH = CLICK(3) - 38;
|
constexpr auto SWIM_DEPTH = CLICK(3) - 38;
|
||||||
constexpr auto SLOPE_DIFFERENCE = 60;
|
constexpr auto SLOPE_DIFFERENCE = 60;
|
||||||
|
@ -95,14 +95,6 @@ extern LaraInfo Lara;
|
||||||
extern ItemInfo* LaraItem;
|
extern ItemInfo* LaraItem;
|
||||||
extern CollisionInfo LaraCollision;
|
extern CollisionInfo LaraCollision;
|
||||||
|
|
||||||
#define LARA_MESHES(slot, mesh) Lara.meshPtrs[mesh] = MESHES(slot, mesh)
|
|
||||||
#define CHECK_LARA_MESHES(slot, mesh) Lara.meshPtrs[mesh] == MESHES(slot, mesh)
|
|
||||||
#define INIT_LARA_MESHES(mesh, to, from) Lara.meshPtrs[mesh] = LARA_MESHES(to, mesh) = LARA_MESHES(from, mesh)
|
|
||||||
|
|
||||||
#define LaraRoutineFunction void(ItemInfo* item, CollisionInfo* coll)
|
|
||||||
extern std::function<LaraRoutineFunction> lara_control_routines[NUM_LARA_STATES + 1];
|
|
||||||
extern std::function<LaraRoutineFunction> lara_collision_routines[NUM_LARA_STATES + 1];
|
|
||||||
|
|
||||||
void LaraControl(ItemInfo* item, CollisionInfo* coll);
|
void LaraControl(ItemInfo* item, CollisionInfo* coll);
|
||||||
void LaraAboveWater(ItemInfo* item, CollisionInfo* coll);
|
void LaraAboveWater(ItemInfo* item, CollisionInfo* coll);
|
||||||
void LaraWaterSurface(ItemInfo* item, CollisionInfo* coll);
|
void LaraWaterSurface(ItemInfo* item, CollisionInfo* coll);
|
||||||
|
@ -110,4 +102,4 @@ void LaraUnderwater(ItemInfo* item, CollisionInfo* coll);
|
||||||
void LaraCheat(ItemInfo* item, CollisionInfo* coll);
|
void LaraCheat(ItemInfo* item, CollisionInfo* coll);
|
||||||
void AnimateItem(ItemInfo* item);
|
void AnimateItem(ItemInfo* item);
|
||||||
void UpdateLara(ItemInfo* item, bool isTitle);
|
void UpdateLara(ItemInfo* item, bool isTitle);
|
||||||
bool UpdateLaraRoom(ItemInfo* item, int height, int xOffset = 0, int zOffset = 0);
|
bool UpdateLaraRoom(ItemInfo* item, int height, int xOffset = 0, int zOffset = 0);
|
||||||
|
|
|
@ -433,17 +433,15 @@ void LaraSnapToHeight(ItemInfo* item, CollisionInfo* coll)
|
||||||
void GetLaraDeadlyBounds()
|
void GetLaraDeadlyBounds()
|
||||||
{
|
{
|
||||||
auto bounds = GameBoundingBox(LaraItem);
|
auto bounds = GameBoundingBox(LaraItem);
|
||||||
auto tBounds = GameBoundingBox::Zero;
|
bounds.Rotate(LaraItem->Pose.Orientation);
|
||||||
tBounds.RotateNoPersp(LaraItem->Pose.Orientation, bounds);
|
|
||||||
|
|
||||||
DeadlyBounds = GameBoundingBox(
|
DeadlyBounds = GameBoundingBox(
|
||||||
LaraItem->Pose.Position.x + tBounds.X1,
|
LaraItem->Pose.Position.x + bounds.X1,
|
||||||
LaraItem->Pose.Position.x + tBounds.X2,
|
LaraItem->Pose.Position.x + bounds.X2,
|
||||||
LaraItem->Pose.Position.y + tBounds.Y1,
|
LaraItem->Pose.Position.y + bounds.Y1,
|
||||||
LaraItem->Pose.Position.y + tBounds.Y2,
|
LaraItem->Pose.Position.y + bounds.Y2,
|
||||||
LaraItem->Pose.Position.z + tBounds.Z1,
|
LaraItem->Pose.Position.z + bounds.Z1,
|
||||||
LaraItem->Pose.Position.z + tBounds.Z2
|
LaraItem->Pose.Position.z + bounds.Z2);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void LaraJumpCollision(ItemInfo* item, CollisionInfo* coll, short moveAngle)
|
void LaraJumpCollision(ItemInfo* item, CollisionInfo* coll, short moveAngle)
|
||||||
|
|
|
@ -357,6 +357,17 @@ static void SetPlayerOptics(ItemInfo* item)
|
||||||
AlterFOV(LastFOV);
|
AlterFOV(LastFOV);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static short NormalizeLookAroundTurnRate(short turnRate, short opticRange)
|
||||||
|
{
|
||||||
|
constexpr auto ZOOM_LEVEL_MAX = ANGLE(10.0f);
|
||||||
|
constexpr auto ZOOM_LEVEL_REF = ANGLE(17.0f);
|
||||||
|
|
||||||
|
if (opticRange == 0)
|
||||||
|
return turnRate;
|
||||||
|
|
||||||
|
return short(turnRate * (ZOOM_LEVEL_MAX - opticRange) / ZOOM_LEVEL_REF);
|
||||||
|
};
|
||||||
|
|
||||||
void HandlePlayerLookAround(ItemInfo& item, bool invertXAxis)
|
void HandlePlayerLookAround(ItemInfo& item, bool invertXAxis)
|
||||||
{
|
{
|
||||||
constexpr auto OPTIC_RANGE_MAX = ANGLE(8.5f);
|
constexpr auto OPTIC_RANGE_MAX = ANGLE(8.5f);
|
||||||
|
@ -365,17 +376,6 @@ void HandlePlayerLookAround(ItemInfo& item, bool invertXAxis)
|
||||||
constexpr auto TURN_RATE_MAX = ANGLE(4.0f);
|
constexpr auto TURN_RATE_MAX = ANGLE(4.0f);
|
||||||
constexpr auto TURN_RATE_ACCEL = ANGLE(0.75f);
|
constexpr auto TURN_RATE_ACCEL = ANGLE(0.75f);
|
||||||
|
|
||||||
auto normalizeTurnRate = [](short turnRate, short opticRange)
|
|
||||||
{
|
|
||||||
constexpr auto ZOOM_LEVEL_MAX = ANGLE(10.0f);
|
|
||||||
constexpr auto ZOOM_LEVEL_REF = ANGLE(17.0f);
|
|
||||||
|
|
||||||
if (opticRange == 0)
|
|
||||||
return turnRate;
|
|
||||||
|
|
||||||
return short(turnRate * (ZOOM_LEVEL_MAX - opticRange) / ZOOM_LEVEL_REF);
|
|
||||||
};
|
|
||||||
|
|
||||||
auto& player = GetLaraInfo(item);
|
auto& player = GetLaraInfo(item);
|
||||||
|
|
||||||
if (!CanPlayerLookAround(item))
|
if (!CanPlayerLookAround(item))
|
||||||
|
@ -427,14 +427,14 @@ void HandlePlayerLookAround(ItemInfo& item, bool invertXAxis)
|
||||||
if ((IsHeld(In::Forward) || IsHeld(In::Back)) &&
|
if ((IsHeld(In::Forward) || IsHeld(In::Back)) &&
|
||||||
(player.Control.Look.Mode == LookMode::Free || player.Control.Look.Mode == LookMode::Vertical))
|
(player.Control.Look.Mode == LookMode::Free || player.Control.Look.Mode == LookMode::Vertical))
|
||||||
{
|
{
|
||||||
axisCoeff.x = AxisMap[InputAxis::MoveVertical];
|
axisCoeff.x = AxisMap[(int)InputAxis::Move].y;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine Y axis coefficient.
|
// Determine Y axis coefficient.
|
||||||
if ((IsHeld(In::Left) || IsHeld(In::Right)) &&
|
if ((IsHeld(In::Left) || IsHeld(In::Right)) &&
|
||||||
(player.Control.Look.Mode == LookMode::Free || player.Control.Look.Mode == LookMode::Horizontal))
|
(player.Control.Look.Mode == LookMode::Free || player.Control.Look.Mode == LookMode::Horizontal))
|
||||||
{
|
{
|
||||||
axisCoeff.y = AxisMap[InputAxis::MoveHorizontal];
|
axisCoeff.y = AxisMap[(int)InputAxis::Move].x;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine turn rate base values.
|
// Determine turn rate base values.
|
||||||
|
@ -442,8 +442,8 @@ void HandlePlayerLookAround(ItemInfo& item, bool invertXAxis)
|
||||||
short turnRateAccel = isSlow ? (TURN_RATE_ACCEL / 2) : TURN_RATE_ACCEL;
|
short turnRateAccel = isSlow ? (TURN_RATE_ACCEL / 2) : TURN_RATE_ACCEL;
|
||||||
|
|
||||||
// Normalize turn rate base values.
|
// Normalize turn rate base values.
|
||||||
turnRateMax = normalizeTurnRate(turnRateMax, player.Control.Look.OpticRange);
|
turnRateMax = NormalizeLookAroundTurnRate(turnRateMax, player.Control.Look.OpticRange);
|
||||||
turnRateAccel = normalizeTurnRate(turnRateAccel, player.Control.Look.OpticRange);
|
turnRateAccel = NormalizeLookAroundTurnRate(turnRateAccel, player.Control.Look.OpticRange);
|
||||||
|
|
||||||
// Modulate turn rates.
|
// Modulate turn rates.
|
||||||
player.Control.Look.TurnRate = EulerAngles(
|
player.Control.Look.TurnRate = EulerAngles(
|
||||||
|
@ -539,8 +539,8 @@ void HandlePlayerWetnessDrips(ItemInfo& item)
|
||||||
int jointIndex = 0;
|
int jointIndex = 0;
|
||||||
for (auto& node : player.Effect.DripNodes)
|
for (auto& node : player.Effect.DripNodes)
|
||||||
{
|
{
|
||||||
auto pos = GetJointPosition(&item, jointIndex).ToVector3();
|
auto pos = GetJointPosition(&item, jointIndex);
|
||||||
int roomNumber = GetRoom(item.Location, pos.x, pos.y, pos.z).roomNumber;
|
int roomNumber = GetRoom(item.Location, pos).roomNumber;
|
||||||
jointIndex++;
|
jointIndex++;
|
||||||
|
|
||||||
// Node underwater; set max wetness value.
|
// Node underwater; set max wetness value.
|
||||||
|
@ -558,7 +558,7 @@ void HandlePlayerWetnessDrips(ItemInfo& item)
|
||||||
float chance = (node / PLAYER_DRIP_NODE_MAX) / 2;
|
float chance = (node / PLAYER_DRIP_NODE_MAX) / 2;
|
||||||
if (Random::TestProbability(chance))
|
if (Random::TestProbability(chance))
|
||||||
{
|
{
|
||||||
SpawnWetnessDrip(pos, item.RoomNumber);
|
SpawnWetnessDrip(pos.ToVector3(), item.RoomNumber);
|
||||||
|
|
||||||
node -= 1.0f;
|
node -= 1.0f;
|
||||||
if (node <= 0.0f)
|
if (node <= 0.0f)
|
||||||
|
@ -576,8 +576,8 @@ void HandlePlayerDiveBubbles(ItemInfo& item)
|
||||||
int jointIndex = 0;
|
int jointIndex = 0;
|
||||||
for (auto& node : player.Effect.BubbleNodes)
|
for (auto& node : player.Effect.BubbleNodes)
|
||||||
{
|
{
|
||||||
auto pos = GetJointPosition(&item, jointIndex).ToVector3();
|
auto pos = GetJointPosition(&item, jointIndex);
|
||||||
int roomNumber = GetRoom(item.Location, pos.x, pos.y, pos.z).roomNumber;
|
int roomNumber = GetRoom(item.Location, pos).roomNumber;
|
||||||
jointIndex++;
|
jointIndex++;
|
||||||
|
|
||||||
// Node inactive; continue.
|
// Node inactive; continue.
|
||||||
|
@ -593,7 +593,7 @@ void HandlePlayerDiveBubbles(ItemInfo& item)
|
||||||
if (Random::TestProbability(chance))
|
if (Random::TestProbability(chance))
|
||||||
{
|
{
|
||||||
unsigned int count = (int)round(node * BUBBLE_COUNT_MULT);
|
unsigned int count = (int)round(node * BUBBLE_COUNT_MULT);
|
||||||
SpawnDiveBubbles(pos, roomNumber, count);
|
SpawnDiveBubbles(pos.ToVector3(), roomNumber, count);
|
||||||
|
|
||||||
node -= 1.0f;
|
node -= 1.0f;
|
||||||
if (node <= 0.0f)
|
if (node <= 0.0f)
|
||||||
|
@ -862,14 +862,14 @@ void ModulateLaraTurnRateX(ItemInfo* item, short accelRate, short minTurnRate, s
|
||||||
{
|
{
|
||||||
auto* lara = GetLaraInfo(item);
|
auto* lara = GetLaraInfo(item);
|
||||||
|
|
||||||
//lara->Control.TurnRate.x = ModulateLaraTurnRate(lara->Control.TurnRate.x, accelRate, minTurnRate, maxTurnRate, AxisMap[InputAxis::MoveVertical], invert);
|
//lara->Control.TurnRate.x = ModulateLaraTurnRate(lara->Control.TurnRate.x, accelRate, minTurnRate, maxTurnRate, AxisMap[InputAxis::Move].y, invert);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModulateLaraTurnRateY(ItemInfo* item, short accelRate, short minTurnRate, short maxTurnRate, bool invert)
|
void ModulateLaraTurnRateY(ItemInfo* item, short accelRate, short minTurnRate, short maxTurnRate, bool invert)
|
||||||
{
|
{
|
||||||
auto* lara = GetLaraInfo(item);
|
auto* lara = GetLaraInfo(item);
|
||||||
|
|
||||||
float axisCoeff = AxisMap[InputAxis::MoveHorizontal];
|
float axisCoeff = AxisMap[(int)InputAxis::Move].x;
|
||||||
if (item->Animation.IsAirborne)
|
if (item->Animation.IsAirborne)
|
||||||
{
|
{
|
||||||
int sign = std::copysign(1, axisCoeff);
|
int sign = std::copysign(1, axisCoeff);
|
||||||
|
@ -1000,7 +1000,7 @@ void ModulateLaraLean(ItemInfo* item, CollisionInfo* coll, short baseRate, short
|
||||||
if (!item->Animation.Velocity.z)
|
if (!item->Animation.Velocity.z)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
float axisCoeff = AxisMap[InputAxis::MoveHorizontal];
|
float axisCoeff = AxisMap[(int)InputAxis::Move].x;
|
||||||
int sign = copysign(1, axisCoeff);
|
int sign = copysign(1, axisCoeff);
|
||||||
short maxAngleNormalized = maxAngle * axisCoeff;
|
short maxAngleNormalized = maxAngle * axisCoeff;
|
||||||
|
|
||||||
|
@ -1017,7 +1017,7 @@ void ModulateLaraCrawlFlex(ItemInfo* item, short baseRate, short maxAngle)
|
||||||
if (!item->Animation.Velocity.z)
|
if (!item->Animation.Velocity.z)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
float axisCoeff = AxisMap[InputAxis::MoveHorizontal];
|
float axisCoeff = AxisMap[(int)InputAxis::Move].x;
|
||||||
int sign = copysign(1, axisCoeff);
|
int sign = copysign(1, axisCoeff);
|
||||||
short maxAngleNormalized = maxAngle * axisCoeff;
|
short maxAngleNormalized = maxAngle * axisCoeff;
|
||||||
|
|
||||||
|
|
|
@ -157,36 +157,36 @@ void lara_as_use_puzzle(ItemInfo* item, CollisionInfo* coll)
|
||||||
// PUSHABLE
|
// PUSHABLE
|
||||||
// --------
|
// --------
|
||||||
|
|
||||||
// State: LS_PUSHABLE_PUSH (36)
|
// State: LS_PUSHABLE_PUSH (36)
|
||||||
// Collision: lara_default_col()
|
// Collision: lara_void_func()
|
||||||
void lara_as_pushable_push(ItemInfo* item, CollisionInfo* coll)
|
void lara_as_pushable_push(ItemInfo* item, CollisionInfo* coll)
|
||||||
{
|
{
|
||||||
auto* lara = GetLaraInfo(item);
|
auto& player = GetLaraInfo(*item);
|
||||||
|
|
||||||
lara->Control.Look.Mode = LookMode::None;
|
player.Control.Look.Mode = LookMode::None;
|
||||||
coll->Setup.EnableObjectPush = false;
|
coll->Setup.EnableObjectPush = false;
|
||||||
coll->Setup.EnableSpasm = false;
|
coll->Setup.EnableSpasm = false;
|
||||||
Camera.targetAngle = ANGLE(35.0f);
|
Camera.targetAngle = ANGLE(35.0f);
|
||||||
Camera.targetElevation = -ANGLE(25.0f);
|
Camera.targetElevation = ANGLE(-25.0f);
|
||||||
Camera.flags = CF_FOLLOW_CENTER;
|
Camera.flags = CF_FOLLOW_CENTER;
|
||||||
}
|
}
|
||||||
|
|
||||||
// State: LS_PUSHABLE_PULL (37)
|
// State: LS_PUSHABLE_PULL (37)
|
||||||
// Collision: lara_default_col()
|
// Collision: lara_void_func()
|
||||||
void lara_as_pushable_pull(ItemInfo* item, CollisionInfo* coll)
|
void lara_as_pushable_pull(ItemInfo* item, CollisionInfo* coll)
|
||||||
{
|
{
|
||||||
auto* lara = GetLaraInfo(item);
|
auto& player = GetLaraInfo(*item);
|
||||||
|
|
||||||
lara->Control.Look.Mode = LookMode::None;
|
player.Control.Look.Mode = LookMode::None;
|
||||||
coll->Setup.EnableObjectPush = false;
|
coll->Setup.EnableObjectPush = false;
|
||||||
coll->Setup.EnableSpasm = false;
|
coll->Setup.EnableSpasm = false;
|
||||||
Camera.targetAngle = ANGLE(35.0f);
|
Camera.targetAngle = ANGLE(35.0f);
|
||||||
Camera.targetElevation = -ANGLE(25.0f);
|
Camera.targetElevation = ANGLE(-25.0f);
|
||||||
Camera.flags = CF_FOLLOW_CENTER;
|
Camera.flags = CF_FOLLOW_CENTER;
|
||||||
}
|
}
|
||||||
|
|
||||||
// State: LS_PUSHABLE_GRAB (38)
|
// State: LS_PUSHABLE_GRAB (38)
|
||||||
// Collision: lara_default_col()
|
// Collision: lara_default_col()
|
||||||
void lara_as_pushable_grab(ItemInfo* item, CollisionInfo* coll)
|
void lara_as_pushable_grab(ItemInfo* item, CollisionInfo* coll)
|
||||||
{
|
{
|
||||||
coll->Setup.EnableObjectPush = false;
|
coll->Setup.EnableObjectPush = false;
|
||||||
|
@ -197,6 +197,20 @@ void lara_as_pushable_grab(ItemInfo* item, CollisionInfo* coll)
|
||||||
item->Animation.TargetState = LS_IDLE;
|
item->Animation.TargetState = LS_IDLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// State: LS_PUSHABLE_PUSH_EDGE_SLIP (190)
|
||||||
|
// Collision: lara_void_func()
|
||||||
|
void lara_as_pushable_edge_slip(ItemInfo* item, CollisionInfo* coll)
|
||||||
|
{
|
||||||
|
auto& player = GetLaraInfo(*item);
|
||||||
|
|
||||||
|
player.Control.Look.Mode = LookMode::None;
|
||||||
|
coll->Setup.EnableObjectPush = false;
|
||||||
|
coll->Setup.EnableSpasm = false;
|
||||||
|
Camera.targetAngle = ANGLE(35.0f);
|
||||||
|
Camera.targetElevation = ANGLE(-25.0f);
|
||||||
|
Camera.flags = CF_FOLLOW_CENTER;
|
||||||
|
}
|
||||||
|
|
||||||
// ------
|
// ------
|
||||||
// PULLEY
|
// PULLEY
|
||||||
// ------
|
// ------
|
||||||
|
|
|
@ -37,6 +37,7 @@ void lara_as_use_puzzle(ItemInfo* item, CollisionInfo* coll);
|
||||||
void lara_as_pushable_push(ItemInfo* item, CollisionInfo* coll);
|
void lara_as_pushable_push(ItemInfo* item, CollisionInfo* coll);
|
||||||
void lara_as_pushable_pull(ItemInfo* item, CollisionInfo* coll);
|
void lara_as_pushable_pull(ItemInfo* item, CollisionInfo* coll);
|
||||||
void lara_as_pushable_grab(ItemInfo* item, CollisionInfo* coll);
|
void lara_as_pushable_grab(ItemInfo* item, CollisionInfo* coll);
|
||||||
|
void lara_as_pushable_edge_slip(ItemInfo* item, CollisionInfo* coll);
|
||||||
|
|
||||||
// ------
|
// ------
|
||||||
// PULLEY
|
// PULLEY
|
||||||
|
|
|
@ -1569,7 +1569,7 @@ void HandleProjectile(ItemInfo& projectile, ItemInfo& emitter, const Vector3i& p
|
||||||
hasHit = hasHitNotByEmitter = doShatter = true;
|
hasHit = hasHitNotByEmitter = doShatter = true;
|
||||||
doExplosion = isExplosive;
|
doExplosion = isExplosive;
|
||||||
|
|
||||||
if (StaticObjects[meshPtr->staticNumber].shatterType == SHT_NONE)
|
if (StaticObjects[meshPtr->staticNumber].shatterType == ShatterType::None)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
meshPtr->HitPoints -= damage;
|
meshPtr->HitPoints -= damage;
|
||||||
|
@ -1625,7 +1625,7 @@ void HandleProjectile(ItemInfo& projectile, ItemInfo& emitter, const Vector3i& p
|
||||||
if (type != ProjectileType::FlashGrenade && currentObject.damageType != DamageMode::None)
|
if (type != ProjectileType::FlashGrenade && currentObject.damageType != DamageMode::None)
|
||||||
DoExplosiveDamage(emitter, *itemPtr, projectile, damage);
|
DoExplosiveDamage(emitter, *itemPtr, projectile, damage);
|
||||||
}
|
}
|
||||||
else if (currentObject.damageType == DamageMode::AnyWeapon)
|
else if (currentObject.damageType == DamageMode::Any)
|
||||||
{
|
{
|
||||||
if (type == ProjectileType::Poison)
|
if (type == ProjectileType::Poison)
|
||||||
{
|
{
|
||||||
|
|
|
@ -208,10 +208,10 @@ enum LaraState
|
||||||
LS_CRAWL_TURN_180 = 172,
|
LS_CRAWL_TURN_180 = 172,
|
||||||
LS_TURN_180 = 173,
|
LS_TURN_180 = 173,
|
||||||
|
|
||||||
// 174-188 reserved for "true" ladders. -- Sezz 2023.04.16
|
// 174-188 reserved for "true" ladders. DO NOT add anything here. -- Sezz 2023.04.16
|
||||||
|
|
||||||
LS_REMOVE_PUZZLE = 189,
|
LS_REMOVE_PUZZLE = 189,
|
||||||
LS_RESERVED_PUSHABLE_STATE = 190,
|
LS_PUSHABLE_EDGE_SLIP = 190,
|
||||||
LS_SPRINT_SLIDE = 191,
|
LS_SPRINT_SLIDE = 191,
|
||||||
|
|
||||||
NUM_LARA_STATES
|
NUM_LARA_STATES
|
||||||
|
@ -346,8 +346,8 @@ enum LaraAnim
|
||||||
LA_ONWATER_DIVE = 119, // Tread water > underwater
|
LA_ONWATER_DIVE = 119, // Tread water > underwater
|
||||||
LA_PUSHABLE_GRAB = 120, // Grab pushable object (looped)
|
LA_PUSHABLE_GRAB = 120, // Grab pushable object (looped)
|
||||||
LA_PUSHABLE_RELEASE = 121, // Release pushable object
|
LA_PUSHABLE_RELEASE = 121, // Release pushable object
|
||||||
LA_PUSHABLE_PULL = 122, // Pull pushable object (looped)
|
LA_PUSHABLE_OBJECT_PULL = 122, // Pull pushable object (looped)
|
||||||
LA_PUSHABLE_PUSH = 123, // Push pushable object (looped)
|
LA_PUSHABLE_OBJECT_PUSH = 123, // Push pushable object (looped)
|
||||||
LA_UNDERWATER_DEATH = 124, // Drowning death
|
LA_UNDERWATER_DEATH = 124, // Drowning death
|
||||||
LA_STAND_HIT_FRONT = 125, // Jerk back standing from damage
|
LA_STAND_HIT_FRONT = 125, // Jerk back standing from damage
|
||||||
LA_STAND_HIT_BACK = 126, // Jerk forward standing from damage
|
LA_STAND_HIT_BACK = 126, // Jerk forward standing from damage
|
||||||
|
@ -578,9 +578,9 @@ enum LaraAnim
|
||||||
LA_PULLEY_PULL = 340, // Pull pulley
|
LA_PULLEY_PULL = 340, // Pull pulley
|
||||||
LA_PULLEY_RELEASE = 341, // Pull pulley > stand
|
LA_PULLEY_RELEASE = 341, // Pull pulley > stand
|
||||||
LA_POLE_TO_STAND = 342, // Pole > stand
|
LA_POLE_TO_STAND = 342, // Pole > stand
|
||||||
LA_POLE_TURN_CLOCKWISE_CONTINUE_UNUSED = 343, // TODO: remove.
|
LA_PUSHABLE_BLOCK_PULL = 343, // Pull pushable block
|
||||||
LA_POLE_TURN_CLOCKWISE_END = 344, // Rotate clockwise on pole (2/2)
|
LA_POLE_TURN_CLOCKWISE_END = 344, // Rotate clockwise on pole (2/2)
|
||||||
LA_POLE_TURN_COUNTER_CLOCKWISE_CONTINUE_UNUSED = 345, // TODO: remove.
|
LA_PUSHABLE_BLOCK_PUSH = 345, // Push pushable block
|
||||||
LA_POLE_TURN_COUNTER_CLOCKWISE_END = 346, // Rotate counter-clockwise on pole (2/2)
|
LA_POLE_TURN_COUNTER_CLOCKWISE_END = 346, // Rotate counter-clockwise on pole (2/2)
|
||||||
LA_TURNSWITCH_PUSH_CLOCKWISE_START = 347, // Push turnswitch clockwise (1/3)
|
LA_TURNSWITCH_PUSH_CLOCKWISE_START = 347, // Push turnswitch clockwise (1/3)
|
||||||
LA_TURNSWITCH_PUSH_CLOCKWISE_CONTINUE = 348, // Push turnswitch clockwise (2/3)
|
LA_TURNSWITCH_PUSH_CLOCKWISE_CONTINUE = 348, // Push turnswitch clockwise (2/3)
|
||||||
|
@ -598,14 +598,14 @@ enum LaraAnim
|
||||||
LA_SHIMMY_LEFT_CORNER_INNER_45 = 360, // Shimmy around inner left corner (45)
|
LA_SHIMMY_LEFT_CORNER_INNER_45 = 360, // Shimmy around inner left corner (45)
|
||||||
LA_SHIMMY_RIGHT_CORNER_INNER_90 = 361, // Shimmy around inner right corner (90)
|
LA_SHIMMY_RIGHT_CORNER_INNER_90 = 361, // Shimmy around inner right corner (90)
|
||||||
LA_SHIMMY_RIGHT_CORNER_INNER_45 = 362, // Shimmy around inner right corner (45)
|
LA_SHIMMY_RIGHT_CORNER_INNER_45 = 362, // Shimmy around inner right corner (45)
|
||||||
LA_LADDER_LEFT_CORNER_OUTER_START = 363, // Ladder around outer left corner (1/2)
|
LA_LADDER_LEFT_CORNER_OUTER_START = 363, // Ladder around outer left corner
|
||||||
LA_LADDER_LEFT_CORNER_OUTER_END = 364, // Ladder around outer left corner (2/2)
|
LA_PUSHABLE_OBJECT_PUSH_EDGE_SLIP = 364,
|
||||||
LA_LADDER_RIGHT_CORNER_OUTER_START = 365, // Ladder around outer right corner (1/2)
|
LA_LADDER_RIGHT_CORNER_OUTER_START = 365, // Ladder around outer right corner
|
||||||
LA_LADDER_RIGHT_CORNER_OUTER_END = 366, // Ladder around outer right corner (2/2)
|
LA_PUSHABLE_BLOCK_PUSH_EDGE_SLIP = 366,
|
||||||
LA_LADDER_LEFT_CORNER_INNER_START = 367, // Ladder around inner left corner (1/2)
|
LA_LADDER_LEFT_CORNER_INNER_START = 367, // Ladder around inner left corner
|
||||||
LA_LADDER_LEFT_CORNER_INNER_END = 368, // Ladder around inner left corner (2/2)
|
LA_LADDER_LEFT_CORNER_INNER_END = 368, // TODO: Remove.
|
||||||
LA_LADDER_RIGHT_CORNER_INNER_START = 369, // Ladder around inner right corner (1/2)
|
LA_LADDER_RIGHT_CORNER_INNER_START = 369, // Ladder around inner right corner
|
||||||
LA_LADDER_RIGHT_CORNER_INNER_END = 370, // Ladder around inner right corner (2/2)
|
LA_LADDER_RIGHT_CORNER_INNER_END = 370, // TODO: Remove.
|
||||||
LA_JUMP_UP_TO_ROPE_START = 371, // Jump up > rope idle (1/2)
|
LA_JUMP_UP_TO_ROPE_START = 371, // Jump up > rope idle (1/2)
|
||||||
LA_TRAIN_OVERBOARD_DEATH = 372, // Train overboard death
|
LA_TRAIN_OVERBOARD_DEATH = 372, // Train overboard death
|
||||||
LA_JUMP_UP_TO_ROPE_END = 373, // Jump up > rope idle (2/2)
|
LA_JUMP_UP_TO_ROPE_END = 373, // Jump up > rope idle (2/2)
|
||||||
|
@ -654,9 +654,8 @@ enum LaraAnim
|
||||||
LA_JUMPSWITCH_PULL = 414, // Pull jumpswitch
|
LA_JUMPSWITCH_PULL = 414, // Pull jumpswitch
|
||||||
LA_UNDERWATER_CEILING_SWITCH_PULL = 415, // Pull underwater ceiling switch
|
LA_UNDERWATER_CEILING_SWITCH_PULL = 415, // Pull underwater ceiling switch
|
||||||
LA_UNDERWATER_DOOR_OPEN = 416, // Open underwater_door
|
LA_UNDERWATER_DOOR_OPEN = 416, // Open underwater_door
|
||||||
LA_PUSHABLE_PUSH_TO_STAND = 417, // Push pushable object (not looped) > stand
|
LA_PUSHABLE_OBJECT_PUSH_TO_STAND = 417, // Push pushable object (not looped) > stand
|
||||||
LA_PUSHABLE_PULL_TO_STAND = 418, // Pull pushable object (not looped) > stand
|
LA_PUSHABLE_OBJECT_PULL_TO_STAND = 418, // Pull pushable object (not looped) > stand
|
||||||
// TODO: add TR1-3 push/pull anims.
|
|
||||||
LA_CROWBAR_PRY_WALL_FAST = 419, // Pry item off wall quickly
|
LA_CROWBAR_PRY_WALL_FAST = 419, // Pry item off wall quickly
|
||||||
LA_CROWBAR_USE_ON_FLOOR = 420, // Use crowbar to activate floor lever
|
LA_CROWBAR_USE_ON_FLOOR = 420, // Use crowbar to activate floor lever
|
||||||
LA_CRAWL_JUMP_FLIP_DOWN = 421, // Roll jump down from crawl
|
LA_CRAWL_JUMP_FLIP_DOWN = 421, // Roll jump down from crawl
|
||||||
|
@ -817,14 +816,13 @@ enum LaraAnim
|
||||||
LA_LEDGE_JUMP_BACK_START = 567,
|
LA_LEDGE_JUMP_BACK_START = 567,
|
||||||
LA_LEDGE_JUMP_BACK_END = 568,
|
LA_LEDGE_JUMP_BACK_END = 568,
|
||||||
|
|
||||||
// 569-598 reserved for "true" ladders. -- Sezz 2023.04.16
|
// 569-598 reserved for "true" ladders. DO NOT add anything here. -- Sezz 2023.04.16
|
||||||
|
|
||||||
NUM_LARA_ANIMS
|
NUM_LARA_ANIMS
|
||||||
|
|
||||||
// TRASHED ANIMS (please reuse slots before going any higher and remove entries from this list as you go):
|
// TRASHED ANIMS (reuse slots before going any higher and remove entries from this list when you do):
|
||||||
// 280,
|
// 280,
|
||||||
// 343, 345,
|
// 368, 370,
|
||||||
// 364, 366, 368, 370,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
enum LaraExtraAnim
|
enum LaraExtraAnim
|
||||||
|
|
|
@ -43,8 +43,8 @@ bool TestValidLedge(ItemInfo* item, CollisionInfo* coll, bool ignoreHeadroom, bo
|
||||||
int y = item->Pose.Position.y - coll->Setup.Height;
|
int y = item->Pose.Position.y - coll->Setup.Height;
|
||||||
|
|
||||||
// Get frontal collision data
|
// Get frontal collision data
|
||||||
auto frontLeft = GetCollision(item->Pose.Position.x + xl, y, item->Pose.Position.z + zl, GetRoom(item->Location, item->Pose.Position.x, y, item->Pose.Position.z).roomNumber);
|
auto frontLeft = GetCollision(item->Pose.Position.x + xl, y, item->Pose.Position.z + zl, GetRoom(item->Location, Vector3i(item->Pose.Position.x, y, item->Pose.Position.z)).roomNumber);
|
||||||
auto frontRight = GetCollision(item->Pose.Position.x + xr, y, item->Pose.Position.z + zr, GetRoom(item->Location, item->Pose.Position.x, y, item->Pose.Position.z).roomNumber);
|
auto frontRight = GetCollision(item->Pose.Position.x + xr, y, item->Pose.Position.z + zr, GetRoom(item->Location, Vector3i(item->Pose.Position.x, y, item->Pose.Position.z)).roomNumber);
|
||||||
|
|
||||||
// If any of the frontal collision results intersects item bounds, return false, because there is material intersection.
|
// If any of the frontal collision results intersects item bounds, return false, because there is material intersection.
|
||||||
// This check helps to filter out cases when Lara is formally facing corner but ledge check returns true because probe distance is fixed.
|
// This check helps to filter out cases when Lara is formally facing corner but ledge check returns true because probe distance is fixed.
|
||||||
|
@ -63,8 +63,8 @@ bool TestValidLedge(ItemInfo* item, CollisionInfo* coll, bool ignoreHeadroom, bo
|
||||||
int zf = phd_cos(coll->NearestLedgeAngle) * (coll->Setup.Radius * 1.2f);
|
int zf = phd_cos(coll->NearestLedgeAngle) * (coll->Setup.Radius * 1.2f);
|
||||||
|
|
||||||
// Get floor heights at both points
|
// Get floor heights at both points
|
||||||
auto left = GetCollision(item->Pose.Position.x + xf + xl, y, item->Pose.Position.z + zf + zl, GetRoom(item->Location, item->Pose.Position.x, y, item->Pose.Position.z).roomNumber).Position.Floor;
|
auto left = GetCollision(item->Pose.Position.x + xf + xl, y, item->Pose.Position.z + zf + zl, GetRoom(item->Location, Vector3i(item->Pose.Position.x, y, item->Pose.Position.z)).roomNumber).Position.Floor;
|
||||||
auto right = GetCollision(item->Pose.Position.x + xf + xr, y, item->Pose.Position.z + zf + zr, GetRoom(item->Location, item->Pose.Position.x, y, item->Pose.Position.z).roomNumber).Position.Floor;
|
auto right = GetCollision(item->Pose.Position.x + xf + xr, y, item->Pose.Position.z + zf + zr, GetRoom(item->Location, Vector3i(item->Pose.Position.x, y, item->Pose.Position.z)).roomNumber).Position.Floor;
|
||||||
|
|
||||||
// If specified, limit vertical search zone only to nearest height
|
// If specified, limit vertical search zone only to nearest height
|
||||||
if (heightLimit && (abs(left - y) > CLICK(0.5f) || abs(right - y) > CLICK(0.5f)))
|
if (heightLimit && (abs(left - y) > CLICK(0.5f) || abs(right - y) > CLICK(0.5f)))
|
||||||
|
@ -926,12 +926,25 @@ bool TestLaraWaterClimbOut(ItemInfo* item, CollisionInfo* coll)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
int frontFloor = coll->Front.Floor + LARA_HEIGHT_TREAD;
|
int frontFloor = coll->Front.Floor + LARA_HEIGHT_TREAD;
|
||||||
if (frontFloor <= -CLICK(2) ||
|
if (coll->Front.Bridge == NO_ITEM &&
|
||||||
frontFloor > CLICK(1.25f) - 4)
|
(frontFloor <= -CLICK(2) ||
|
||||||
|
frontFloor > CLICK(1.25f) - 4))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Do extra check if it's a bridge collider:
|
||||||
|
if (coll->Front.Bridge != NO_ITEM)
|
||||||
|
{
|
||||||
|
auto distancePointLara = GetBridgeBorder(coll->Front.Bridge, false) - LaraItem->Pose.Position.y;
|
||||||
|
frontFloor = distancePointLara - 128;
|
||||||
|
if (frontFloor <= -CLICK(2) ||
|
||||||
|
frontFloor > CLICK(1.25f) - 4)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!TestValidLedge(item, coll))
|
if (!TestValidLedge(item, coll))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -980,7 +993,11 @@ bool TestLaraWaterClimbOut(ItemInfo* item, CollisionInfo* coll)
|
||||||
SetAnimation(item, LA_ONWATER_TO_STAND_0_STEP);
|
SetAnimation(item, LA_ONWATER_TO_STAND_0_STEP);
|
||||||
}
|
}
|
||||||
|
|
||||||
UpdateLaraRoom(item, -LARA_HEIGHT / 2);
|
if (coll->Front.Bridge == NO_ITEM)
|
||||||
|
UpdateLaraRoom(item, -LARA_HEIGHT / 2);
|
||||||
|
else
|
||||||
|
UpdateLaraRoom(item, -LARA_HEIGHT);
|
||||||
|
|
||||||
SnapItemToLedge(item, coll, 1.7f);
|
SnapItemToLedge(item, coll, 1.7f);
|
||||||
|
|
||||||
item->Pose.Position.y += frontFloor - 5;
|
item->Pose.Position.y += frontFloor - 5;
|
||||||
|
|
|
@ -34,6 +34,87 @@ using namespace TEN::Entities::Switches;
|
||||||
ObjectHandler Objects;
|
ObjectHandler Objects;
|
||||||
StaticInfo StaticObjects[MAX_STATICS];
|
StaticInfo StaticObjects[MAX_STATICS];
|
||||||
|
|
||||||
|
void ObjectHandler::Initialize()
|
||||||
|
{
|
||||||
|
std::memset(_objects, 0, sizeof(ObjectInfo) * GAME_OBJECT_ID::ID_NUMBER_OBJECTS);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ObjectHandler::CheckID(GAME_OBJECT_ID objectID, bool isSilent)
|
||||||
|
{
|
||||||
|
if (objectID == GAME_OBJECT_ID::ID_NO_OBJECT || objectID >= GAME_OBJECT_ID::ID_NUMBER_OBJECTS)
|
||||||
|
{
|
||||||
|
if (!isSilent)
|
||||||
|
{
|
||||||
|
TENLog(
|
||||||
|
"Attempted to access unavailable slot ID (" + std::to_string(objectID) + "). " +
|
||||||
|
"Check if last accessed item exists in level.", LogLevel::Warning, LogConfig::Debug);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ObjectInfo& ObjectHandler::operator [](int objectID)
|
||||||
|
{
|
||||||
|
if (CheckID((GAME_OBJECT_ID)objectID))
|
||||||
|
return _objects[objectID];
|
||||||
|
|
||||||
|
return GetFirstAvailableObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
ObjectInfo& ObjectHandler::GetFirstAvailableObject()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < ID_NUMBER_OBJECTS; i++)
|
||||||
|
{
|
||||||
|
if (_objects[i].loaded)
|
||||||
|
return _objects[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return _objects[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: JointRotationFlags allows bones to be rotated with CreatureJoint().
|
||||||
|
void ObjectInfo::SetBoneRotationFlags(int boneID, int flags)
|
||||||
|
{
|
||||||
|
g_Level.Bones[boneIndex + (boneID * 4)] |= flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: Use if object is alive, but not intelligent, to set up blood effects.
|
||||||
|
void ObjectInfo::SetHitEffect(bool isSolid, bool isAlive)
|
||||||
|
{
|
||||||
|
// Avoid some objects such as ID_SAS_DYING having None.
|
||||||
|
if (isAlive)
|
||||||
|
{
|
||||||
|
hitEffect = HitEffect::Blood;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (intelligent)
|
||||||
|
{
|
||||||
|
if (isSolid && HitPoints > 0)
|
||||||
|
{
|
||||||
|
hitEffect = HitEffect::Richochet;
|
||||||
|
}
|
||||||
|
else if ((damageType != DamageMode::Any && HitPoints > 0) || HitPoints == NOT_TARGETABLE)
|
||||||
|
{
|
||||||
|
hitEffect = HitEffect::Smoke;
|
||||||
|
}
|
||||||
|
else if (damageType == DamageMode::Any && HitPoints > 0)
|
||||||
|
{
|
||||||
|
hitEffect = HitEffect::Blood;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (isSolid && HitPoints <= 0)
|
||||||
|
{
|
||||||
|
hitEffect = HitEffect::Richochet;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
hitEffect = HitEffect::None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void InitializeGameFlags()
|
void InitializeGameFlags()
|
||||||
{
|
{
|
||||||
|
@ -101,7 +182,7 @@ void InitializeObjects()
|
||||||
obj->waterCreature = false;
|
obj->waterCreature = false;
|
||||||
obj->nonLot = false;
|
obj->nonLot = false;
|
||||||
obj->usingDrawAnimatingItem = true;
|
obj->usingDrawAnimatingItem = true;
|
||||||
obj->damageType = DamageMode::AnyWeapon;
|
obj->damageType = DamageMode::Any;
|
||||||
obj->LotType = LotType::Basic;
|
obj->LotType = LotType::Basic;
|
||||||
obj->meshSwapSlot = NO_ITEM;
|
obj->meshSwapSlot = NO_ITEM;
|
||||||
obj->isPickup = false;
|
obj->isPickup = false;
|
||||||
|
|
|
@ -9,6 +9,24 @@ struct CollisionInfo;
|
||||||
struct ItemInfo;
|
struct ItemInfo;
|
||||||
|
|
||||||
constexpr auto DEFAULT_RADIUS = 10;
|
constexpr auto DEFAULT_RADIUS = 10;
|
||||||
|
constexpr auto GRAVITY = 6.0f;
|
||||||
|
constexpr auto SWAMP_GRAVITY = GRAVITY / 3.0f;
|
||||||
|
|
||||||
|
constexpr auto MAX_STATICS = 1000;
|
||||||
|
|
||||||
|
enum JointRotationFlags
|
||||||
|
{
|
||||||
|
ROT_X = (1 << 2),
|
||||||
|
ROT_Y = (1 << 3),
|
||||||
|
ROT_Z = (1 << 4)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Unused.
|
||||||
|
enum ShatterFlags
|
||||||
|
{
|
||||||
|
NoCollision = (1 << 0),
|
||||||
|
Shatterable = (1 << 1)
|
||||||
|
};
|
||||||
|
|
||||||
// Custom LOT definition for Creature. Used in InitializeSlot() in lot.cpp.
|
// Custom LOT definition for Creature. Used in InitializeSlot() in lot.cpp.
|
||||||
enum class LotType
|
enum class LotType
|
||||||
|
@ -27,35 +45,27 @@ enum class LotType
|
||||||
SnowmobileGun // Only 1 block vault allowed and 4 block drop max.
|
SnowmobileGun // Only 1 block vault allowed and 4 block drop max.
|
||||||
};
|
};
|
||||||
|
|
||||||
enum JointRotationFlags
|
|
||||||
{
|
|
||||||
ROT_X = (1 << 2),
|
|
||||||
ROT_Y = (1 << 3),
|
|
||||||
ROT_Z = (1 << 4)
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class HitEffect
|
enum class HitEffect
|
||||||
{
|
{
|
||||||
None,
|
None,
|
||||||
Blood,
|
Blood,
|
||||||
Smoke,
|
Smoke,
|
||||||
Richochet,
|
Richochet,
|
||||||
Special,
|
Special
|
||||||
Max
|
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class DamageMode
|
enum class DamageMode
|
||||||
{
|
{
|
||||||
AnyWeapon,
|
None,
|
||||||
ExplosivesOnly,
|
Any,
|
||||||
None
|
Explosion
|
||||||
};
|
};
|
||||||
|
|
||||||
enum ShatterType
|
enum class ShatterType
|
||||||
{
|
{
|
||||||
SHT_NONE,
|
None,
|
||||||
SHT_FRAGMENT,
|
Fragment,
|
||||||
SHT_EXPLODE
|
Explode
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ObjectInfo
|
struct ObjectInfo
|
||||||
|
@ -99,98 +109,23 @@ struct ObjectInfo
|
||||||
std::function<int(short itemNumber)> floorBorder;
|
std::function<int(short itemNumber)> floorBorder;
|
||||||
std::function<int(short itemNumber)> ceilingBorder;
|
std::function<int(short itemNumber)> ceilingBorder;
|
||||||
|
|
||||||
// NOTE: ROT_X/Y/Z allows bones to be rotated with CreatureJoint().
|
void SetBoneRotationFlags(int boneID, int flags);
|
||||||
void SetBoneRotationFlags(int boneNumber, int flags)
|
void SetHitEffect(bool isSolid = false, bool isAlive = false);
|
||||||
{
|
|
||||||
g_Level.Bones[boneIndex + (boneNumber * 4)] |= flags;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set up hit effect for object based on its value.
|
|
||||||
// Use if object is alive but not intelligent to set up blood effects.
|
|
||||||
void SetupHitEffect(bool isSolid = false, bool isAlive = false)
|
|
||||||
{
|
|
||||||
// Avoid some objects such as ID_SAS_DYING having None.
|
|
||||||
if (isAlive)
|
|
||||||
{
|
|
||||||
hitEffect = HitEffect::Blood;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (intelligent)
|
|
||||||
{
|
|
||||||
if (isSolid && HitPoints > 0)
|
|
||||||
{
|
|
||||||
hitEffect = HitEffect::Richochet;
|
|
||||||
}
|
|
||||||
else if ((damageType != DamageMode::AnyWeapon && HitPoints > 0) || HitPoints == NOT_TARGETABLE)
|
|
||||||
{
|
|
||||||
hitEffect = HitEffect::Smoke;
|
|
||||||
}
|
|
||||||
else if (damageType == DamageMode::AnyWeapon && HitPoints > 0)
|
|
||||||
{
|
|
||||||
hitEffect = HitEffect::Blood;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (isSolid && HitPoints <= 0)
|
|
||||||
{
|
|
||||||
hitEffect = HitEffect::Richochet;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
hitEffect = HitEffect::None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class ObjectHandler
|
class ObjectHandler
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
ObjectInfo Objects[ID_NUMBER_OBJECTS];
|
ObjectInfo _objects[ID_NUMBER_OBJECTS];
|
||||||
|
|
||||||
ObjectInfo& GetFirstAvailableObject()
|
public:
|
||||||
{
|
void Initialize();
|
||||||
for (int i = 0; i < ID_NUMBER_OBJECTS; i++)
|
bool CheckID(GAME_OBJECT_ID objectID, bool isSilent = false);
|
||||||
{
|
|
||||||
if (Objects[i].loaded)
|
|
||||||
return Objects[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
return Objects[0];
|
ObjectInfo& operator [](int objectID);
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
private:
|
||||||
void Initialize()
|
ObjectInfo& GetFirstAvailableObject();
|
||||||
{
|
|
||||||
std::memset(Objects, 0, sizeof(ObjectInfo) * GAME_OBJECT_ID::ID_NUMBER_OBJECTS);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CheckID(int index, bool isSilent = false)
|
|
||||||
{
|
|
||||||
if (index == GAME_OBJECT_ID::ID_NO_OBJECT || index >= GAME_OBJECT_ID::ID_NUMBER_OBJECTS)
|
|
||||||
{
|
|
||||||
if (!isSilent)
|
|
||||||
{
|
|
||||||
TENLog("Attempted to access unavailable slot ID (" + std::to_string(index) + "). " +
|
|
||||||
"Check if last accessed item exists in level.", LogLevel::Warning, LogConfig::Debug);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
ObjectInfo& operator[](int index)
|
|
||||||
{
|
|
||||||
if (CheckID(index))
|
|
||||||
{
|
|
||||||
return Objects[index];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return GetFirstAvailableObject();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct StaticInfo
|
struct StaticInfo
|
||||||
|
@ -199,16 +134,10 @@ struct StaticInfo
|
||||||
int flags;
|
int flags;
|
||||||
GameBoundingBox visibilityBox;
|
GameBoundingBox visibilityBox;
|
||||||
GameBoundingBox collisionBox;
|
GameBoundingBox collisionBox;
|
||||||
int shatterType;
|
ShatterType shatterType;
|
||||||
int shatterSound;
|
int shatterSound;
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr auto MAX_STATICS = 1000;
|
|
||||||
constexpr auto SF_NO_COLLISION = 0x01;
|
|
||||||
constexpr auto SF_SHATTERABLE = 0x02;
|
|
||||||
constexpr auto GRAVITY = 6.0f;
|
|
||||||
constexpr auto SWAMP_GRAVITY = GRAVITY / 3.0f;
|
|
||||||
|
|
||||||
extern ObjectHandler Objects;
|
extern ObjectHandler Objects;
|
||||||
extern StaticInfo StaticObjects[MAX_STATICS];
|
extern StaticInfo StaticObjects[MAX_STATICS];
|
||||||
|
|
||||||
|
|
|
@ -91,9 +91,7 @@ void DoThumbstickCamera()
|
||||||
|
|
||||||
if (Camera.laraNode == -1 && Camera.target.ToVector3i() == OldCam.target)
|
if (Camera.laraNode == -1 && Camera.target.ToVector3i() == OldCam.target)
|
||||||
{
|
{
|
||||||
auto axisCoeff = Vector2(
|
const auto& axisCoeff = AxisMap[(int)InputAxis::Camera];
|
||||||
AxisMap[InputAxis::CameraHorizontal],
|
|
||||||
AxisMap[InputAxis::CameraVertical]);
|
|
||||||
|
|
||||||
if (abs(axisCoeff.x) > EPSILON && abs(Camera.targetAngle) == 0)
|
if (abs(axisCoeff.x) > EPSILON && abs(Camera.targetAngle) == 0)
|
||||||
Camera.targetAngle = ANGLE(VERTICAL_CONSTRAINT_ANGLE * axisCoeff.x);
|
Camera.targetAngle = ANGLE(VERTICAL_CONSTRAINT_ANGLE * axisCoeff.x);
|
||||||
|
|
|
@ -1877,7 +1877,9 @@ void DoObjectCollision(ItemInfo* laraItem, CollisionInfo* coll)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (coll->Setup.EnableObjectPush)
|
else if (coll->Setup.EnableObjectPush)
|
||||||
|
{
|
||||||
ItemPushItem(item, laraItem, coll, false, 1);
|
ItemPushItem(item, laraItem, coll, false, 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1904,15 +1906,19 @@ void DoObjectCollision(ItemInfo* laraItem, CollisionInfo* coll)
|
||||||
// HACK: Shatter statics only by non-harmless vehicles.
|
// HACK: Shatter statics only by non-harmless vehicles.
|
||||||
if (!doPlayerCollision &&
|
if (!doPlayerCollision &&
|
||||||
!harmless && abs(laraItem->Animation.Velocity.z) > VEHICLE_COLLISION_TERMINAL_VELOCITY &&
|
!harmless && abs(laraItem->Animation.Velocity.z) > VEHICLE_COLLISION_TERMINAL_VELOCITY &&
|
||||||
StaticObjects[mesh.staticNumber].shatterType != SHT_NONE)
|
StaticObjects[mesh.staticNumber].shatterType != ShatterType::None)
|
||||||
{
|
{
|
||||||
SoundEffect(GetShatterSound(mesh.staticNumber), &mesh.pos);
|
SoundEffect(GetShatterSound(mesh.staticNumber), &mesh.pos);
|
||||||
ShatterObject(nullptr, &mesh, -128, laraItem->RoomNumber, 0);
|
ShatterObject(nullptr, &mesh, -128, laraItem->RoomNumber, 0);
|
||||||
}
|
}
|
||||||
else if (coll->Setup.EnableObjectPush)
|
else if (coll->Setup.EnableObjectPush)
|
||||||
|
{
|
||||||
ItemPushStatic(laraItem, mesh, coll);
|
ItemPushStatic(laraItem, mesh, coll);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -78,9 +78,13 @@ int FindGridShift(int x, int z)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if ((z / BLOCK(1)) <= (x / BLOCK(1)))
|
if ((z / BLOCK(1)) <= (x / BLOCK(1)))
|
||||||
|
{
|
||||||
return (-1 - (x & WALL_MASK));
|
return (-1 - (x & WALL_MASK));
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
return ((BLOCK(1) + 1) - (x & WALL_MASK));
|
return ((BLOCK(1) + 1) - (x & WALL_MASK));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test if the axis-aligned bounding box collides with geometry at all.
|
// Test if the axis-aligned bounding box collides with geometry at all.
|
||||||
|
@ -142,7 +146,7 @@ CollisionResult GetCollision(ItemInfo* item, short headingAngle, float forward,
|
||||||
ROOM_VECTOR{ GetFloor(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z, &tempRoomNumber)->Room, item->Pose.Position.y };
|
ROOM_VECTOR{ GetFloor(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z, &tempRoomNumber)->Room, item->Pose.Position.y };
|
||||||
|
|
||||||
auto point = Geometry::TranslatePoint(item->Pose.Position, headingAngle, forward, down, right);
|
auto point = Geometry::TranslatePoint(item->Pose.Position, headingAngle, forward, down, right);
|
||||||
int adjacentRoomNumber = GetRoom(location, item->Pose.Position.x, point.y, item->Pose.Position.z).roomNumber;
|
int adjacentRoomNumber = GetRoom(location, Vector3i(item->Pose.Position.x, point.y, item->Pose.Position.z)).roomNumber;
|
||||||
return GetCollision(point.x, point.y, point.z, adjacentRoomNumber);
|
return GetCollision(point.x, point.y, point.z, adjacentRoomNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -153,7 +157,7 @@ CollisionResult GetCollision(const Vector3i& pos, int roomNumber, short headingA
|
||||||
auto location = ROOM_VECTOR{ GetFloor(pos.x, pos.y, pos.z, &tempRoomNumber)->Room, pos.y };
|
auto location = ROOM_VECTOR{ GetFloor(pos.x, pos.y, pos.z, &tempRoomNumber)->Room, pos.y };
|
||||||
|
|
||||||
auto point = Geometry::TranslatePoint(pos, headingAngle, forward, down, right);
|
auto point = Geometry::TranslatePoint(pos, headingAngle, forward, down, right);
|
||||||
int adjacentRoomNumber = GetRoom(location, pos.x, point.y, pos.z).roomNumber;
|
int adjacentRoomNumber = GetRoom(location, Vector3i(pos.x, point.y, pos.z)).roomNumber;
|
||||||
return GetCollision(point.x, point.y, point.z, adjacentRoomNumber);
|
return GetCollision(point.x, point.y, point.z, adjacentRoomNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,7 +167,7 @@ CollisionResult GetCollision(const Vector3i& pos, int roomNumber, const EulerAng
|
||||||
|
|
||||||
short tempRoomNumber = roomNumber;
|
short tempRoomNumber = roomNumber;
|
||||||
auto location = ROOM_VECTOR{ GetFloor(pos.x, pos.y, pos.z, &tempRoomNumber)->Room, pos.y };
|
auto location = ROOM_VECTOR{ GetFloor(pos.x, pos.y, pos.z, &tempRoomNumber)->Room, pos.y };
|
||||||
int adjacentRoomNumber = GetRoom(location, pos.x, point.y, pos.z).roomNumber;
|
int adjacentRoomNumber = GetRoom(location, Vector3i(pos.x, point.y, pos.z)).roomNumber;
|
||||||
return GetCollision(point.x, point.y, point.z, adjacentRoomNumber);
|
return GetCollision(point.x, point.y, point.z, adjacentRoomNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -213,9 +217,9 @@ CollisionResult GetCollision(FloorInfo* floor, int x, int y, int z)
|
||||||
result.Position.Ceiling = GetCeilingHeight(ROOM_VECTOR{ floor->Room, y }, x, z).value_or(NO_HEIGHT);
|
result.Position.Ceiling = GetCeilingHeight(ROOM_VECTOR{ floor->Room, y }, x, z).value_or(NO_HEIGHT);
|
||||||
|
|
||||||
// Probe bottom collision block through portals.
|
// Probe bottom collision block through portals.
|
||||||
while (floor->GetRoomNumberBelow(x, y, z).value_or(NO_ROOM) != NO_ROOM)
|
while (floor->GetRoomNumberBelow(Vector3i(x, y, z)).value_or(NO_ROOM) != NO_ROOM)
|
||||||
{
|
{
|
||||||
auto* room = &g_Level.Rooms[floor->GetRoomNumberBelow(x, y, z).value_or(floor->Room)];
|
auto* room = &g_Level.Rooms[floor->GetRoomNumberBelow(Vector3i(x, y, z)).value_or(floor->Room)];
|
||||||
floor = GetSector(room, x - room->x, z - room->z);
|
floor = GetSector(room, x - room->x, z - room->z);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -229,7 +233,7 @@ CollisionResult GetCollision(FloorInfo* floor, int x, int y, int z)
|
||||||
// Split, bridge and slope data.
|
// Split, bridge and slope data.
|
||||||
result.Position.DiagonalStep = floor->IsSurfaceDiagonalStep(true);
|
result.Position.DiagonalStep = floor->IsSurfaceDiagonalStep(true);
|
||||||
result.Position.SplitAngle = floor->FloorCollision.SplitAngle;
|
result.Position.SplitAngle = floor->FloorCollision.SplitAngle;
|
||||||
result.Position.Bridge = result.BottomBlock->GetInsideBridgeItemNumber(x, result.Position.Floor, z, true, false);
|
result.Position.Bridge = result.BottomBlock->GetInsideBridgeItemNumber(Vector3i(x, result.Position.Floor, z), true, false);
|
||||||
result.Position.FloorSlope = result.Position.Bridge < 0 && (abs(result.FloorTilt.x) >= 3 || (abs(result.FloorTilt.y) >= 3));
|
result.Position.FloorSlope = result.Position.Bridge < 0 && (abs(result.FloorTilt.x) >= 3 || (abs(result.FloorTilt.y) >= 3));
|
||||||
result.Position.CeilingSlope = abs(result.CeilingTilt.x) >= 4 || abs(result.CeilingTilt.y) >= 4; // TODO: Fix on bridges placed beneath ceiling slopes. @Sezz 2022.01.29
|
result.Position.CeilingSlope = abs(result.CeilingTilt.x) >= 4 || abs(result.CeilingTilt.y) >= 4; // TODO: Fix on bridges placed beneath ceiling slopes. @Sezz 2022.01.29
|
||||||
|
|
||||||
|
@ -279,12 +283,29 @@ static void SetSectorAttribs(CollisionPosition& sectorAttribs, const CollisionSe
|
||||||
}
|
}
|
||||||
else if (collSetup.BlockMonkeySwingEdge)
|
else if (collSetup.BlockMonkeySwingEdge)
|
||||||
{
|
{
|
||||||
auto monkeyPointColl = GetCollision(probePos.x, probePos.y + collSetup.Height, probePos.z, realRoomNumber);
|
auto pointColl = GetCollision(probePos.x, probePos.y + collSetup.Height, probePos.z, realRoomNumber);
|
||||||
if (!monkeyPointColl.BottomBlock->Flags.Monkeyswing)
|
if (!pointColl.BottomBlock->Flags.Monkeyswing)
|
||||||
sectorAttribs.Floor = MAX_HEIGHT;
|
sectorAttribs.Floor = MAX_HEIGHT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void HandleDiagonalShift(ItemInfo& item, CollisionInfo& coll, const Vector3i& pos, CardinalDirection quadrant, bool isRightShift)
|
||||||
|
{
|
||||||
|
// Calculate angles.
|
||||||
|
int sign = isRightShift ? 1 : -1;
|
||||||
|
short perpSplitAngle = (ANGLE(90.0f) * quadrant) + (ANGLE(45.0f) * sign);
|
||||||
|
short deltaAngle = abs(Geometry::GetShortestAngle(coll.Setup.ForwardAngle, perpSplitAngle));
|
||||||
|
|
||||||
|
// HACK: Force slight push left to avoid getting stuck.
|
||||||
|
float alpha = 1.0f - ((float)deltaAngle / (float)ANGLE(90.0f));
|
||||||
|
if (alpha >= 0.5f)
|
||||||
|
TranslateItem(&item, perpSplitAngle, item.Animation.Velocity.z * alpha);
|
||||||
|
|
||||||
|
// Set shift.
|
||||||
|
coll.Shift.Position.x += coll.Setup.PrevPosition.x - pos.x;
|
||||||
|
coll.Shift.Position.z += coll.Setup.PrevPosition.z - pos.z;
|
||||||
|
};
|
||||||
|
|
||||||
void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, const Vector3i& offset, bool resetRoom)
|
void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, const Vector3i& offset, bool resetRoom)
|
||||||
{
|
{
|
||||||
// Player collision has several more precise checks for bridge collisions.
|
// Player collision has several more precise checks for bridge collisions.
|
||||||
|
@ -311,7 +332,7 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, const Vector3i& offse
|
||||||
// If unconstrained mode is specified, don't use quadrant.
|
// If unconstrained mode is specified, don't use quadrant.
|
||||||
switch ((coll->Setup.Mode == CollisionProbeMode::Quadrants) ? quadrant : -1)
|
switch ((coll->Setup.Mode == CollisionProbeMode::Quadrants) ? quadrant : -1)
|
||||||
{
|
{
|
||||||
case 0:
|
case NORTH:
|
||||||
xFront = phd_sin(coll->Setup.ForwardAngle) * coll->Setup.Radius;
|
xFront = phd_sin(coll->Setup.ForwardAngle) * coll->Setup.Radius;
|
||||||
zFront = coll->Setup.Radius;
|
zFront = coll->Setup.Radius;
|
||||||
xLeft = -coll->Setup.Radius;
|
xLeft = -coll->Setup.Radius;
|
||||||
|
@ -320,7 +341,7 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, const Vector3i& offse
|
||||||
zRight = coll->Setup.Radius;
|
zRight = coll->Setup.Radius;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 1:
|
case EAST:
|
||||||
xFront = coll->Setup.Radius;
|
xFront = coll->Setup.Radius;
|
||||||
zFront = phd_cos(coll->Setup.ForwardAngle) * coll->Setup.Radius;
|
zFront = phd_cos(coll->Setup.ForwardAngle) * coll->Setup.Radius;
|
||||||
xLeft = coll->Setup.Radius;
|
xLeft = coll->Setup.Radius;
|
||||||
|
@ -329,7 +350,7 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, const Vector3i& offse
|
||||||
zRight = -coll->Setup.Radius;
|
zRight = -coll->Setup.Radius;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 2:
|
case SOUTH:
|
||||||
xFront = phd_sin(coll->Setup.ForwardAngle) * coll->Setup.Radius;
|
xFront = phd_sin(coll->Setup.ForwardAngle) * coll->Setup.Radius;
|
||||||
zFront = -coll->Setup.Radius;
|
zFront = -coll->Setup.Radius;
|
||||||
xLeft = coll->Setup.Radius;
|
xLeft = coll->Setup.Radius;
|
||||||
|
@ -338,7 +359,7 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, const Vector3i& offse
|
||||||
zRight = -coll->Setup.Radius;
|
zRight = -coll->Setup.Radius;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 3:
|
case WEST:
|
||||||
xFront = -coll->Setup.Radius;
|
xFront = -coll->Setup.Radius;
|
||||||
zFront = phd_cos(coll->Setup.ForwardAngle) * coll->Setup.Radius;
|
zFront = phd_cos(coll->Setup.ForwardAngle) * coll->Setup.Radius;
|
||||||
xLeft = -coll->Setup.Radius;
|
xLeft = -coll->Setup.Radius;
|
||||||
|
@ -347,7 +368,7 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, const Vector3i& offse
|
||||||
zRight = coll->Setup.Radius;
|
zRight = coll->Setup.Radius;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// No valid quadrant; return true probe offsets from object rotation.
|
// No valid quadrant; get true probe offsets from object rotation.
|
||||||
default:
|
default:
|
||||||
xFront = phd_sin(coll->Setup.ForwardAngle) * coll->Setup.Radius;
|
xFront = phd_sin(coll->Setup.ForwardAngle) * coll->Setup.Radius;
|
||||||
zFront = phd_cos(coll->Setup.ForwardAngle) * coll->Setup.Radius;
|
zFront = phd_cos(coll->Setup.ForwardAngle) * coll->Setup.Radius;
|
||||||
|
@ -392,10 +413,10 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, const Vector3i& offse
|
||||||
|
|
||||||
if (doPlayerCollision)
|
if (doPlayerCollision)
|
||||||
{
|
{
|
||||||
tfLocation = GetRoom(item->Location, probePos.x, probePos.y, probePos.z);
|
tfLocation = GetRoom(item->Location, probePos);
|
||||||
height = GetFloorHeight(tfLocation, probePos.x, probePos.z).value_or(NO_HEIGHT);
|
height = GetFloorHeight(tfLocation, probePos.x, probePos.z).value_or(NO_HEIGHT);
|
||||||
|
|
||||||
tcLocation = GetRoom(item->Location, probePos.x, probePos.y - item->Animation.Velocity.y, probePos.z);
|
tcLocation = GetRoom(item->Location, Vector3i(probePos.x, probePos.y - item->Animation.Velocity.y, probePos.z));
|
||||||
ceiling = GetCeilingHeight(tcLocation, probePos.x, probePos.z).value_or(NO_HEIGHT);
|
ceiling = GetCeilingHeight(tcLocation, probePos.x, probePos.z).value_or(NO_HEIGHT);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -435,10 +456,10 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, const Vector3i& offse
|
||||||
topRoomNumber = realRoomNumber;
|
topRoomNumber = realRoomNumber;
|
||||||
}
|
}
|
||||||
|
|
||||||
tfLocation = GetRoom(tfLocation, probePos.x, probePos.y, probePos.z);
|
tfLocation = GetRoom(tfLocation, probePos);
|
||||||
height = GetFloorHeight(tfLocation, probePos.x, probePos.z).value_or(NO_HEIGHT);
|
height = GetFloorHeight(tfLocation, probePos.x, probePos.z).value_or(NO_HEIGHT);
|
||||||
|
|
||||||
tcLocation = GetRoom(tcLocation, probePos.x, probePos.y - item->Animation.Velocity.y, probePos.z);
|
tcLocation = GetRoom(tcLocation, Vector3i(probePos.x, probePos.y - item->Animation.Velocity.y, probePos.z));
|
||||||
ceiling = GetCeilingHeight(tcLocation, probePos.x, probePos.z).value_or(NO_HEIGHT);
|
ceiling = GetCeilingHeight(tcLocation, probePos.x, probePos.z).value_or(NO_HEIGHT);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -459,7 +480,7 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, const Vector3i& offse
|
||||||
|
|
||||||
if (doPlayerCollision)
|
if (doPlayerCollision)
|
||||||
{
|
{
|
||||||
tfLocation = GetRoom(tfLocation, probePos.x + xFront, probePos.y, probePos.z + zFront);
|
tfLocation = GetRoom(tfLocation, Vector3i(probePos.x + xFront, probePos.y, probePos.z + zFront));
|
||||||
height = GetFloorHeight(tfLocation, probePos.x + xFront, probePos.z + zFront).value_or(NO_HEIGHT);
|
height = GetFloorHeight(tfLocation, probePos.x + xFront, probePos.z + zFront).value_or(NO_HEIGHT);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -483,10 +504,10 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, const Vector3i& offse
|
||||||
|
|
||||||
if (doPlayerCollision)
|
if (doPlayerCollision)
|
||||||
{
|
{
|
||||||
lrfLocation = GetRoom(item->Location, probePos.x, probePos.y, probePos.z);
|
lrfLocation = GetRoom(item->Location, probePos);
|
||||||
height = GetFloorHeight(lrfLocation, probePos.x, probePos.z).value_or(NO_HEIGHT);
|
height = GetFloorHeight(lrfLocation, probePos.x, probePos.z).value_or(NO_HEIGHT);
|
||||||
|
|
||||||
lrcLocation = GetRoom(item->Location, probePos.x, probePos.y - item->Animation.Velocity.y, probePos.z);
|
lrcLocation = GetRoom(item->Location, Vector3i(probePos.x, probePos.y - item->Animation.Velocity.y, probePos.z));
|
||||||
ceiling = GetCeilingHeight(lrcLocation, probePos.x, probePos.z).value_or(NO_HEIGHT);
|
ceiling = GetCeilingHeight(lrcLocation, probePos.x, probePos.z).value_or(NO_HEIGHT);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -513,10 +534,10 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, const Vector3i& offse
|
||||||
|
|
||||||
if (doPlayerCollision)
|
if (doPlayerCollision)
|
||||||
{
|
{
|
||||||
tfLocation = GetRoom(tfLocation, probePos.x, probePos.y, probePos.z);
|
tfLocation = GetRoom(tfLocation, probePos);
|
||||||
height = GetFloorHeight(tfLocation, probePos.x, probePos.z).value_or(NO_HEIGHT);
|
height = GetFloorHeight(tfLocation, probePos.x, probePos.z).value_or(NO_HEIGHT);
|
||||||
|
|
||||||
tcLocation = GetRoom(tcLocation, probePos.x, probePos.y - item->Animation.Velocity.y, probePos.z);
|
tcLocation = GetRoom(tcLocation, Vector3i(probePos.x, probePos.y - item->Animation.Velocity.y, probePos.z));
|
||||||
ceiling = GetCeilingHeight(tcLocation, probePos.x, probePos.z).value_or(NO_HEIGHT);
|
ceiling = GetCeilingHeight(tcLocation, probePos.x, probePos.z).value_or(NO_HEIGHT);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -548,10 +569,10 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, const Vector3i& offse
|
||||||
|
|
||||||
if (doPlayerCollision)
|
if (doPlayerCollision)
|
||||||
{
|
{
|
||||||
lrfLocation = GetRoom(item->Location, probePos.x, probePos.y, probePos.z);
|
lrfLocation = GetRoom(item->Location, probePos);
|
||||||
height = GetFloorHeight(lrfLocation, probePos.x, probePos.z).value_or(NO_HEIGHT);
|
height = GetFloorHeight(lrfLocation, probePos.x, probePos.z).value_or(NO_HEIGHT);
|
||||||
|
|
||||||
lrcLocation = GetRoom(item->Location, probePos.x, probePos.y - item->Animation.Velocity.y, probePos.z);
|
lrcLocation = GetRoom(item->Location, Vector3i(probePos.x, probePos.y - item->Animation.Velocity.y, probePos.z));
|
||||||
ceiling = GetCeilingHeight(lrcLocation, probePos.x, probePos.z).value_or(NO_HEIGHT);
|
ceiling = GetCeilingHeight(lrcLocation, probePos.x, probePos.z).value_or(NO_HEIGHT);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -578,10 +599,10 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, const Vector3i& offse
|
||||||
|
|
||||||
if (doPlayerCollision)
|
if (doPlayerCollision)
|
||||||
{
|
{
|
||||||
tfLocation = GetRoom(tfLocation, probePos.x, probePos.y, probePos.z);
|
tfLocation = GetRoom(tfLocation, probePos);
|
||||||
height = GetFloorHeight(tfLocation, probePos.x, probePos.z).value_or(NO_HEIGHT);
|
height = GetFloorHeight(tfLocation, probePos.x, probePos.z).value_or(NO_HEIGHT);
|
||||||
|
|
||||||
tcLocation = GetRoom(tcLocation, probePos.x, probePos.y - item->Animation.Velocity.y, probePos.z);
|
tcLocation = GetRoom(tcLocation, Vector3i(probePos.x, probePos.y - item->Animation.Velocity.y, probePos.z));
|
||||||
ceiling = GetCeilingHeight(tcLocation, probePos.x, probePos.z).value_or(NO_HEIGHT);
|
ceiling = GetCeilingHeight(tcLocation, probePos.x, probePos.z).value_or(NO_HEIGHT);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -629,6 +650,7 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, const Vector3i& offse
|
||||||
coll->CollisionType = CT_TOP;
|
coll->CollisionType = CT_TOP;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Shift away from front wall.
|
||||||
if (coll->Front.Floor > coll->Setup.LowerFloorBound ||
|
if (coll->Front.Floor > coll->Setup.LowerFloorBound ||
|
||||||
coll->Front.Floor < coll->Setup.UpperFloorBound ||
|
coll->Front.Floor < coll->Setup.UpperFloorBound ||
|
||||||
coll->Front.Ceiling > coll->Setup.LowerCeilingBound ||
|
coll->Front.Ceiling > coll->Setup.LowerCeilingBound ||
|
||||||
|
@ -644,20 +666,18 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, const Vector3i& offse
|
||||||
{
|
{
|
||||||
switch (quadrant)
|
switch (quadrant)
|
||||||
{
|
{
|
||||||
case 0:
|
case NORTH:
|
||||||
case 2:
|
case SOUTH:
|
||||||
coll->Shift.Position.x += coll->Setup.PrevPosition.x - entityPos.x;
|
|
||||||
coll->Shift.Position.z += FindGridShift(entityPos.z + zFront, entityPos.z);
|
coll->Shift.Position.z += FindGridShift(entityPos.z + zFront, entityPos.z);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 1:
|
case EAST:
|
||||||
case 3:
|
case WEST:
|
||||||
coll->Shift.Position.x += FindGridShift(entityPos.x + xFront, entityPos.x);
|
coll->Shift.Position.x += FindGridShift(entityPos.x + xFront, entityPos.x);
|
||||||
coll->Shift.Position.z += coll->Setup.PrevPosition.z - entityPos.z;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
coll->CollisionType = (coll->CollisionType == CT_TOP) ? CT_TOP_FRONT : CT_FRONT;
|
coll->CollisionType = (coll->CollisionType == CT_TOP) ? CT_TOP_FRONT : CT_FRONT;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -670,6 +690,7 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, const Vector3i& offse
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Shift away from left wall.
|
||||||
if (coll->MiddleLeft.Floor > coll->Setup.LowerFloorBound ||
|
if (coll->MiddleLeft.Floor > coll->Setup.LowerFloorBound ||
|
||||||
coll->MiddleLeft.Floor < coll->Setup.UpperFloorBound ||
|
coll->MiddleLeft.Floor < coll->Setup.UpperFloorBound ||
|
||||||
coll->MiddleLeft.Ceiling > coll->Setup.LowerCeilingBound ||
|
coll->MiddleLeft.Ceiling > coll->Setup.LowerCeilingBound ||
|
||||||
|
@ -678,23 +699,19 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, const Vector3i& offse
|
||||||
{
|
{
|
||||||
if (coll->TriangleAtLeft() && !coll->MiddleLeft.FloorSlope)
|
if (coll->TriangleAtLeft() && !coll->MiddleLeft.FloorSlope)
|
||||||
{
|
{
|
||||||
// HACK: Force slight push-out to the left side to avoid stucking
|
HandleDiagonalShift(*item, *coll, entityPos, (CardinalDirection)quadrant, true);
|
||||||
TranslateItem(item, coll->Setup.ForwardAngle + ANGLE(8.0f), item->Animation.Velocity.z);
|
|
||||||
|
|
||||||
coll->Shift.Position.x += coll->Setup.PrevPosition.x - entityPos.x;
|
|
||||||
coll->Shift.Position.z += coll->Setup.PrevPosition.z - entityPos.z;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
switch (quadrant)
|
switch (quadrant)
|
||||||
{
|
{
|
||||||
case 0:
|
case NORTH:
|
||||||
case 2:
|
case SOUTH:
|
||||||
coll->Shift.Position.x += FindGridShift(entityPos.x + xLeft, entityPos.x + xFront);
|
coll->Shift.Position.x += FindGridShift(entityPos.x + xLeft, entityPos.x + xFront);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 1:
|
case EAST:
|
||||||
case 3:
|
case WEST:
|
||||||
coll->Shift.Position.z += FindGridShift(entityPos.z + zLeft, entityPos.z + zFront);
|
coll->Shift.Position.z += FindGridShift(entityPos.z + zLeft, entityPos.z + zFront);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -702,7 +719,8 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, const Vector3i& offse
|
||||||
|
|
||||||
if (coll->DiagonalStepAtLeft())
|
if (coll->DiagonalStepAtLeft())
|
||||||
{
|
{
|
||||||
int quarter = (unsigned short)(coll->Setup.ForwardAngle) / ANGLE(90.0f); // NOTE: Different from quadrant!
|
// NOTE: Different from quadrant.
|
||||||
|
int quarter = (unsigned short)coll->Setup.ForwardAngle / ANGLE(90.0f);
|
||||||
quarter %= 2;
|
quarter %= 2;
|
||||||
|
|
||||||
if (coll->MiddleLeft.HasFlippedDiagonalSplit())
|
if (coll->MiddleLeft.HasFlippedDiagonalSplit())
|
||||||
|
@ -724,6 +742,7 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, const Vector3i& offse
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Shift away from right wall.
|
||||||
if (coll->MiddleRight.Floor > coll->Setup.LowerFloorBound ||
|
if (coll->MiddleRight.Floor > coll->Setup.LowerFloorBound ||
|
||||||
coll->MiddleRight.Floor < coll->Setup.UpperFloorBound ||
|
coll->MiddleRight.Floor < coll->Setup.UpperFloorBound ||
|
||||||
coll->MiddleRight.Ceiling > coll->Setup.LowerCeilingBound ||
|
coll->MiddleRight.Ceiling > coll->Setup.LowerCeilingBound ||
|
||||||
|
@ -732,23 +751,19 @@ void GetCollisionInfo(CollisionInfo* coll, ItemInfo* item, const Vector3i& offse
|
||||||
{
|
{
|
||||||
if (coll->TriangleAtRight() && !coll->MiddleRight.FloorSlope)
|
if (coll->TriangleAtRight() && !coll->MiddleRight.FloorSlope)
|
||||||
{
|
{
|
||||||
// HACK: Force slight push out to the right side to avoid getting stuck.
|
HandleDiagonalShift(*item, *coll, entityPos, (CardinalDirection)quadrant, false);
|
||||||
TranslateItem(item, coll->Setup.ForwardAngle - ANGLE(8.0f), item->Animation.Velocity.z);
|
|
||||||
|
|
||||||
coll->Shift.Position.x += coll->Setup.PrevPosition.x - entityPos.x;
|
|
||||||
coll->Shift.Position.z += coll->Setup.PrevPosition.z - entityPos.z;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
switch (quadrant)
|
switch (quadrant)
|
||||||
{
|
{
|
||||||
case 0:
|
case NORTH:
|
||||||
case 2:
|
case SOUTH:
|
||||||
coll->Shift.Position.x += FindGridShift(entityPos.x + xRight, entityPos.x + xFront);
|
coll->Shift.Position.x += FindGridShift(entityPos.x + xRight, entityPos.x + xFront);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 1:
|
case EAST:
|
||||||
case 3:
|
case WEST:
|
||||||
coll->Shift.Position.z += FindGridShift(entityPos.z + zRight, entityPos.z + zFront);
|
coll->Shift.Position.z += FindGridShift(entityPos.z + zRight, entityPos.z + zFront);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -903,7 +918,7 @@ short GetNearestLedgeAngle(ItemInfo* item, CollisionInfo* coll, float& distance)
|
||||||
float maxZ = ceil(ffpZ / BLOCK(1)) * BLOCK(1) + 1.0f;
|
float maxZ = ceil(ffpZ / BLOCK(1)) * BLOCK(1) + 1.0f;
|
||||||
|
|
||||||
// Get front floor block
|
// Get front floor block
|
||||||
auto room = GetRoom(item->Location, ffpX, y, ffpZ).roomNumber;
|
auto room = GetRoom(item->Location, Vector3i(ffpX, y, ffpZ)).roomNumber;
|
||||||
auto block = GetCollision(ffpX, y, ffpZ, room).Block;
|
auto block = GetCollision(ffpX, y, ffpZ, room).Block;
|
||||||
|
|
||||||
// Get front floor surface heights
|
// Get front floor surface heights
|
||||||
|
@ -921,7 +936,7 @@ short GetNearestLedgeAngle(ItemInfo* item, CollisionInfo* coll, float& distance)
|
||||||
int height = useCeilingLedge ? ceilingHeight : floorHeight;
|
int height = useCeilingLedge ? ceilingHeight : floorHeight;
|
||||||
|
|
||||||
// Determine if there is a bridge in front.
|
// Determine if there is a bridge in front.
|
||||||
auto bridge = block->GetInsideBridgeItemNumber(ffpX, height, ffpZ, true, y == height);
|
auto bridge = block->GetInsideBridgeItemNumber(Vector3i(ffpX, height, ffpZ), true, y == height);
|
||||||
|
|
||||||
// Determine floor probe offset.
|
// Determine floor probe offset.
|
||||||
// This must be slightly in front of own coll radius so no bridge misfires occur.
|
// This must be slightly in front of own coll radius so no bridge misfires occur.
|
||||||
|
@ -933,7 +948,7 @@ short GetNearestLedgeAngle(ItemInfo* item, CollisionInfo* coll, float& distance)
|
||||||
// g_Renderer.AddDebugSphere(Vector3(fpX, y, fpZ), 16, Vector4(0, 1, 0, 1), RendererDebugPage::CollisionStats);
|
// g_Renderer.AddDebugSphere(Vector3(fpX, y, fpZ), 16, Vector4(0, 1, 0, 1), RendererDebugPage::CollisionStats);
|
||||||
|
|
||||||
// Get true room number and block, based on derived height
|
// Get true room number and block, based on derived height
|
||||||
room = GetRoom(item->Location, fpX, height, fpZ).roomNumber;
|
room = GetRoom(item->Location, Vector3i(fpX, height, fpZ)).roomNumber;
|
||||||
block = GetCollision(fpX, height, fpZ, room).Block;
|
block = GetCollision(fpX, height, fpZ, room).Block;
|
||||||
|
|
||||||
// We don't need actual corner heights to build planes, so just use normalized value here.
|
// We don't need actual corner heights to build planes, so just use normalized value here.
|
||||||
|
@ -1132,7 +1147,7 @@ short GetNearestLedgeAngle(ItemInfo* item, CollisionInfo* coll, float& distance)
|
||||||
|
|
||||||
FloorInfo* GetFloor(int x, int y, int z, short* roomNumber)
|
FloorInfo* GetFloor(int x, int y, int z, short* roomNumber)
|
||||||
{
|
{
|
||||||
const auto location = GetRoom(ROOM_VECTOR{ *roomNumber, y }, x, y, z);
|
const auto location = GetRoom(ROOM_VECTOR{ *roomNumber, y }, Vector3i(x, y, z));
|
||||||
*roomNumber = location.roomNumber;
|
*roomNumber = location.roomNumber;
|
||||||
return &GetFloor(*roomNumber, x, z);
|
return &GetFloor(*roomNumber, x, z);
|
||||||
}
|
}
|
||||||
|
@ -1164,33 +1179,6 @@ int GetDistanceToFloor(int itemNumber, bool precise)
|
||||||
return (minHeight + item->Pose.Position.y - height);
|
return (minHeight + item->Pose.Position.y - height);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AlterFloorHeight(ItemInfo* item, int height)
|
|
||||||
{
|
|
||||||
if (abs(height))
|
|
||||||
{
|
|
||||||
if (height >= 0)
|
|
||||||
height++;
|
|
||||||
else
|
|
||||||
height--;
|
|
||||||
}
|
|
||||||
|
|
||||||
short roomNumber = item->RoomNumber;
|
|
||||||
FloorInfo* floor = GetFloor(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z, &roomNumber);
|
|
||||||
FloorInfo* ceiling = GetFloor(item->Pose.Position.x, height + item->Pose.Position.y - BLOCK(1), item->Pose.Position.z, &roomNumber);
|
|
||||||
|
|
||||||
floor->FloorCollision.Planes[0].z += height;
|
|
||||||
floor->FloorCollision.Planes[1].z += height;
|
|
||||||
|
|
||||||
auto* box = &g_Level.Boxes[floor->Box];
|
|
||||||
if (box->flags & BLOCKABLE)
|
|
||||||
{
|
|
||||||
if (height >= 0)
|
|
||||||
box->flags &= ~BLOCKED;
|
|
||||||
else
|
|
||||||
box->flags |= BLOCKED;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int GetWaterSurface(int x, int y, int z, short roomNumber)
|
int GetWaterSurface(int x, int y, int z, short roomNumber)
|
||||||
{
|
{
|
||||||
auto* room = &g_Level.Rooms[roomNumber];
|
auto* room = &g_Level.Rooms[roomNumber];
|
||||||
|
@ -1198,9 +1186,9 @@ int GetWaterSurface(int x, int y, int z, short roomNumber)
|
||||||
|
|
||||||
if (TestEnvironment(ENV_FLAG_WATER, room))
|
if (TestEnvironment(ENV_FLAG_WATER, room))
|
||||||
{
|
{
|
||||||
while (floor->GetRoomNumberAbove(x, y, z).value_or(NO_ROOM) != NO_ROOM)
|
while (floor->GetRoomNumberAbove(Vector3i(x, y, z)).value_or(NO_ROOM) != NO_ROOM)
|
||||||
{
|
{
|
||||||
room = &g_Level.Rooms[floor->GetRoomNumberAbove(x, y, z).value_or(floor->Room)];
|
room = &g_Level.Rooms[floor->GetRoomNumberAbove(Vector3i(x, y, z)).value_or(floor->Room)];
|
||||||
if (!TestEnvironment(ENV_FLAG_WATER, room))
|
if (!TestEnvironment(ENV_FLAG_WATER, room))
|
||||||
return (floor->GetSurfaceHeight(x, z, false));
|
return (floor->GetSurfaceHeight(x, z, false));
|
||||||
|
|
||||||
|
@ -1211,9 +1199,9 @@ int GetWaterSurface(int x, int y, int z, short roomNumber)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
while (floor->GetRoomNumberBelow(x, y, z).value_or(NO_ROOM) != NO_ROOM)
|
while (floor->GetRoomNumberBelow(Vector3i(x, y, z)).value_or(NO_ROOM) != NO_ROOM)
|
||||||
{
|
{
|
||||||
room = &g_Level.Rooms[floor->GetRoomNumberBelow(x, y, z).value_or(floor->Room)];
|
room = &g_Level.Rooms[floor->GetRoomNumberBelow(Vector3i(x, y, z)).value_or(floor->Room)];
|
||||||
if (TestEnvironment(ENV_FLAG_WATER, room))
|
if (TestEnvironment(ENV_FLAG_WATER, room))
|
||||||
return (floor->GetSurfaceHeight(x, z, true));
|
return (floor->GetSurfaceHeight(x, z, true));
|
||||||
|
|
||||||
|
@ -1274,9 +1262,9 @@ int GetWaterDepth(int x, int y, int z, short roomNumber)
|
||||||
if (TestEnvironment(ENV_FLAG_WATER, room) ||
|
if (TestEnvironment(ENV_FLAG_WATER, room) ||
|
||||||
TestEnvironment(ENV_FLAG_SWAMP, room))
|
TestEnvironment(ENV_FLAG_SWAMP, room))
|
||||||
{
|
{
|
||||||
while (floor->GetRoomNumberAbove(x, y, z).value_or(NO_ROOM) != NO_ROOM)
|
while (floor->GetRoomNumberAbove(Vector3i(x, y, z)).value_or(NO_ROOM) != NO_ROOM)
|
||||||
{
|
{
|
||||||
room = &g_Level.Rooms[floor->GetRoomNumberAbove(x, y, z).value_or(floor->Room)];
|
room = &g_Level.Rooms[floor->GetRoomNumberAbove(Vector3i(x, y, z)).value_or(floor->Room)];
|
||||||
|
|
||||||
if (!TestEnvironment(ENV_FLAG_WATER, room) &&
|
if (!TestEnvironment(ENV_FLAG_WATER, room) &&
|
||||||
!TestEnvironment(ENV_FLAG_SWAMP, room))
|
!TestEnvironment(ENV_FLAG_SWAMP, room))
|
||||||
|
@ -1293,9 +1281,9 @@ int GetWaterDepth(int x, int y, int z, short roomNumber)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
while (floor->GetRoomNumberBelow(x, y, z).value_or(NO_ROOM) != NO_ROOM)
|
while (floor->GetRoomNumberBelow(Vector3i(x, y, z)).value_or(NO_ROOM) != NO_ROOM)
|
||||||
{
|
{
|
||||||
room = &g_Level.Rooms[floor->GetRoomNumberBelow(x, y, z).value_or(floor->Room)];
|
room = &g_Level.Rooms[floor->GetRoomNumberBelow(Vector3i(x, y, z)).value_or(floor->Room)];
|
||||||
|
|
||||||
if (TestEnvironment(ENV_FLAG_WATER, room) ||
|
if (TestEnvironment(ENV_FLAG_WATER, room) ||
|
||||||
TestEnvironment(ENV_FLAG_SWAMP, room))
|
TestEnvironment(ENV_FLAG_SWAMP, room))
|
||||||
|
@ -1365,9 +1353,9 @@ int GetWaterHeight(int x, int y, int z, short roomNumber)
|
||||||
if (TestEnvironment(ENV_FLAG_WATER, room) ||
|
if (TestEnvironment(ENV_FLAG_WATER, room) ||
|
||||||
TestEnvironment(ENV_FLAG_SWAMP, room))
|
TestEnvironment(ENV_FLAG_SWAMP, room))
|
||||||
{
|
{
|
||||||
while (floor->GetRoomNumberAbove(x, y, z).value_or(NO_ROOM) != NO_ROOM)
|
while (floor->GetRoomNumberAbove(Vector3i(x, y, z)).value_or(NO_ROOM) != NO_ROOM)
|
||||||
{
|
{
|
||||||
auto* room = &g_Level.Rooms[floor->GetRoomNumberAbove(x, y, z).value_or(floor->Room)];
|
auto* room = &g_Level.Rooms[floor->GetRoomNumberAbove(Vector3i(x, y, z)).value_or(floor->Room)];
|
||||||
|
|
||||||
if (!TestEnvironment(ENV_FLAG_WATER, room) &&
|
if (!TestEnvironment(ENV_FLAG_WATER, room) &&
|
||||||
!TestEnvironment(ENV_FLAG_SWAMP, room))
|
!TestEnvironment(ENV_FLAG_SWAMP, room))
|
||||||
|
@ -1376,13 +1364,13 @@ int GetWaterHeight(int x, int y, int z, short roomNumber)
|
||||||
floor = GetSector(room, x - room->x, z - room->z);
|
floor = GetSector(room, x - room->x, z - room->z);
|
||||||
}
|
}
|
||||||
|
|
||||||
return GetCollision(floor, x, y, z).Block->GetSurfaceHeight(x, y, z, false);
|
return GetCollision(floor, x, y, z).Block->GetSurfaceHeight(Vector3i(x, y, z), false);
|
||||||
}
|
}
|
||||||
else if (floor->GetRoomNumberBelow(x, y, z).value_or(NO_ROOM) != NO_ROOM)
|
else if (floor->GetRoomNumberBelow(Vector3i(x, y, z)).value_or(NO_ROOM) != NO_ROOM)
|
||||||
{
|
{
|
||||||
while (floor->GetRoomNumberBelow(x, y, z).value_or(NO_ROOM) != NO_ROOM)
|
while (floor->GetRoomNumberBelow(Vector3i(x, y, z)).value_or(NO_ROOM) != NO_ROOM)
|
||||||
{
|
{
|
||||||
auto* room2 = &g_Level.Rooms[floor->GetRoomNumberBelow(x, y, z).value_or(floor->Room)];
|
auto* room2 = &g_Level.Rooms[floor->GetRoomNumberBelow(Vector3i(x, y, z)).value_or(floor->Room)];
|
||||||
|
|
||||||
if (TestEnvironment(ENV_FLAG_WATER, room2) ||
|
if (TestEnvironment(ENV_FLAG_WATER, room2) ||
|
||||||
TestEnvironment(ENV_FLAG_SWAMP, room2))
|
TestEnvironment(ENV_FLAG_SWAMP, room2))
|
||||||
|
@ -1391,7 +1379,7 @@ int GetWaterHeight(int x, int y, int z, short roomNumber)
|
||||||
floor = GetSector(room2, x - room2->x, z - room2->z);
|
floor = GetSector(room2, x - room2->x, z - room2->z);
|
||||||
}
|
}
|
||||||
|
|
||||||
return GetCollision(floor, x, y, z).Block->GetSurfaceHeight(x, y, z, true);
|
return GetCollision(floor, x, y, z).Block->GetSurfaceHeight(Vector3i(x, y, z), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
return NO_HEIGHT;
|
return NO_HEIGHT;
|
||||||
|
|
|
@ -145,7 +145,6 @@ FloorInfo* GetFloor(int x, int y, int z, short* roomNumber);
|
||||||
int GetFloorHeight(FloorInfo* floor, int x, int y, int z);
|
int GetFloorHeight(FloorInfo* floor, int x, int y, int z);
|
||||||
int GetCeiling(FloorInfo* floor, int x, int y, int z);
|
int GetCeiling(FloorInfo* floor, int x, int y, int z);
|
||||||
int GetDistanceToFloor(int itemNumber, bool precise = true);
|
int GetDistanceToFloor(int itemNumber, bool precise = true);
|
||||||
void AlterFloorHeight(ItemInfo* item, int height);
|
|
||||||
|
|
||||||
int GetWaterSurface(int x, int y, int z, short roomNumber);
|
int GetWaterSurface(int x, int y, int z, short roomNumber);
|
||||||
int GetWaterSurface(ItemInfo* item);
|
int GetWaterSurface(ItemInfo* item);
|
||||||
|
|
|
@ -8,9 +8,11 @@
|
||||||
#include "Math/Math.h"
|
#include "Math/Math.h"
|
||||||
#include "Renderer/Renderer11.h"
|
#include "Renderer/Renderer11.h"
|
||||||
#include "Specific/level.h"
|
#include "Specific/level.h"
|
||||||
|
#include "Specific/trutils.h"
|
||||||
|
|
||||||
using namespace TEN::Collision::Floordata;
|
using namespace TEN::Collision::Floordata;
|
||||||
using namespace TEN::Math;
|
using namespace TEN::Math;
|
||||||
|
using namespace TEN::Utils;
|
||||||
|
|
||||||
int FloorInfo::GetSurfacePlaneIndex(int x, int z, bool isFloor) const
|
int FloorInfo::GetSurfacePlaneIndex(int x, int z, bool isFloor) const
|
||||||
{
|
{
|
||||||
|
@ -86,25 +88,25 @@ std::optional<int> FloorInfo::GetRoomNumberBelow(int x, int z) const
|
||||||
return GetRoomNumberBelow(planeIndex);
|
return GetRoomNumberBelow(planeIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<int> FloorInfo::GetRoomNumberBelow(int x, int y, int z) const
|
std::optional<int> FloorInfo::GetRoomNumberBelow(const Vector3i& pos) const
|
||||||
{
|
{
|
||||||
// Get surface heights.
|
// Get surface heights.
|
||||||
int floorHeight = GetSurfaceHeight(x, z, true);
|
int floorHeight = GetSurfaceHeight(pos.x, pos.z, true);
|
||||||
int ceilingHeight = GetSurfaceHeight(x, z, false);
|
int ceilingHeight = GetSurfaceHeight(pos.x, pos.z, false);
|
||||||
|
|
||||||
// Loop through bridges.
|
// Loop through bridges.
|
||||||
for (const int& i : BridgeItemNumbers)
|
for (int i : BridgeItemNumbers)
|
||||||
{
|
{
|
||||||
const auto& bridgeItem = g_Level.Items[i];
|
const auto& bridgeItem = g_Level.Items[i];
|
||||||
const auto& bridgeObject = Objects[bridgeItem.ObjectNumber];
|
const auto& bridgeObject = Objects[bridgeItem.ObjectNumber];
|
||||||
|
|
||||||
// Get bridge floor height.
|
// Get bridge floor height.
|
||||||
auto bridgeFloorHeight = bridgeObject.floor(i, x, y, z);
|
auto bridgeFloorHeight = bridgeObject.floor(i, pos.x, pos.y, pos.z);
|
||||||
if (!bridgeFloorHeight.has_value())
|
if (!bridgeFloorHeight.has_value())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Assess relation of bridge to collision block.
|
// Assess relation of bridge to collision block.
|
||||||
if (*bridgeFloorHeight >= y && // Below input height bound.
|
if (*bridgeFloorHeight >= pos.y && // Below input height bound.
|
||||||
*bridgeFloorHeight <= floorHeight && // Within floor bound.
|
*bridgeFloorHeight <= floorHeight && // Within floor bound.
|
||||||
*bridgeFloorHeight >= ceilingHeight) // Within ceiling bound.
|
*bridgeFloorHeight >= ceilingHeight) // Within ceiling bound.
|
||||||
{
|
{
|
||||||
|
@ -112,7 +114,7 @@ std::optional<int> FloorInfo::GetRoomNumberBelow(int x, int y, int z) const
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return GetRoomNumberBelow(x, z);
|
return GetRoomNumberBelow(pos.x, pos.z);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<int> FloorInfo::GetRoomNumberAbove(int planeIndex) const
|
std::optional<int> FloorInfo::GetRoomNumberAbove(int planeIndex) const
|
||||||
|
@ -127,25 +129,25 @@ std::optional<int> FloorInfo::GetRoomNumberAbove(int x, int z) const
|
||||||
return GetRoomNumberAbove(planeIndex);
|
return GetRoomNumberAbove(planeIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<int> FloorInfo::GetRoomNumberAbove(int x, int y, int z) const
|
std::optional<int> FloorInfo::GetRoomNumberAbove(const Vector3i& pos) const
|
||||||
{
|
{
|
||||||
// Get surface heights.
|
// Get surface heights.
|
||||||
int floorHeight = GetSurfaceHeight(x, z, true);
|
int floorHeight = GetSurfaceHeight(pos.x, pos.z, true);
|
||||||
int ceilingHeight = GetSurfaceHeight(x, z, false);
|
int ceilingHeight = GetSurfaceHeight(pos.x, pos.z, false);
|
||||||
|
|
||||||
// Loop through bridges.
|
// Loop through bridges.
|
||||||
for (const int& i : BridgeItemNumbers)
|
for (int i : BridgeItemNumbers)
|
||||||
{
|
{
|
||||||
const auto& bridgeItem = g_Level.Items[i];
|
const auto& bridgeItem = g_Level.Items[i];
|
||||||
const auto& bridgeObject = Objects[bridgeItem.ObjectNumber];
|
const auto& bridgeObject = Objects[bridgeItem.ObjectNumber];
|
||||||
|
|
||||||
// Get bridge ceiling height.
|
// Get bridge ceiling height.
|
||||||
auto bridgeCeilingHeight = bridgeObject.ceiling(i, x, y, z);
|
auto bridgeCeilingHeight = bridgeObject.ceiling(i, pos.x, pos.y, pos.z);
|
||||||
if (!bridgeCeilingHeight.has_value())
|
if (!bridgeCeilingHeight.has_value())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Assess relation of bridge to collision block.
|
// Assess relation of bridge to collision block.
|
||||||
if (*bridgeCeilingHeight <= y && // Above input height bound.
|
if (*bridgeCeilingHeight <= pos.y && // Above input height bound.
|
||||||
*bridgeCeilingHeight <= floorHeight && // Within floor bound.
|
*bridgeCeilingHeight <= floorHeight && // Within floor bound.
|
||||||
*bridgeCeilingHeight >= ceilingHeight) // Within ceiling bound.
|
*bridgeCeilingHeight >= ceilingHeight) // Within ceiling bound.
|
||||||
{
|
{
|
||||||
|
@ -153,7 +155,7 @@ std::optional<int> FloorInfo::GetRoomNumberAbove(int x, int y, int z) const
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return GetRoomNumberAbove(x, z);
|
return GetRoomNumberAbove(pos.x, pos.z);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<int> FloorInfo::GetRoomNumberAtSide() const
|
std::optional<int> FloorInfo::GetRoomNumberAtSide() const
|
||||||
|
@ -177,27 +179,27 @@ int FloorInfo::GetSurfaceHeight(int x, int z, bool isFloor) const
|
||||||
plane.z);
|
plane.z);
|
||||||
}
|
}
|
||||||
|
|
||||||
int FloorInfo::GetSurfaceHeight(int x, int y, int z, bool isFloor) const
|
int FloorInfo::GetSurfaceHeight(const Vector3i& pos, bool isFloor) const
|
||||||
{
|
{
|
||||||
// Get surface heights.
|
// Get surface heights.
|
||||||
int floorHeight = GetSurfaceHeight(x, z, true);
|
int floorHeight = GetSurfaceHeight(pos.x, pos.z, true);
|
||||||
int ceilingHeight = GetSurfaceHeight(x, z, false);
|
int ceilingHeight = GetSurfaceHeight(pos.x, pos.z, false);
|
||||||
|
|
||||||
// Loop through bridges.
|
// Loop through bridges.
|
||||||
for (const int& i : BridgeItemNumbers)
|
for (int i : BridgeItemNumbers)
|
||||||
{
|
{
|
||||||
const auto& bridgeItem = g_Level.Items[i];
|
const auto& bridgeItem = g_Level.Items[i];
|
||||||
const auto& bridgeObject = Objects[bridgeItem.ObjectNumber];
|
const auto& bridgeObject = Objects[bridgeItem.ObjectNumber];
|
||||||
|
|
||||||
// Get bridge surface height.
|
// Get bridge surface height.
|
||||||
auto bridgeSurfaceHeight = isFloor ? bridgeObject.floor(i, x, y, z) : bridgeObject.ceiling(i, x, y, z);
|
auto bridgeSurfaceHeight = isFloor ? bridgeObject.floor(i, pos.x, pos.y, pos.z) : bridgeObject.ceiling(i, pos.x, pos.y, pos.z);
|
||||||
if (!bridgeSurfaceHeight.has_value())
|
if (!bridgeSurfaceHeight.has_value())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Assess relation of bridge to collision block.
|
// Assess relation of bridge to collision block.
|
||||||
if (isFloor)
|
if (isFloor)
|
||||||
{
|
{
|
||||||
if (*bridgeSurfaceHeight >= y && // Below input height bound.
|
if (*bridgeSurfaceHeight >= pos.y && // Below input height bound.
|
||||||
*bridgeSurfaceHeight < floorHeight && // Within floor bound.
|
*bridgeSurfaceHeight < floorHeight && // Within floor bound.
|
||||||
*bridgeSurfaceHeight >= ceilingHeight) // Within ceiling bound.
|
*bridgeSurfaceHeight >= ceilingHeight) // Within ceiling bound.
|
||||||
{
|
{
|
||||||
|
@ -206,7 +208,7 @@ int FloorInfo::GetSurfaceHeight(int x, int y, int z, bool isFloor) const
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (*bridgeSurfaceHeight <= y && // Above input height bound.
|
if (*bridgeSurfaceHeight <= pos.y && // Above input height bound.
|
||||||
*bridgeSurfaceHeight <= floorHeight && // Within floor bound.
|
*bridgeSurfaceHeight <= floorHeight && // Within floor bound.
|
||||||
*bridgeSurfaceHeight > ceilingHeight) // Within ceiling bound.
|
*bridgeSurfaceHeight > ceilingHeight) // Within ceiling bound.
|
||||||
{
|
{
|
||||||
|
@ -219,33 +221,33 @@ int FloorInfo::GetSurfaceHeight(int x, int y, int z, bool isFloor) const
|
||||||
return (isFloor ? floorHeight : ceilingHeight);
|
return (isFloor ? floorHeight : ceilingHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
int FloorInfo::GetBridgeSurfaceHeight(int x, int y, int z, bool isFloor) const
|
int FloorInfo::GetBridgeSurfaceHeight(const Vector3i& pos, bool isFloor) const
|
||||||
{
|
{
|
||||||
// Loop through bridges.
|
// Loop through bridges.
|
||||||
for (const int& i : BridgeItemNumbers)
|
for (int i : BridgeItemNumbers)
|
||||||
{
|
{
|
||||||
const auto& bridgeItem = g_Level.Items[i];
|
const auto& bridgeItem = g_Level.Items[i];
|
||||||
const auto& bridgeObject = Objects[bridgeItem.ObjectNumber];
|
const auto& bridgeObject = Objects[bridgeItem.ObjectNumber];
|
||||||
|
|
||||||
// Get surface heights.
|
// Get surface heights.
|
||||||
auto floorHeight = bridgeObject.floor(i, x, y, z);
|
auto floorHeight = bridgeObject.floor(i, pos.x, pos.y, pos.z);
|
||||||
auto ceilingHeight = bridgeObject.ceiling(i, x, y, z);
|
auto ceilingHeight = bridgeObject.ceiling(i, pos.x, pos.y, pos.z);
|
||||||
if (!floorHeight.has_value() || !ceilingHeight.has_value())
|
if (!floorHeight.has_value() || !ceilingHeight.has_value())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Assess relation of bridge to collision block.
|
// Assess relation of bridge to collision block.
|
||||||
if (isFloor)
|
if (isFloor)
|
||||||
{
|
{
|
||||||
if (y > *floorHeight &&
|
if (pos.y > *floorHeight &&
|
||||||
y <= *ceilingHeight)
|
pos.y <= *ceilingHeight)
|
||||||
{
|
{
|
||||||
return *floorHeight;
|
return *floorHeight;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (y >= *floorHeight &&
|
if (pos.y >= *floorHeight &&
|
||||||
y < *ceilingHeight)
|
pos.y < *ceilingHeight)
|
||||||
{
|
{
|
||||||
return *ceilingHeight;
|
return *ceilingHeight;
|
||||||
}
|
}
|
||||||
|
@ -253,7 +255,7 @@ int FloorInfo::GetBridgeSurfaceHeight(int x, int y, int z, bool isFloor) const
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return bridge surface height.
|
// Return bridge surface height.
|
||||||
return GetSurfaceHeight(x, y, z, isFloor);
|
return GetSurfaceHeight(pos, isFloor);
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector2 FloorInfo::GetSurfaceSlope(int planeIndex, bool isFloor) const
|
Vector2 FloorInfo::GetSurfaceSlope(int planeIndex, bool isFloor) const
|
||||||
|
@ -281,7 +283,7 @@ bool FloorInfo::IsWall(int x, int z) const
|
||||||
return IsWall(planeIndex);
|
return IsWall(planeIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
int FloorInfo::GetInsideBridgeItemNumber(int x, int y, int z, bool testFloorBorder, bool testCeilingBorder) const
|
int FloorInfo::GetInsideBridgeItemNumber(const Vector3i& pos, bool testFloorBorder, bool testCeilingBorder) const
|
||||||
{
|
{
|
||||||
for (int itemNumber : BridgeItemNumbers)
|
for (int itemNumber : BridgeItemNumbers)
|
||||||
{
|
{
|
||||||
|
@ -289,14 +291,19 @@ int FloorInfo::GetInsideBridgeItemNumber(int x, int y, int z, bool testFloorBord
|
||||||
const auto& bridgeObject = Objects[bridgeItem.ObjectNumber];
|
const auto& bridgeObject = Objects[bridgeItem.ObjectNumber];
|
||||||
|
|
||||||
// Get surface heights.
|
// Get surface heights.
|
||||||
auto floorHeight = bridgeObject.floor(itemNumber, x, y, z);
|
auto floorHeight = bridgeObject.floor(itemNumber, pos.x, pos.y, pos.z);
|
||||||
auto ceilingHeight = bridgeObject.ceiling(itemNumber, x, y, z);
|
auto ceilingHeight = bridgeObject.ceiling(itemNumber, pos.x, pos.y, pos.z);
|
||||||
if (!floorHeight.has_value() || !ceilingHeight.has_value())
|
if (!floorHeight.has_value() || !ceilingHeight.has_value())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if ((y > *floorHeight && y < *ceilingHeight) ||
|
if (pos.y > *floorHeight &&
|
||||||
(testFloorBorder && y == *floorHeight) ||
|
pos.y < *ceilingHeight)
|
||||||
(testCeilingBorder && y == *ceilingHeight))
|
{
|
||||||
|
return itemNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((testFloorBorder && pos.y == *floorHeight) ||
|
||||||
|
(testCeilingBorder && pos.y == *ceilingHeight))
|
||||||
{
|
{
|
||||||
return itemNumber;
|
return itemNumber;
|
||||||
}
|
}
|
||||||
|
@ -361,10 +368,10 @@ namespace TEN::Collision::Floordata
|
||||||
return pos;
|
return pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
FloorInfo& GetFloor(int roomNumber, const Vector2i& pos)
|
FloorInfo& GetFloor(int roomNumber, const Vector2i& roomPos)
|
||||||
{
|
{
|
||||||
auto& room = g_Level.Rooms[roomNumber];
|
auto& room = g_Level.Rooms[roomNumber];
|
||||||
return room.floor[room.zSize * pos.x + pos.y];
|
return room.floor[(room.zSize * roomPos.x) + roomPos.y];
|
||||||
}
|
}
|
||||||
|
|
||||||
FloorInfo& GetFloor(int roomNumber, int x, int z)
|
FloorInfo& GetFloor(int roomNumber, int x, int z)
|
||||||
|
@ -433,7 +440,7 @@ namespace TEN::Collision::Floordata
|
||||||
auto* sectorPtr = &startSector;
|
auto* sectorPtr = &startSector;
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
pos.y = sectorPtr->GetBridgeSurfaceHeight(pos.x, pos.y, pos.z, true);
|
pos.y = sectorPtr->GetBridgeSurfaceHeight(pos, true);
|
||||||
while (pos.y <= sectorPtr->GetSurfaceHeight(pos.x, pos.z, false))
|
while (pos.y <= sectorPtr->GetSurfaceHeight(pos.x, pos.z, false))
|
||||||
{
|
{
|
||||||
auto roomNumberAbove = sectorPtr->GetRoomNumberAbove(pos.x, pos.z);
|
auto roomNumberAbove = sectorPtr->GetRoomNumberAbove(pos.x, pos.z);
|
||||||
|
@ -443,7 +450,7 @@ namespace TEN::Collision::Floordata
|
||||||
sectorPtr = &GetFloorSide(*roomNumberAbove, pos.x, pos.z, &roomNumber);
|
sectorPtr = &GetFloorSide(*roomNumberAbove, pos.x, pos.z, &roomNumber);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
while (sectorPtr->GetInsideBridgeItemNumber(pos.x, pos.y, pos.z, false, true) >= 0);
|
while (sectorPtr->GetInsideBridgeItemNumber(pos, false, true) >= 0);
|
||||||
|
|
||||||
if (topRoomNumberPtr != nullptr)
|
if (topRoomNumberPtr != nullptr)
|
||||||
*topRoomNumberPtr = roomNumber;
|
*topRoomNumberPtr = roomNumber;
|
||||||
|
@ -463,7 +470,7 @@ namespace TEN::Collision::Floordata
|
||||||
auto* sectorPtr = &startSector;
|
auto* sectorPtr = &startSector;
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
pos.y = sectorPtr->GetBridgeSurfaceHeight(pos.x, pos.y, pos.z, false);
|
pos.y = sectorPtr->GetBridgeSurfaceHeight(pos, false);
|
||||||
while (pos.y >= sectorPtr->GetSurfaceHeight(pos.x, pos.z, true))
|
while (pos.y >= sectorPtr->GetSurfaceHeight(pos.x, pos.z, true))
|
||||||
{
|
{
|
||||||
auto roomNumberBelow = sectorPtr->GetRoomNumberBelow(pos.x, pos.z);
|
auto roomNumberBelow = sectorPtr->GetRoomNumberBelow(pos.x, pos.z);
|
||||||
|
@ -473,7 +480,7 @@ namespace TEN::Collision::Floordata
|
||||||
sectorPtr = &GetFloorSide(*roomNumberBelow, pos.x, pos.z, &roomNumber);
|
sectorPtr = &GetFloorSide(*roomNumberBelow, pos.x, pos.z, &roomNumber);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
while (sectorPtr->GetInsideBridgeItemNumber(pos.x, pos.y, pos.z, true, false) >= 0);
|
while (sectorPtr->GetInsideBridgeItemNumber(pos, true, false) >= 0);
|
||||||
|
|
||||||
if (bottomRoomNumberPtr)
|
if (bottomRoomNumberPtr)
|
||||||
*bottomRoomNumberPtr = roomNumber;
|
*bottomRoomNumberPtr = roomNumber;
|
||||||
|
@ -509,16 +516,18 @@ namespace TEN::Collision::Floordata
|
||||||
direction = 1;
|
direction = 1;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto floorHeight = floor->GetSurfaceHeight(x, y, z, true);
|
const auto floorHeight = floor->GetSurfaceHeight(Vector3i(x, y, z), true);
|
||||||
const auto ceilingHeight = floor->GetSurfaceHeight(x, y, z, false);
|
const auto ceilingHeight = floor->GetSurfaceHeight(Vector3i(x, y, z), false);
|
||||||
|
|
||||||
y = std::clamp(y, std::min(ceilingHeight, floorHeight), std::max(ceilingHeight, floorHeight));
|
y = std::clamp(y, std::min(ceilingHeight, floorHeight), std::max(ceilingHeight, floorHeight));
|
||||||
|
|
||||||
if (floor->GetInsideBridgeItemNumber(x, y, z, y == ceilingHeight, y == floorHeight) >= 0)
|
if (floor->GetInsideBridgeItemNumber(Vector3i(x, y, z), y == ceilingHeight, y == floorHeight) >= 0)
|
||||||
{
|
{
|
||||||
if (direction <= 0)
|
if (direction <= 0)
|
||||||
{
|
{
|
||||||
|
@ -539,15 +548,15 @@ namespace TEN::Collision::Floordata
|
||||||
|
|
||||||
if (direction >= 0)
|
if (direction >= 0)
|
||||||
{
|
{
|
||||||
auto roomBelow = floor->GetRoomNumberBelow(x, y, z);
|
auto roomBelow = floor->GetRoomNumberBelow(Vector3i(x, y, z));
|
||||||
while (roomBelow)
|
while (roomBelow)
|
||||||
{
|
{
|
||||||
floor = &GetFloorSide(*roomBelow, x, z);
|
floor = &GetFloorSide(*roomBelow, x, z);
|
||||||
roomBelow = floor->GetRoomNumberBelow(x, y, z);
|
roomBelow = floor->GetRoomNumberBelow(Vector3i(x, y, z));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::optional{floor->GetSurfaceHeight(x, y, z, true)};
|
return floor->GetSurfaceHeight(Vector3i(x, y, z), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<int> GetCeilingHeight(const ROOM_VECTOR& location, int x, int z)
|
std::optional<int> GetCeilingHeight(const ROOM_VECTOR& location, int x, int z)
|
||||||
|
@ -575,16 +584,18 @@ namespace TEN::Collision::Floordata
|
||||||
direction = -1;
|
direction = -1;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto floorHeight = floor->GetSurfaceHeight(x, y, z, true);
|
const auto floorHeight = floor->GetSurfaceHeight(Vector3i(x, y, z), true);
|
||||||
const auto ceilingHeight = floor->GetSurfaceHeight(x, y, z, false);
|
const auto ceilingHeight = floor->GetSurfaceHeight(Vector3i(x, y, z), false);
|
||||||
|
|
||||||
y = std::clamp(y, std::min(ceilingHeight, floorHeight), std::max(ceilingHeight, floorHeight));
|
y = std::clamp(y, std::min(ceilingHeight, floorHeight), std::max(ceilingHeight, floorHeight));
|
||||||
|
|
||||||
if (floor->GetInsideBridgeItemNumber(x, y, z, y == ceilingHeight, y == floorHeight) >= 0)
|
if (floor->GetInsideBridgeItemNumber(Vector3i(x, y, z), y == ceilingHeight, y == floorHeight) >= 0)
|
||||||
{
|
{
|
||||||
if (direction >= 0)
|
if (direction >= 0)
|
||||||
{
|
{
|
||||||
|
@ -605,148 +616,152 @@ namespace TEN::Collision::Floordata
|
||||||
|
|
||||||
if (direction <= 0)
|
if (direction <= 0)
|
||||||
{
|
{
|
||||||
auto roomAbove = floor->GetRoomNumberAbove(x, y, z);
|
auto roomAbove = floor->GetRoomNumberAbove(Vector3i(x, y, z));
|
||||||
while (roomAbove)
|
while (roomAbove)
|
||||||
{
|
{
|
||||||
floor = &GetFloorSide(*roomAbove, x, z);
|
floor = &GetFloorSide(*roomAbove, x, z);
|
||||||
roomAbove = floor->GetRoomNumberAbove(x, y, z);
|
roomAbove = floor->GetRoomNumberAbove(Vector3i(x, y, z));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::optional{floor->GetSurfaceHeight(x, y, z, false)};
|
return floor->GetSurfaceHeight(Vector3i(x, y, z), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<ROOM_VECTOR> GetBottomRoom(ROOM_VECTOR location, int x, int y, int z)
|
std::optional<ROOM_VECTOR> GetBottomRoom(ROOM_VECTOR location, const Vector3i& pos)
|
||||||
{
|
{
|
||||||
auto floor = &GetFloorSide(location.roomNumber, x, z, &location.roomNumber);
|
auto floor = &GetFloorSide(location.roomNumber, pos.x, pos.z, &location.roomNumber);
|
||||||
|
|
||||||
if (floor->IsWall(x, z))
|
if (floor->IsWall(pos.x, pos.z))
|
||||||
{
|
{
|
||||||
floor = &GetBottomFloor(location.roomNumber, x, z, &location.roomNumber);
|
floor = &GetBottomFloor(location.roomNumber, pos.x, pos.z, &location.roomNumber);
|
||||||
|
|
||||||
if (floor->IsWall(x, z))
|
if (floor->IsWall(pos.x, pos.z))
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
|
||||||
location.yNumber = floor->GetSurfaceHeight(x, z, false);
|
location.yNumber = floor->GetSurfaceHeight(pos.x, pos.z, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto floorHeight = floor->GetSurfaceHeight(x, location.yNumber, z, true);
|
auto floorHeight = floor->GetSurfaceHeight(Vector3i(pos.x, location.yNumber, pos.z), true);
|
||||||
auto ceilingHeight = floor->GetSurfaceHeight(x, location.yNumber, z, false);
|
auto ceilingHeight = floor->GetSurfaceHeight(Vector3i(pos.x, location.yNumber, pos.z), false);
|
||||||
|
|
||||||
location.yNumber = std::clamp(location.yNumber, std::min(ceilingHeight, floorHeight), std::max(ceilingHeight, floorHeight));
|
location.yNumber = std::clamp(location.yNumber, std::min(ceilingHeight, floorHeight), std::max(ceilingHeight, floorHeight));
|
||||||
|
|
||||||
if (floor->GetInsideBridgeItemNumber(x, location.yNumber, z, location.yNumber == ceilingHeight, location.yNumber == floorHeight) >= 0)
|
if (floor->GetInsideBridgeItemNumber(Vector3i(pos.x, location.yNumber, pos.z), location.yNumber == ceilingHeight, location.yNumber == floorHeight) >= 0)
|
||||||
{
|
{
|
||||||
const auto height = GetBottomHeight(*floor, Vector3i(x, location.yNumber, z), &location.roomNumber, &floor);
|
const auto height = GetBottomHeight(*floor, Vector3i(pos.x, location.yNumber, pos.z), &location.roomNumber, &floor);
|
||||||
if (!height)
|
if (!height)
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
|
||||||
location.yNumber = *height;
|
location.yNumber = *height;
|
||||||
}
|
}
|
||||||
|
|
||||||
floorHeight = floor->GetSurfaceHeight(x, location.yNumber, z, true);
|
floorHeight = floor->GetSurfaceHeight(Vector3i(pos.x, location.yNumber, pos.z), true);
|
||||||
ceilingHeight = floor->GetSurfaceHeight(x, location.yNumber, z, false);
|
ceilingHeight = floor->GetSurfaceHeight(Vector3i(pos.x, location.yNumber, pos.z), false);
|
||||||
|
|
||||||
if (y < ceilingHeight && floor->GetRoomNumberAbove(x, location.yNumber, z))
|
if (pos.y < ceilingHeight && floor->GetRoomNumberAbove(Vector3i(pos.x, location.yNumber, pos.z)))
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
if (y <= floorHeight)
|
|
||||||
|
if (pos.y <= floorHeight)
|
||||||
{
|
{
|
||||||
location.yNumber = std::max(y, ceilingHeight);
|
location.yNumber = std::max(pos.y, ceilingHeight);
|
||||||
return std::optional{location};
|
return location;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto roomBelow = floor->GetRoomNumberBelow(x, location.yNumber, z);
|
auto roomBelow = floor->GetRoomNumberBelow(Vector3i(pos.x, location.yNumber, pos.z));
|
||||||
while (roomBelow)
|
while (roomBelow)
|
||||||
{
|
{
|
||||||
floor = &GetFloorSide(*roomBelow, x, z, &location.roomNumber);
|
floor = &GetFloorSide(*roomBelow, pos.x, pos.z, &location.roomNumber);
|
||||||
location.yNumber = floor->GetSurfaceHeight(x, z, false);
|
location.yNumber = floor->GetSurfaceHeight(pos.x, pos.z, false);
|
||||||
|
|
||||||
floorHeight = floor->GetSurfaceHeight(x, location.yNumber, z, true);
|
floorHeight = floor->GetSurfaceHeight(Vector3i(pos.x, location.yNumber, pos.z), true);
|
||||||
ceilingHeight = floor->GetSurfaceHeight(x, location.yNumber, z, false);
|
ceilingHeight = floor->GetSurfaceHeight(Vector3i(pos.x, location.yNumber, pos.z), false);
|
||||||
|
|
||||||
if (y < ceilingHeight && floor->GetRoomNumberAbove(x, location.yNumber, z))
|
if (pos.y < ceilingHeight && floor->GetRoomNumberAbove(Vector3i(pos.x, location.yNumber, pos.z)))
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
if (y <= floorHeight)
|
|
||||||
|
if (pos.y <= floorHeight)
|
||||||
{
|
{
|
||||||
location.yNumber = std::max(y, ceilingHeight);
|
location.yNumber = std::max(pos.y, ceilingHeight);
|
||||||
return std::optional{location};
|
return location;
|
||||||
}
|
}
|
||||||
|
|
||||||
roomBelow = floor->GetRoomNumberBelow(x, location.yNumber, z);
|
roomBelow = floor->GetRoomNumberBelow(Vector3i(pos.x, location.yNumber, pos.z));
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<ROOM_VECTOR> GetTopRoom(ROOM_VECTOR location, int x, int y, int z)
|
std::optional<ROOM_VECTOR> GetTopRoom(ROOM_VECTOR location, const Vector3i& pos)
|
||||||
{
|
{
|
||||||
auto floor = &GetFloorSide(location.roomNumber, x, z, &location.roomNumber);
|
auto floor = &GetFloorSide(location.roomNumber, pos.x, pos.z, &location.roomNumber);
|
||||||
|
|
||||||
if (floor->IsWall(x, z))
|
if (floor->IsWall(pos.x, pos.z))
|
||||||
{
|
{
|
||||||
floor = &GetTopFloor(location.roomNumber, x, z, &location.roomNumber);
|
floor = &GetTopFloor(location.roomNumber, pos.x, pos.z, &location.roomNumber);
|
||||||
|
|
||||||
if (floor->IsWall(x, z))
|
if (floor->IsWall(pos.x, pos.z))
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
|
||||||
location.yNumber = floor->GetSurfaceHeight(x, z, true);
|
location.yNumber = floor->GetSurfaceHeight(pos.x, pos.z, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto floorHeight = floor->GetSurfaceHeight(x, location.yNumber, z, true);
|
auto floorHeight = floor->GetSurfaceHeight(Vector3i(pos.x, location.yNumber, pos.z), true);
|
||||||
auto ceilingHeight = floor->GetSurfaceHeight(x, location.yNumber, z, false);
|
auto ceilingHeight = floor->GetSurfaceHeight(Vector3i(pos.x, location.yNumber, pos.z), false);
|
||||||
|
|
||||||
location.yNumber = std::clamp(location.yNumber, std::min(ceilingHeight, floorHeight), std::max(ceilingHeight, floorHeight));
|
location.yNumber = std::clamp(location.yNumber, std::min(ceilingHeight, floorHeight), std::max(ceilingHeight, floorHeight));
|
||||||
|
|
||||||
if (floor->GetInsideBridgeItemNumber(x, location.yNumber, z, location.yNumber == ceilingHeight, location.yNumber == floorHeight) >= 0)
|
if (floor->GetInsideBridgeItemNumber(Vector3i(pos.x, location.yNumber, pos.z), location.yNumber == ceilingHeight, location.yNumber == floorHeight) >= 0)
|
||||||
{
|
{
|
||||||
const auto height = GetTopHeight(*floor, Vector3i(x, location.yNumber, z), &location.roomNumber, &floor);
|
const auto height = GetTopHeight(*floor, Vector3i(pos.x, location.yNumber, pos.z), &location.roomNumber, &floor);
|
||||||
if (!height)
|
if (!height)
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
|
||||||
location.yNumber = *height;
|
location.yNumber = *height;
|
||||||
}
|
}
|
||||||
|
|
||||||
floorHeight = floor->GetSurfaceHeight(x, location.yNumber, z, true);
|
floorHeight = floor->GetSurfaceHeight(Vector3i(pos.x, location.yNumber, pos.z), true);
|
||||||
ceilingHeight = floor->GetSurfaceHeight(x, location.yNumber, z, false);
|
ceilingHeight = floor->GetSurfaceHeight(Vector3i(pos.x, location.yNumber, pos.z), false);
|
||||||
|
|
||||||
if (y > floorHeight && floor->GetRoomNumberBelow(x, location.yNumber, z))
|
if (pos.y > floorHeight && floor->GetRoomNumberBelow(Vector3i(pos.x, location.yNumber, pos.z)))
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
if (y >= ceilingHeight)
|
|
||||||
|
if (pos.y >= ceilingHeight)
|
||||||
{
|
{
|
||||||
location.yNumber = std::min(y, floorHeight);
|
location.yNumber = std::min(pos.y, floorHeight);
|
||||||
return std::optional{location};
|
return location;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto roomAbove = floor->GetRoomNumberAbove(x, location.yNumber, z);
|
auto roomAbove = floor->GetRoomNumberAbove(Vector3i(pos.x, location.yNumber, pos.z));
|
||||||
while (roomAbove)
|
while (roomAbove)
|
||||||
{
|
{
|
||||||
floor = &GetFloorSide(*roomAbove, x, z, &location.roomNumber);
|
floor = &GetFloorSide(*roomAbove, pos.x, pos.z, &location.roomNumber);
|
||||||
location.yNumber = floor->GetSurfaceHeight(x, z, true);
|
location.yNumber = floor->GetSurfaceHeight(pos.x, pos.z, true);
|
||||||
|
|
||||||
floorHeight = floor->GetSurfaceHeight(x, location.yNumber, z, true);
|
floorHeight = floor->GetSurfaceHeight(Vector3i(pos.x, location.yNumber, pos.z), true);
|
||||||
ceilingHeight = floor->GetSurfaceHeight(x, location.yNumber, z, false);
|
ceilingHeight = floor->GetSurfaceHeight(Vector3i(pos.x, location.yNumber, pos.z), false);
|
||||||
|
|
||||||
if (y > floorHeight && floor->GetRoomNumberBelow(x, location.yNumber, z))
|
if (pos.y > floorHeight && floor->GetRoomNumberBelow(Vector3i(pos.x, location.yNumber, pos.z)))
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
if (y >= ceilingHeight)
|
|
||||||
|
if (pos.y >= ceilingHeight)
|
||||||
{
|
{
|
||||||
location.yNumber = std::min(y, floorHeight);
|
location.yNumber = std::min(pos.y, floorHeight);
|
||||||
return std::optional{location};
|
return location;
|
||||||
}
|
}
|
||||||
|
|
||||||
roomAbove = floor->GetRoomNumberAbove(x, location.yNumber, z);
|
roomAbove = floor->GetRoomNumberAbove(Vector3i(pos.x, location.yNumber, pos.z));
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
ROOM_VECTOR GetRoom(ROOM_VECTOR location, int x, int y, int z)
|
ROOM_VECTOR GetRoom(ROOM_VECTOR location, const Vector3i& pos)
|
||||||
{
|
{
|
||||||
const auto locationBelow = GetBottomRoom(location, x, y, z);
|
const auto locationBelow = GetBottomRoom(location, pos);
|
||||||
if (locationBelow)
|
if (locationBelow)
|
||||||
return *locationBelow;
|
return *locationBelow;
|
||||||
|
|
||||||
const auto locationAbove = GetTopRoom(location, x, y, z);
|
const auto locationAbove = GetTopRoom(location, pos);
|
||||||
if (locationAbove)
|
if (locationAbove)
|
||||||
return *locationAbove;
|
return *locationAbove;
|
||||||
|
|
||||||
|
@ -841,7 +856,7 @@ namespace TEN::Collision::Floordata
|
||||||
// Animated objects are also supported, although horizontal collision shifting is unstable.
|
// Animated objects are also supported, although horizontal collision shifting is unstable.
|
||||||
// Method: get accurate bounds in world transform by converting to OBB, then do a ray test
|
// Method: get accurate bounds in world transform by converting to OBB, then do a ray test
|
||||||
// on top or bottom (depending on test side) to determine if box is present at a particular point.
|
// on top or bottom (depending on test side) to determine if box is present at a particular point.
|
||||||
std::optional<int> GetBridgeItemIntersect(int itemNumber, int x, int y, int z, bool useBottomHeight)
|
std::optional<int> GetBridgeItemIntersect(const Vector3i& pos, int itemNumber, bool useBottomHeight)
|
||||||
{
|
{
|
||||||
auto& item = g_Level.Items[itemNumber];
|
auto& item = g_Level.Items[itemNumber];
|
||||||
|
|
||||||
|
@ -849,10 +864,11 @@ namespace TEN::Collision::Floordata
|
||||||
auto dxBounds = bounds.ToBoundingOrientedBox(item.Pose);
|
auto dxBounds = bounds.ToBoundingOrientedBox(item.Pose);
|
||||||
|
|
||||||
// Introduce slight vertical margin just in case.
|
// Introduce slight vertical margin just in case.
|
||||||
auto pos = Vector3(x, y + (useBottomHeight ? 4 : -4), z);
|
auto origin = Vector3(pos.x, pos.y + (useBottomHeight ? 4 : -4), pos.z);
|
||||||
|
auto dir = useBottomHeight ? -Vector3::UnitY : Vector3::UnitY;
|
||||||
|
|
||||||
float dist = 0.0f;
|
float dist = 0.0f;
|
||||||
if (dxBounds.Intersects(pos, (useBottomHeight ? -Vector3::UnitY : Vector3::UnitY), dist))
|
if (dxBounds.Intersects(origin, dir, dist))
|
||||||
return (item.Pose.Position.y + (useBottomHeight ? bounds.Y2 : bounds.Y1));
|
return (item.Pose.Position.y + (useBottomHeight ? bounds.Y2 : bounds.Y1));
|
||||||
|
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
@ -928,15 +944,9 @@ namespace TEN::Collision::Floordata
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TestMaterial(MaterialType refMaterial, const std::vector<MaterialType>& materialList)
|
bool TestMaterial(MaterialType refMaterial, const std::vector<MaterialType>& materials)
|
||||||
{
|
{
|
||||||
for (const auto& material : materialList)
|
return Contains(materials, refMaterial);
|
||||||
{
|
|
||||||
if (material == refMaterial)
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void DrawSectorFlagLabel(const Vector3& pos, const std::string& string, const Vector4& color, float verticalOffset)
|
static void DrawSectorFlagLabel(const Vector3& pos, const std::string& string, const Vector4& color, float verticalOffset)
|
||||||
|
@ -968,7 +978,7 @@ namespace TEN::Collision::Floordata
|
||||||
constexpr auto ACTIVATOR_MINECART_LEFT_COLOR = Vector4(1.0f, 0.4f, 1.0f, 1.0f);
|
constexpr auto ACTIVATOR_MINECART_LEFT_COLOR = Vector4(1.0f, 0.4f, 1.0f, 1.0f);
|
||||||
constexpr auto MINECART_STOP_COLOR = Vector4(0.4f, 1.0f, 1.0f, 1.0f);
|
constexpr auto MINECART_STOP_COLOR = Vector4(0.4f, 1.0f, 1.0f, 1.0f);
|
||||||
|
|
||||||
// Only check sectors in player vicinity.
|
// Only check sectors in player's vicinity.
|
||||||
const auto& room = g_Level.Rooms[item.RoomNumber];
|
const auto& room = g_Level.Rooms[item.RoomNumber];
|
||||||
int minX = std::max(item.Pose.Position.x - DRAW_RANGE, room.x) / BLOCK(1);
|
int minX = std::max(item.Pose.Position.x - DRAW_RANGE, room.x) / BLOCK(1);
|
||||||
int maxX = std::min(item.Pose.Position.x + DRAW_RANGE, room.x + (room.xSize * BLOCK(1))) / BLOCK(1);
|
int maxX = std::min(item.Pose.Position.x + DRAW_RANGE, room.x + (room.xSize * BLOCK(1))) / BLOCK(1);
|
||||||
|
|
|
@ -4,20 +4,24 @@
|
||||||
|
|
||||||
using namespace TEN::Math;
|
using namespace TEN::Math;
|
||||||
|
|
||||||
// GLOSSARY OF TERMS:
|
// Ceiling: Upper surface of a sector.
|
||||||
// Block: Collision unit describing a room division within the grid.
|
// Floor: Lower surface of a sector.
|
||||||
// Ceiling: Upper surface of a collision block.
|
// Floordata: Name of the engine's level geometry collision system composed of rooms with sectors.
|
||||||
// Floor: Lower surface of a collision block.
|
// Plane: Mathematical representation of one of two surface triangles.
|
||||||
// Floordata: Name of the engine's level geometry collision system consisting of rooms and their divisions within a grid.
|
// Portal: Link from one room to another allowing traversal between them.
|
||||||
// Plane: One of two surface triangles.
|
// Room number: Unique ID of a room.
|
||||||
// Portal: Link from one room to another allowing traversal.
|
// Room position: Relative grid coordinates of room sectors.
|
||||||
// Room number: Unique numeric index of a room.
|
// Sector/block: Collision data describing a single grid division within a room.
|
||||||
// Surface: Floor or ceiling.
|
// Surface: Floor or ceiling consisting of two triangles.
|
||||||
// Wall: Inferred from a floor or ceiling with max height. Note that true "walls" do not exist.
|
// Triangle: Surface subdivision.
|
||||||
|
// Wall: Inferred from a floor or ceiling with max height. Note that true "walls" don't exist in floordata.
|
||||||
// Planes are non-standard. Instead of 4 components storing a normal and a distance, floordata uses a Vector3:
|
//
|
||||||
// x and y store the 2D direction vector in the xz plane (not normalized).
|
// The way floordata "planes" are stored is non-standard.
|
||||||
// z stores the distance.
|
// Instead of a Plane object with a normal + distance,
|
||||||
|
// they use a Vector3 object with data laid out as follows:
|
||||||
|
// x: X tilt grade (0.25f = 1/4 block).
|
||||||
|
// y: Z tilt grade (0.25f = 1/4 block).
|
||||||
|
// z: Plane's absolute height at the sector's center (i.e. distance in regular plane terms).
|
||||||
|
|
||||||
constexpr auto WALL_PLANE = Vector3(0, 0, -CLICK(127));
|
constexpr auto WALL_PLANE = Vector3(0, 0, -CLICK(127));
|
||||||
|
|
||||||
|
@ -132,15 +136,15 @@ class FloorInfo
|
||||||
|
|
||||||
std::optional<int> GetRoomNumberAbove(int planeIndex) const;
|
std::optional<int> GetRoomNumberAbove(int planeIndex) const;
|
||||||
std::optional<int> GetRoomNumberAbove(int x, int z) const;
|
std::optional<int> GetRoomNumberAbove(int x, int z) const;
|
||||||
std::optional<int> GetRoomNumberAbove(int x, int y, int z) const;
|
std::optional<int> GetRoomNumberAbove(const Vector3i& pos) const;
|
||||||
std::optional<int> GetRoomNumberBelow(int planeIndex) const;
|
std::optional<int> GetRoomNumberBelow(int planeIndex) const;
|
||||||
std::optional<int> GetRoomNumberBelow(int x, int z) const;
|
std::optional<int> GetRoomNumberBelow(int x, int z) const;
|
||||||
std::optional<int> GetRoomNumberBelow(int x, int y, int z) const;
|
std::optional<int> GetRoomNumberBelow(const Vector3i& pos) const;
|
||||||
std::optional<int> GetRoomNumberAtSide() const; // Through wall?
|
std::optional<int> GetRoomNumberAtSide() const;
|
||||||
|
|
||||||
int GetSurfaceHeight(int x, int z, bool isFloor) const;
|
int GetSurfaceHeight(int x, int z, bool isFloor) const;
|
||||||
int GetSurfaceHeight(int x, int y, int z, bool isFloor) const;
|
int GetSurfaceHeight(const Vector3i& pos, bool isFloor) const;
|
||||||
int GetBridgeSurfaceHeight(int x, int y, int z, bool isFloor) const;
|
int GetBridgeSurfaceHeight(const Vector3i& pos, bool isFloor) const;
|
||||||
|
|
||||||
Vector2 GetSurfaceSlope(int planeIndex, bool isFloor) const;
|
Vector2 GetSurfaceSlope(int planeIndex, bool isFloor) const;
|
||||||
Vector2 GetSurfaceSlope(int x, int z, bool isFloor) const;
|
Vector2 GetSurfaceSlope(int x, int z, bool isFloor) const;
|
||||||
|
@ -153,7 +157,7 @@ class FloorInfo
|
||||||
bool IsWall(int x, int z) const;
|
bool IsWall(int x, int z) const;
|
||||||
|
|
||||||
// Bridge methods
|
// Bridge methods
|
||||||
int GetInsideBridgeItemNumber(int x, int y, int z, bool floorBorder, bool ceilingBorder) const;
|
int GetInsideBridgeItemNumber(const Vector3i& pos, bool floorBorder, bool ceilingBorder) const;
|
||||||
void AddBridge(int itemNumber);
|
void AddBridge(int itemNumber);
|
||||||
void RemoveBridge(int itemNumber);
|
void RemoveBridge(int itemNumber);
|
||||||
};
|
};
|
||||||
|
@ -166,7 +170,7 @@ namespace TEN::Collision::Floordata
|
||||||
Vector2i GetSectorPoint(int x, int z);
|
Vector2i GetSectorPoint(int x, int z);
|
||||||
Vector2i GetRoomPosition(int roomNumber, int x, int z);
|
Vector2i GetRoomPosition(int roomNumber, int x, int z);
|
||||||
|
|
||||||
FloorInfo& GetFloor(int roomNumber, const Vector2i& pos);
|
FloorInfo& GetFloor(int roomNumber, const Vector2i& roomPos);
|
||||||
FloorInfo& GetFloor(int roomNumber, int x, int z);
|
FloorInfo& GetFloor(int roomNumber, int x, int z);
|
||||||
FloorInfo& GetFloorSide(int roomNumber, int x, int z, int* sideRoomNumber = nullptr);
|
FloorInfo& GetFloorSide(int roomNumber, int x, int z, int* sideRoomNumber = nullptr);
|
||||||
FloorInfo& GetBottomFloor(int roomNumber, int x, int z, int* bottomRoomNumber = nullptr);
|
FloorInfo& GetBottomFloor(int roomNumber, int x, int z, int* bottomRoomNumber = nullptr);
|
||||||
|
@ -177,18 +181,18 @@ namespace TEN::Collision::Floordata
|
||||||
std::optional<int> GetFloorHeight(const ROOM_VECTOR& location, int x, int z);
|
std::optional<int> GetFloorHeight(const ROOM_VECTOR& location, int x, int z);
|
||||||
std::optional<int> GetCeilingHeight(const ROOM_VECTOR& location, int x, int z);
|
std::optional<int> GetCeilingHeight(const ROOM_VECTOR& location, int x, int z);
|
||||||
|
|
||||||
std::optional<ROOM_VECTOR> GetBottomRoom(ROOM_VECTOR location, int x, int y, int z);
|
std::optional<ROOM_VECTOR> GetBottomRoom(ROOM_VECTOR location, const Vector3i& pos);
|
||||||
std::optional<ROOM_VECTOR> GetTopRoom(ROOM_VECTOR location, int x, int y, int z);
|
std::optional<ROOM_VECTOR> GetTopRoom(ROOM_VECTOR location, const Vector3i& pos);
|
||||||
ROOM_VECTOR GetRoom(ROOM_VECTOR location, int x, int y, int z);
|
ROOM_VECTOR GetRoom(ROOM_VECTOR location, const Vector3i& pos);
|
||||||
|
|
||||||
void AddBridge(int itemNumber, int x = 0, int z = 0);
|
void AddBridge(int itemNumber, int x = 0, int z = 0);
|
||||||
void RemoveBridge(int itemNumber, int x = 0, int z = 0);
|
void RemoveBridge(int itemNumber, int x = 0, int z = 0);
|
||||||
|
|
||||||
std::optional<int> GetBridgeItemIntersect(int itemNumber, int x, int y, int z, bool useBottomHeight);
|
std::optional<int> GetBridgeItemIntersect(const Vector3i& pos, int itemNumber, bool useBottomHeight);
|
||||||
int GetBridgeBorder(int itemNumber, bool bottom);
|
int GetBridgeBorder(int itemNumber, bool bottom);
|
||||||
void UpdateBridgeItem(int itemNumber, bool forceRemoval = false);
|
void UpdateBridgeItem(int itemNumber, bool forceRemoval = false);
|
||||||
|
|
||||||
bool TestMaterial(MaterialType refMaterial, const std::vector<MaterialType>& materialList);
|
bool TestMaterial(MaterialType refMaterial, const std::vector<MaterialType>& materials);
|
||||||
|
|
||||||
void DrawNearbySectorFlags(const ItemInfo& item);
|
void DrawNearbySectorFlags(const ItemInfo& item);
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
#include "Game/Setup.h"
|
#include "Game/Setup.h"
|
||||||
#include "Math/Math.h"
|
#include "Math/Math.h"
|
||||||
#include "Objects/objectslist.h"
|
#include "Objects/objectslist.h"
|
||||||
#include "Objects/TR5/Object/tr5_pushableblock.h"
|
#include "Objects/Generic/Object/Pushable/PushableObject.h"
|
||||||
#include "Renderer/Renderer11.h"
|
#include "Renderer/Renderer11.h"
|
||||||
|
|
||||||
using namespace TEN::Effects::Smoke;
|
using namespace TEN::Effects::Smoke;
|
||||||
|
@ -2134,9 +2134,6 @@ void InitializeItemBoxData()
|
||||||
for (int i = 0; i < g_Level.Items.size(); i++)
|
for (int i = 0; i < g_Level.Items.size(); i++)
|
||||||
{
|
{
|
||||||
auto* currentItem = &g_Level.Items[i];
|
auto* currentItem = &g_Level.Items[i];
|
||||||
|
|
||||||
if (currentItem->Active && currentItem->Data.is<PushableInfo>())
|
|
||||||
ClearMovableBlockSplitters(currentItem->Pose.Position, currentItem->RoomNumber);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto& room : g_Level.Rooms)
|
for (auto& room : g_Level.Rooms)
|
||||||
|
|
|
@ -21,7 +21,6 @@
|
||||||
#include "Objects/TR4/Entity/tr4_beetle_swarm.h"
|
#include "Objects/TR4/Entity/tr4_beetle_swarm.h"
|
||||||
#include "Objects/TR5/Emitter/tr5_spider_emitter.h"
|
#include "Objects/TR5/Emitter/tr5_spider_emitter.h"
|
||||||
#include "Objects/TR5/Emitter/tr5_rats_emitter.h"
|
#include "Objects/TR5/Emitter/tr5_rats_emitter.h"
|
||||||
#include "Objects/TR5/Object/tr5_pushableblock.h"
|
|
||||||
#include "Objects/Effects/tr4_locusts.h"
|
#include "Objects/Effects/tr4_locusts.h"
|
||||||
|
|
||||||
using namespace TEN::Effects::Environment;
|
using namespace TEN::Effects::Environment;
|
||||||
|
|
|
@ -116,7 +116,7 @@ bool GetTargetOnLOS(GameVector* origin, GameVector* target, bool drawTarget, boo
|
||||||
{
|
{
|
||||||
if (itemNumber < 0)
|
if (itemNumber < 0)
|
||||||
{
|
{
|
||||||
if (StaticObjects[mesh->staticNumber].shatterType != SHT_NONE)
|
if (StaticObjects[mesh->staticNumber].shatterType != ShatterType::None)
|
||||||
{
|
{
|
||||||
ShatterImpactData.impactDirection = directionNorm;
|
ShatterImpactData.impactDirection = directionNorm;
|
||||||
ShatterImpactData.impactLocation = Vector3(mesh->pos.Position.x, mesh->pos.Position.y, mesh->pos.Position.z);
|
ShatterImpactData.impactLocation = Vector3(mesh->pos.Position.x, mesh->pos.Position.y, mesh->pos.Position.z);
|
||||||
|
|
|
@ -41,22 +41,22 @@ namespace TEN::Effects::Streamer
|
||||||
// Apply expansion.
|
// Apply expansion.
|
||||||
if (scaleRate != 0.0f)
|
if (scaleRate != 0.0f)
|
||||||
{
|
{
|
||||||
auto direction = Orientation.ToDirection();
|
auto dir = Orientation.ToDirection();
|
||||||
Vertices[0] = Geometry::TranslatePoint(Vertices[0], -direction, scaleRate);
|
Vertices[0] = Geometry::TranslatePoint(Vertices[0], -dir, scaleRate);
|
||||||
Vertices[1] = Geometry::TranslatePoint(Vertices[1], direction, scaleRate);
|
Vertices[1] = Geometry::TranslatePoint(Vertices[1], dir, scaleRate);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply directional velocity.
|
// Apply directional velocity.
|
||||||
if (vel != 0.0f)
|
if (vel != 0.0f)
|
||||||
{
|
{
|
||||||
auto direction = Orientation.GetAxis();
|
auto dir = Orientation.GetAxis();
|
||||||
Vertices[0] = Geometry::TranslatePoint(Vertices[0], direction, vel);
|
Vertices[0] = Geometry::TranslatePoint(Vertices[0], dir, vel);
|
||||||
Vertices[1] = Geometry::TranslatePoint(Vertices[1], direction, vel);
|
Vertices[1] = Geometry::TranslatePoint(Vertices[1], dir, vel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Streamer::AddSegment(const Vector3& pos, const Vector3& direction, short orient2D, const Vector4& color,
|
void Streamer::AddSegment(const Vector3& pos, const Vector3& dir, short orient, const Vector4& color,
|
||||||
float width, float life, float vel, float scaleRate, short rot2D, int flags, unsigned int segmentCount)
|
float width, float life, float vel, float scaleRate, short rot, int flags, unsigned int segmentCount)
|
||||||
{
|
{
|
||||||
constexpr auto FADE_IN_COEFF = 3.0f;
|
constexpr auto FADE_IN_COEFF = 3.0f;
|
||||||
|
|
||||||
|
@ -68,14 +68,14 @@ namespace TEN::Effects::Streamer
|
||||||
float alpha = (segmentCount / lifeMax) * FADE_IN_COEFF;
|
float alpha = (segmentCount / lifeMax) * FADE_IN_COEFF;
|
||||||
float opacityMax = InterpolateCos(0.0f, color.w, alpha);
|
float opacityMax = InterpolateCos(0.0f, color.w, alpha);
|
||||||
|
|
||||||
segment.Orientation = AxisAngle(direction, orient2D);
|
segment.Orientation = AxisAngle(dir, orient);
|
||||||
segment.Color = Vector4(color.x, color.y, color.z, opacityMax);
|
segment.Color = Vector4(color.x, color.y, color.z, opacityMax);
|
||||||
segment.Life =
|
segment.Life =
|
||||||
segment.LifeMax = lifeMax;
|
segment.LifeMax = lifeMax;
|
||||||
segment.OpacityMax = opacityMax;
|
segment.OpacityMax = opacityMax;
|
||||||
segment.Velocity = vel;
|
segment.Velocity = vel;
|
||||||
segment.ScaleRate = scaleRate;
|
segment.ScaleRate = scaleRate;
|
||||||
segment.Rotation = rot2D;
|
segment.Rotation = rot;
|
||||||
segment.Flags = flags;
|
segment.Flags = flags;
|
||||||
segment.InitializeVertices(pos, width);
|
segment.InitializeVertices(pos, width);
|
||||||
}
|
}
|
||||||
|
@ -109,8 +109,8 @@ namespace TEN::Effects::Streamer
|
||||||
return Segments.emplace_back();
|
return Segments.emplace_back();
|
||||||
}
|
}
|
||||||
|
|
||||||
void StreamerModule::AddStreamer(int tag, const Vector3& pos, const Vector3& direction, short orient2D, const Vector4& color,
|
void StreamerModule::AddStreamer(int tag, const Vector3& pos, const Vector3& dir, short orient, const Vector4& color,
|
||||||
float width, float life, float vel, float scaleRate, short rot2D, int flags)
|
float width, float life, float vel, float scaleRate, short rot, int flags)
|
||||||
{
|
{
|
||||||
assertion(Pools.size() <= POOL_COUNT_MAX, "Streamer pool count overflow.");
|
assertion(Pools.size() <= POOL_COUNT_MAX, "Streamer pool count overflow.");
|
||||||
|
|
||||||
|
@ -120,7 +120,7 @@ namespace TEN::Effects::Streamer
|
||||||
|
|
||||||
// Get and extend streamer with new segment.
|
// Get and extend streamer with new segment.
|
||||||
auto& streamer = GetStreamer(tag);
|
auto& streamer = GetStreamer(tag);
|
||||||
streamer.AddSegment(pos, direction, orient2D, color, width, life, vel, scaleRate, rot2D, flags, (unsigned int)streamer.Segments.size());
|
streamer.AddSegment(pos, dir, orient, color, width, life, vel, scaleRate, rot, flags, (unsigned int)streamer.Segments.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
void StreamerModule::Update()
|
void StreamerModule::Update()
|
||||||
|
@ -194,18 +194,18 @@ namespace TEN::Effects::Streamer
|
||||||
pool.end());
|
pool.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
void StreamerEffectController::Spawn(int entityNumber, int tag, const Vector3& pos, const Vector3& direction, short orient2D, const Vector4& color,
|
void StreamerEffectController::Spawn(int itemNumber, int tag, const Vector3& pos, const Vector3& direction, short orient, const Vector4& color,
|
||||||
float width, float life, float vel, float scaleRate, short rot2D, int flags)
|
float width, float life, float vel, float scaleRate, short rot, int flags)
|
||||||
{
|
{
|
||||||
assertion(Modules.size() <= MODULE_COUNT_MAX, "Streamer module count overflow.");
|
assertion(Modules.size() <= MODULE_COUNT_MAX, "Streamer module count overflow.");
|
||||||
|
|
||||||
// Return early if module map is full and element with entityNumber key doesn't already exist.
|
// Return early if module map is full and element with itemNumber key doesn't already exist.
|
||||||
if (Modules.size() == MODULE_COUNT_MAX && !Modules.count(entityNumber))
|
if (Modules.size() == MODULE_COUNT_MAX && !Modules.count(itemNumber))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Get module and extend streamer within pool.
|
// Get module and extend streamer within pool.
|
||||||
auto& module = GetModule(entityNumber);
|
auto& module = GetModule(itemNumber);
|
||||||
module.AddStreamer(tag, pos, direction, orient2D, color, width, life, vel, scaleRate, rot2D, flags);
|
module.AddStreamer(tag, pos, direction, orient, color, width, life, vel, scaleRate, rot, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
void StreamerEffectController::Update()
|
void StreamerEffectController::Update()
|
||||||
|
@ -213,7 +213,7 @@ namespace TEN::Effects::Streamer
|
||||||
if (Modules.empty())
|
if (Modules.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (auto& [entityNumber, module] : Modules)
|
for (auto& [itemNumber, module] : Modules)
|
||||||
module.Update();
|
module.Update();
|
||||||
|
|
||||||
ClearInactiveModules();
|
ClearInactiveModules();
|
||||||
|
@ -224,11 +224,11 @@ namespace TEN::Effects::Streamer
|
||||||
*this = {};
|
*this = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
StreamerModule& StreamerEffectController::GetModule(int entityNumber)
|
StreamerModule& StreamerEffectController::GetModule(int itemNumber)
|
||||||
{
|
{
|
||||||
// Get module at entityNumber key.
|
// Get module at itemNumber key.
|
||||||
Modules.insert({ entityNumber, {} });
|
Modules.insert({ itemNumber, {} });
|
||||||
auto& module = Modules.at(entityNumber);
|
auto& module = Modules.at(itemNumber);
|
||||||
return module;
|
return module;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -51,8 +51,8 @@ namespace TEN::Effects::Streamer
|
||||||
std::vector<StreamerSegment> Segments = {};
|
std::vector<StreamerSegment> Segments = {};
|
||||||
|
|
||||||
// Utilities
|
// Utilities
|
||||||
void AddSegment(const Vector3& pos, const Vector3& direction, short orient2D, const Vector4& color,
|
void AddSegment(const Vector3& pos, const Vector3& dir, short orient, const Vector4& color,
|
||||||
float width, float life, float vel, float scaleRate, short rot2D, int flags, unsigned int segmentCount);
|
float width, float life, float vel, float scaleRate, short rot, int flags, unsigned int segmentCount);
|
||||||
void Update();
|
void Update();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -72,8 +72,8 @@ namespace TEN::Effects::Streamer
|
||||||
std::unordered_map<int, std::vector<Streamer>> Pools = {}; // Key = tag.
|
std::unordered_map<int, std::vector<Streamer>> Pools = {}; // Key = tag.
|
||||||
|
|
||||||
// Utilities
|
// Utilities
|
||||||
void AddStreamer(int tag, const Vector3& pos, const Vector3& direction, short orient2D, const Vector4& color,
|
void AddStreamer(int tag, const Vector3& pos, const Vector3& dir, short orient, const Vector4& color,
|
||||||
float width, float life, float vel, float scaleRate, short rot2D, int flags);
|
float width, float life, float vel, float scaleRate, short rot, int flags);
|
||||||
void Update();
|
void Update();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -92,17 +92,17 @@ namespace TEN::Effects::Streamer
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Members
|
// Members
|
||||||
std::unordered_map<int, StreamerModule> Modules = {}; // Key = entity number.
|
std::unordered_map<int, StreamerModule> Modules = {}; // Key = item number.
|
||||||
|
|
||||||
// Utilities
|
// Utilities
|
||||||
void Spawn(int entityID, int tag, const Vector3& pos, const Vector3& direction, short orient2D, const Vector4& color,
|
void Spawn(int itemNumber, int tag, const Vector3& pos, const Vector3& dir, short orient, const Vector4& color,
|
||||||
float width, float life, float vel, float scaleRate, short rot2D, int flags = 0);
|
float width, float life, float vel, float scaleRate, short rot, int flags = 0);
|
||||||
void Update();
|
void Update();
|
||||||
void Clear();
|
void Clear();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Helpers
|
// Helpers
|
||||||
StreamerModule& GetModule(int entityNumber);
|
StreamerModule& GetModule(int itemNumber);
|
||||||
void ClearInactiveModules();
|
void ClearInactiveModules();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -149,7 +149,7 @@ namespace TEN::Effects::Bubble
|
||||||
|
|
||||||
// Update room number. TODO: Should use GetCollision(), but calling it for each bubble is very inefficient.
|
// Update room number. TODO: Should use GetCollision(), but calling it for each bubble is very inefficient.
|
||||||
auto roomVector = ROOM_VECTOR{ bubble.RoomNumber, int(bubble.Position.y - bubble.Gravity) };
|
auto roomVector = ROOM_VECTOR{ bubble.RoomNumber, int(bubble.Position.y - bubble.Gravity) };
|
||||||
int roomNumber = GetRoom(roomVector, bubble.Position.x, bubble.Position.y - bubble.Gravity, bubble.Position.z).roomNumber;
|
int roomNumber = GetRoom(roomVector, Vector3i(bubble.Position.x, bubble.Position.y - bubble.Gravity, bubble.Position.z)).roomNumber;
|
||||||
int prevRoomNumber = bubble.RoomNumber;
|
int prevRoomNumber = bubble.RoomNumber;
|
||||||
bubble.RoomNumber = roomNumber;
|
bubble.RoomNumber = roomNumber;
|
||||||
|
|
||||||
|
|
|
@ -231,14 +231,14 @@ void UpdateDebris()
|
||||||
|
|
||||||
if (deb.worldPosition.y < floor->GetSurfaceHeight(deb.worldPosition.x, deb.worldPosition.z, false))
|
if (deb.worldPosition.y < floor->GetSurfaceHeight(deb.worldPosition.x, deb.worldPosition.z, false))
|
||||||
{
|
{
|
||||||
auto roomNumber = floor->GetRoomNumberAbove(deb.worldPosition.x, deb.worldPosition.y, deb.worldPosition.z).value_or(NO_ROOM);
|
auto roomNumber = floor->GetRoomNumberAbove(Vector3i(deb.worldPosition)).value_or(NO_ROOM);
|
||||||
if (roomNumber != NO_ROOM)
|
if (roomNumber != NO_ROOM)
|
||||||
deb.roomNumber = roomNumber;
|
deb.roomNumber = roomNumber;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (deb.worldPosition.y > floor->GetSurfaceHeight(deb.worldPosition.x, deb.worldPosition.z, true))
|
if (deb.worldPosition.y > floor->GetSurfaceHeight(deb.worldPosition.x, deb.worldPosition.z, true))
|
||||||
{
|
{
|
||||||
auto roomNumber = floor->GetRoomNumberBelow(deb.worldPosition.x, deb.worldPosition.y, deb.worldPosition.z).value_or(NO_ROOM);
|
auto roomNumber = floor->GetRoomNumberBelow(Vector3i(deb.worldPosition)).value_or(NO_ROOM);
|
||||||
if (roomNumber != NO_ROOM)
|
if (roomNumber != NO_ROOM)
|
||||||
{
|
{
|
||||||
deb.roomNumber = roomNumber;
|
deb.roomNumber = roomNumber;
|
||||||
|
|
|
@ -480,7 +480,7 @@ namespace TEN::Effects::Environment
|
||||||
|
|
||||||
auto coll = GetCollision(xPos, yPos, zPos, outsideRoom);
|
auto coll = GetCollision(xPos, yPos, zPos, outsideRoom);
|
||||||
|
|
||||||
if (!(coll.Position.Ceiling < yPos || coll.Block->GetRoomNumberAbove(xPos, yPos, zPos).value_or(NO_ROOM) != NO_ROOM))
|
if (!(coll.Position.Ceiling < yPos || coll.Block->GetRoomNumberAbove(Vector3i(xPos, yPos, zPos)).value_or(NO_ROOM) != NO_ROOM))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
auto part = WeatherParticle();
|
auto part = WeatherParticle();
|
||||||
|
|
|
@ -32,13 +32,17 @@ using namespace TEN::Utils;
|
||||||
|
|
||||||
namespace TEN::Gui
|
namespace TEN::Gui
|
||||||
{
|
{
|
||||||
constexpr int LINE_HEIGHT = 25;
|
constexpr int LINE_HEIGHT = 25;
|
||||||
constexpr int PHD_CENTER_X = SCREEN_SPACE_RES.x / 2;
|
constexpr int PHD_CENTER_X = SCREEN_SPACE_RES.x / 2;
|
||||||
constexpr int PHD_CENTER_Y = SCREEN_SPACE_RES.y / 2;
|
constexpr int PHD_CENTER_Y = SCREEN_SPACE_RES.y / 2;
|
||||||
constexpr int OBJLIST_SPACING = PHD_CENTER_X / 2;
|
constexpr int OBJLIST_SPACING = PHD_CENTER_X / 2;
|
||||||
|
|
||||||
constexpr auto VOLUME_MAX = 100;
|
constexpr auto VOLUME_MAX = 100;
|
||||||
constexpr auto VOLUME_STEP = VOLUME_MAX / 20;
|
constexpr auto VOLUME_STEP = VOLUME_MAX / 20;
|
||||||
|
constexpr auto MOUSE_SENSITIVITY_MAX = 35;
|
||||||
|
constexpr auto MOUSE_SENSITIVITY_MIN = 1;
|
||||||
|
constexpr auto MOUSE_SMOOTHING_MAX = 5;
|
||||||
|
constexpr auto MOUSE_SMOOTHING_MIN = 0;
|
||||||
|
|
||||||
GuiController g_Gui;
|
GuiController g_Gui;
|
||||||
|
|
||||||
|
@ -875,12 +879,14 @@ namespace TEN::Gui
|
||||||
TargetHighlighter,
|
TargetHighlighter,
|
||||||
ToggleRumble,
|
ToggleRumble,
|
||||||
ThumbstickCameraControl,
|
ThumbstickCameraControl,
|
||||||
|
MouseSensitivity,
|
||||||
|
MouseSmoothing,
|
||||||
|
|
||||||
Apply,
|
Apply,
|
||||||
Cancel
|
Cancel
|
||||||
};
|
};
|
||||||
|
|
||||||
static const int numOtherSettingsOptions = 9;
|
static const auto numOtherSettingsOptions = 11;
|
||||||
|
|
||||||
OptionCount = numOtherSettingsOptions;
|
OptionCount = numOtherSettingsOptions;
|
||||||
|
|
||||||
|
@ -961,6 +967,30 @@ namespace TEN::Gui
|
||||||
isVolumeAdjusted = true;
|
isVolumeAdjusted = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OtherSettingsOption::MouseSensitivity:
|
||||||
|
if (CurrentSettings.Configuration.MouseSensitivity > MOUSE_SENSITIVITY_MIN)
|
||||||
|
{
|
||||||
|
CurrentSettings.Configuration.MouseSensitivity -= 1;
|
||||||
|
if (CurrentSettings.Configuration.MouseSensitivity < MOUSE_SENSITIVITY_MIN)
|
||||||
|
CurrentSettings.Configuration.MouseSensitivity = MOUSE_SENSITIVITY_MIN;
|
||||||
|
|
||||||
|
SoundEffect(SFX_TR4_MENU_CHOOSE, nullptr, SoundEnvironment::Always);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OtherSettingsOption::MouseSmoothing:
|
||||||
|
if (CurrentSettings.Configuration.MouseSmoothing > MOUSE_SMOOTHING_MIN)
|
||||||
|
{
|
||||||
|
CurrentSettings.Configuration.MouseSmoothing -= 1;
|
||||||
|
if (CurrentSettings.Configuration.MouseSmoothing < MOUSE_SMOOTHING_MIN)
|
||||||
|
CurrentSettings.Configuration.MouseSmoothing = MOUSE_SMOOTHING_MIN;
|
||||||
|
|
||||||
|
SoundEffect(SFX_TR4_MENU_CHOOSE, nullptr, SoundEnvironment::Always);
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1000,6 +1030,30 @@ namespace TEN::Gui
|
||||||
isVolumeAdjusted = true;
|
isVolumeAdjusted = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OtherSettingsOption::MouseSensitivity:
|
||||||
|
if (CurrentSettings.Configuration.MouseSensitivity < MOUSE_SENSITIVITY_MAX)
|
||||||
|
{
|
||||||
|
CurrentSettings.Configuration.MouseSensitivity += 1;
|
||||||
|
if (CurrentSettings.Configuration.MouseSensitivity > MOUSE_SENSITIVITY_MAX)
|
||||||
|
CurrentSettings.Configuration.MouseSensitivity = MOUSE_SENSITIVITY_MAX;
|
||||||
|
|
||||||
|
SoundEffect(SFX_TR4_MENU_CHOOSE, nullptr, SoundEnvironment::Always);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OtherSettingsOption::MouseSmoothing:
|
||||||
|
if (CurrentSettings.Configuration.MouseSmoothing < MOUSE_SMOOTHING_MAX)
|
||||||
|
{
|
||||||
|
CurrentSettings.Configuration.MouseSmoothing += 1;
|
||||||
|
if (CurrentSettings.Configuration.MouseSmoothing > MOUSE_SMOOTHING_MAX)
|
||||||
|
CurrentSettings.Configuration.MouseSmoothing = MOUSE_SMOOTHING_MAX;
|
||||||
|
|
||||||
|
SoundEffect(SFX_TR4_MENU_CHOOSE, nullptr, SoundEnvironment::Always);
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include "Game/itemdata/door_data.h"
|
#include "Game/itemdata/door_data.h"
|
||||||
#include "Game/Lara/lara_struct.h"
|
#include "Game/Lara/lara_struct.h"
|
||||||
#include "Math/Math.h"
|
#include "Math/Math.h"
|
||||||
|
#include "Objects/Generic/Object/Pushable/PushableInfo.h"
|
||||||
#include "Objects/TR2/Vehicles/skidoo_info.h"
|
#include "Objects/TR2/Vehicles/skidoo_info.h"
|
||||||
#include "Objects/TR2/Vehicles/speedboat_info.h"
|
#include "Objects/TR2/Vehicles/speedboat_info.h"
|
||||||
#include "Objects/TR3/Vehicles/big_gun_info.h"
|
#include "Objects/TR3/Vehicles/big_gun_info.h"
|
||||||
|
@ -22,7 +23,6 @@
|
||||||
#include "Objects/TR4/Vehicles/motorbike_info.h"
|
#include "Objects/TR4/Vehicles/motorbike_info.h"
|
||||||
#include "Objects/TR5/Entity/tr5_laserhead_info.h"
|
#include "Objects/TR5/Entity/tr5_laserhead_info.h"
|
||||||
#include "Objects/TR5/Light/tr5_light_info.h"
|
#include "Objects/TR5/Light/tr5_light_info.h"
|
||||||
#include "Objects/TR5/Object/tr5_pushableblock_info.h"
|
|
||||||
|
|
||||||
template<class... Ts> struct visitor : Ts... { using Ts::operator()...; };
|
template<class... Ts> struct visitor : Ts... { using Ts::operator()...; };
|
||||||
template<class... Ts> visitor(Ts...)->visitor<Ts...>; // TODO: Line not needed in C++20.
|
template<class... Ts> visitor(Ts...)->visitor<Ts...>; // TODO: Line not needed in C++20.
|
||||||
|
|
|
@ -827,8 +827,8 @@ void DoItemHit(ItemInfo* target, int damage, bool isExplosive, bool allowBurn)
|
||||||
{
|
{
|
||||||
const auto& object = Objects[target->ObjectNumber];
|
const auto& object = Objects[target->ObjectNumber];
|
||||||
|
|
||||||
if ((object.damageType == DamageMode::AnyWeapon) ||
|
if ((object.damageType == DamageMode::Any) ||
|
||||||
(object.damageType == DamageMode::ExplosivesOnly && isExplosive))
|
(object.damageType == DamageMode::Explosion && isExplosive))
|
||||||
{
|
{
|
||||||
if (target->HitPoints > 0)
|
if (target->HitPoints > 0)
|
||||||
{
|
{
|
||||||
|
@ -871,3 +871,17 @@ void DefaultItemHit(ItemInfo& target, ItemInfo& source, std::optional<GameVector
|
||||||
|
|
||||||
DoItemHit(&target, damage, isExplosive);
|
DoItemHit(&target, damage, isExplosive);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Vector3i GetNearestSectorCenter(const Vector3i& pos)
|
||||||
|
{
|
||||||
|
constexpr int SECTOR_SIZE = 1024;
|
||||||
|
|
||||||
|
// Calculate the sector-aligned coordinates.
|
||||||
|
int x = (pos.x / SECTOR_SIZE) * SECTOR_SIZE + SECTOR_SIZE / 2;
|
||||||
|
int z = (pos.z / SECTOR_SIZE) * SECTOR_SIZE + SECTOR_SIZE / 2;
|
||||||
|
|
||||||
|
// Keep the y-coordinate unchanged.
|
||||||
|
int y = pos.y;
|
||||||
|
|
||||||
|
return Vector3i(x, y, z);
|
||||||
|
}
|
||||||
|
|
|
@ -197,3 +197,5 @@ void DoDamage(ItemInfo* item, int damage);
|
||||||
void DoItemHit(ItemInfo* target, int damage, bool isExplosive, bool allowBurn = true);
|
void DoItemHit(ItemInfo* target, int damage, bool isExplosive, bool allowBurn = true);
|
||||||
void DefaultItemHit(ItemInfo& target, ItemInfo& source, std::optional<GameVector> pos, int damage, bool isExplosive, int jointIndex);
|
void DefaultItemHit(ItemInfo& target, ItemInfo& source, std::optional<GameVector> pos, int damage, bool isExplosive, int jointIndex);
|
||||||
short SpawnItem(ItemInfo* item, GAME_OBJECT_ID objectNumber);
|
short SpawnItem(ItemInfo* item, GAME_OBJECT_ID objectNumber);
|
||||||
|
|
||||||
|
Vector3i GetNearestSectorCenter(const Vector3i& pos);
|
||||||
|
|
|
@ -1090,9 +1090,7 @@ void InitializePickup(short itemNumber)
|
||||||
{
|
{
|
||||||
// Automatically align pickups to the floor surface.
|
// Automatically align pickups to the floor surface.
|
||||||
auto pointColl = GetCollision(item);
|
auto pointColl = GetCollision(item);
|
||||||
int bridgeItemNumber = pointColl.Block->GetInsideBridgeItemNumber(
|
int bridgeItemNumber = pointColl.Block->GetInsideBridgeItemNumber(item->Pose.Position, true, true);
|
||||||
item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z,
|
|
||||||
true, true);
|
|
||||||
|
|
||||||
if (bridgeItemNumber != NO_ITEM)
|
if (bridgeItemNumber != NO_ITEM)
|
||||||
{
|
{
|
||||||
|
|
|
@ -585,6 +585,7 @@ bool SaveGame::Save(int slot)
|
||||||
flatbuffers::Offset<Save::Minecart> mineOffset;
|
flatbuffers::Offset<Save::Minecart> mineOffset;
|
||||||
flatbuffers::Offset<Save::UPV> upvOffset;
|
flatbuffers::Offset<Save::UPV> upvOffset;
|
||||||
flatbuffers::Offset<Save::Kayak> kayakOffset;
|
flatbuffers::Offset<Save::Kayak> kayakOffset;
|
||||||
|
flatbuffers::Offset<Save::Pushable> pushableOffset;
|
||||||
|
|
||||||
flatbuffers::Offset<Save::Short> shortOffset;
|
flatbuffers::Offset<Save::Short> shortOffset;
|
||||||
flatbuffers::Offset<Save::Int> intOffset;
|
flatbuffers::Offset<Save::Int> intOffset;
|
||||||
|
@ -705,6 +706,44 @@ bool SaveGame::Save(int slot)
|
||||||
kayakBuilder.add_water_height(kayak->WaterHeight);
|
kayakBuilder.add_water_height(kayak->WaterHeight);
|
||||||
kayakOffset = kayakBuilder.Finish();
|
kayakOffset = kayakBuilder.Finish();
|
||||||
}
|
}
|
||||||
|
else if (itemToSerialize.Data.is<PushableInfo>())
|
||||||
|
{
|
||||||
|
auto pushable = (PushableInfo*)itemToSerialize.Data;
|
||||||
|
|
||||||
|
Save::PushableBuilder pushableBuilder{ fbb };
|
||||||
|
|
||||||
|
pushableBuilder.add_pushable_behaviour_state((int)pushable->BehaviorState);
|
||||||
|
pushableBuilder.add_pushable_gravity(pushable->Gravity);
|
||||||
|
pushableBuilder.add_pushable_water_force(pushable->Oscillation);
|
||||||
|
|
||||||
|
pushableBuilder.add_pushable_stack_limit(pushable->Stack.Limit);
|
||||||
|
pushableBuilder.add_pushable_stack_upper(pushable->Stack.ItemNumberAbove);
|
||||||
|
pushableBuilder.add_pushable_stack_lower(pushable->Stack.ItemNumberBelow);
|
||||||
|
|
||||||
|
pushableBuilder.add_pushable_start_x(pushable->StartPos.x);
|
||||||
|
pushableBuilder.add_pushable_start_z(pushable->StartPos.z);
|
||||||
|
pushableBuilder.add_pushable_room_number(pushable->StartPos.RoomNumber);
|
||||||
|
|
||||||
|
pushableBuilder.add_pushable_collider_flag(pushable->UseBridgeCollision);
|
||||||
|
|
||||||
|
pushableBuilder.add_pushable_north_pullable(pushable->EdgeAttribs[0].IsPullable);
|
||||||
|
pushableBuilder.add_pushable_north_pushable(pushable->EdgeAttribs[0].IsPushable);
|
||||||
|
pushableBuilder.add_pushable_north_climbable(pushable->EdgeAttribs[0].IsClimbable);
|
||||||
|
|
||||||
|
pushableBuilder.add_pushable_east_pullable(pushable->EdgeAttribs[1].IsPullable);
|
||||||
|
pushableBuilder.add_pushable_east_pushable(pushable->EdgeAttribs[1].IsPushable);
|
||||||
|
pushableBuilder.add_pushable_east_climbable(pushable->EdgeAttribs[1].IsClimbable);
|
||||||
|
|
||||||
|
pushableBuilder.add_pushable_south_pullable(pushable->EdgeAttribs[2].IsPullable);
|
||||||
|
pushableBuilder.add_pushable_south_pushable(pushable->EdgeAttribs[2].IsPushable);
|
||||||
|
pushableBuilder.add_pushable_south_climbable(pushable->EdgeAttribs[2].IsClimbable);
|
||||||
|
|
||||||
|
pushableBuilder.add_pushable_west_pullable(pushable->EdgeAttribs[3].IsPullable);
|
||||||
|
pushableBuilder.add_pushable_west_pushable(pushable->EdgeAttribs[3].IsPushable);
|
||||||
|
pushableBuilder.add_pushable_west_climbable(pushable->EdgeAttribs[3].IsClimbable);
|
||||||
|
|
||||||
|
pushableOffset = pushableBuilder.Finish();
|
||||||
|
}
|
||||||
else if (itemToSerialize.Data.is<short>())
|
else if (itemToSerialize.Data.is<short>())
|
||||||
{
|
{
|
||||||
Save::ShortBuilder sb{ fbb };
|
Save::ShortBuilder sb{ fbb };
|
||||||
|
@ -787,6 +826,11 @@ bool SaveGame::Save(int slot)
|
||||||
serializedItem.add_data_type(Save::ItemData::Kayak);
|
serializedItem.add_data_type(Save::ItemData::Kayak);
|
||||||
serializedItem.add_data(kayakOffset.Union());
|
serializedItem.add_data(kayakOffset.Union());
|
||||||
}
|
}
|
||||||
|
else if (itemToSerialize.Data.is<PushableInfo>())
|
||||||
|
{
|
||||||
|
serializedItem.add_data_type(Save::ItemData::Pushable);
|
||||||
|
serializedItem.add_data(pushableOffset.Union());
|
||||||
|
}
|
||||||
else if (itemToSerialize.Data.is<short>())
|
else if (itemToSerialize.Data.is<short>())
|
||||||
{
|
{
|
||||||
serializedItem.add_data_type(Save::ItemData::Short);
|
serializedItem.add_data_type(Save::ItemData::Short);
|
||||||
|
@ -1245,7 +1289,7 @@ bool SaveGame::Save(int slot)
|
||||||
|
|
||||||
case SavedVarType::Vec3:
|
case SavedVarType::Vec3:
|
||||||
{
|
{
|
||||||
SaveVec(SavedVarType::Vec3, s, Save::vec3TableBuilder, Save::VarUnion::vec3, Save::Vector3, FromVector3i);
|
SaveVec(SavedVarType::Vec3, s, Save::vec3TableBuilder, Save::VarUnion::vec3, Save::Vector3, FromVector3);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1782,6 +1826,41 @@ bool SaveGame::Load(int slot)
|
||||||
kayak->Velocity = savedKayak->velocity();
|
kayak->Velocity = savedKayak->velocity();
|
||||||
kayak->WaterHeight = savedKayak->water_height();
|
kayak->WaterHeight = savedKayak->water_height();
|
||||||
}
|
}
|
||||||
|
else if (item->Data.is <PushableInfo>())
|
||||||
|
{
|
||||||
|
auto* pushable = (PushableInfo*)item->Data;
|
||||||
|
auto* savedPushable = (Save::Pushable*)savedItem->data();
|
||||||
|
|
||||||
|
pushable->BehaviorState = (PushableBehaviourState)savedPushable->pushable_behaviour_state();
|
||||||
|
pushable->Gravity = savedPushable->pushable_gravity();
|
||||||
|
pushable->Oscillation = savedPushable->pushable_water_force();
|
||||||
|
|
||||||
|
pushable->Stack.Limit = savedPushable->pushable_stack_limit();
|
||||||
|
pushable->Stack.ItemNumberAbove = savedPushable->pushable_stack_upper();
|
||||||
|
pushable->Stack.ItemNumberBelow = savedPushable->pushable_stack_lower();
|
||||||
|
|
||||||
|
pushable->StartPos.x = savedPushable->pushable_start_x();
|
||||||
|
pushable->StartPos.z = savedPushable->pushable_start_z();
|
||||||
|
pushable->StartPos.RoomNumber = savedPushable->pushable_room_number();
|
||||||
|
|
||||||
|
pushable->UseBridgeCollision = savedPushable->pushable_collider_flag();
|
||||||
|
|
||||||
|
pushable->EdgeAttribs[0].IsPullable = savedPushable->pushable_north_pullable();
|
||||||
|
pushable->EdgeAttribs[0].IsPushable = savedPushable->pushable_north_pushable();
|
||||||
|
pushable->EdgeAttribs[0].IsClimbable = savedPushable->pushable_north_climbable();
|
||||||
|
|
||||||
|
pushable->EdgeAttribs[1].IsPullable = savedPushable->pushable_east_pullable();
|
||||||
|
pushable->EdgeAttribs[1].IsPushable = savedPushable->pushable_east_pushable();
|
||||||
|
pushable->EdgeAttribs[1].IsClimbable = savedPushable->pushable_east_climbable();
|
||||||
|
|
||||||
|
pushable->EdgeAttribs[2].IsPullable = savedPushable->pushable_south_pullable();
|
||||||
|
pushable->EdgeAttribs[2].IsPushable = savedPushable->pushable_south_pushable();
|
||||||
|
pushable->EdgeAttribs[2].IsClimbable = savedPushable->pushable_south_climbable();
|
||||||
|
|
||||||
|
pushable->EdgeAttribs[3].IsPullable = savedPushable->pushable_west_pullable();
|
||||||
|
pushable->EdgeAttribs[3].IsPushable = savedPushable->pushable_west_pushable();
|
||||||
|
pushable->EdgeAttribs[3].IsClimbable = savedPushable->pushable_west_climbable();
|
||||||
|
}
|
||||||
else if (savedItem->data_type() == Save::ItemData::Short)
|
else if (savedItem->data_type() == Save::ItemData::Short)
|
||||||
{
|
{
|
||||||
auto* data = savedItem->data();
|
auto* data = savedItem->data();
|
||||||
|
@ -2176,7 +2255,7 @@ bool SaveGame::Load(int slot)
|
||||||
{
|
{
|
||||||
auto stored = var->u_as_vec3()->vec();
|
auto stored = var->u_as_vec3()->vec();
|
||||||
SavedVar var;
|
SavedVar var;
|
||||||
var.emplace<(int)SavedVarType::Vec3>(ToVector3i(stored));
|
var.emplace<(int)SavedVarType::Vec3>(ToVector3(stored));
|
||||||
loadedVars.push_back(var);
|
loadedVars.push_back(var);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,6 @@
|
||||||
constexpr auto BLOCK = [](auto x) { return (BLOCK_UNIT * x); };
|
constexpr auto BLOCK = [](auto x) { return (BLOCK_UNIT * x); };
|
||||||
constexpr auto CLICK = [](auto x) { return ((BLOCK(1) / 4) * x); };
|
constexpr auto CLICK = [](auto x) { return ((BLOCK(1) / 4) * x); };
|
||||||
|
|
||||||
constexpr auto STEP_SIZE = CLICK(1);
|
|
||||||
constexpr auto WALL_MASK = BLOCK(1) - 1;
|
constexpr auto WALL_MASK = BLOCK(1) - 1;
|
||||||
constexpr auto GRID_SNAP_SIZE = (int)BLOCK(1 / 8.0f);
|
constexpr auto GRID_SNAP_SIZE = (int)BLOCK(1 / 8.0f);
|
||||||
//}
|
//}
|
||||||
|
|
|
@ -25,19 +25,19 @@ namespace TEN::Math::Geometry
|
||||||
return Vector3i(TranslatePoint(point.ToVector3(), orient, relOffset.ToVector3()));
|
return Vector3i(TranslatePoint(point.ToVector3(), orient, relOffset.ToVector3()));
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector3i TranslatePoint(const Vector3i& point, const EulerAngles& orient, float distance)
|
Vector3i TranslatePoint(const Vector3i& point, const EulerAngles& orient, float dist)
|
||||||
{
|
{
|
||||||
return Vector3i(TranslatePoint(point.ToVector3(), orient, distance));
|
return Vector3i(TranslatePoint(point.ToVector3(), orient, dist));
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector3i TranslatePoint(const Vector3i& point, const AxisAngle& orient, float distance)
|
Vector3i TranslatePoint(const Vector3i& point, const AxisAngle& orient, float dist)
|
||||||
{
|
{
|
||||||
return Vector3i(TranslatePoint(point.ToVector3(), orient, distance));
|
return Vector3i(TranslatePoint(point.ToVector3(), orient, dist));
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector3i TranslatePoint(const Vector3i& point, const Vector3& direction, float distance)
|
Vector3i TranslatePoint(const Vector3i& point, const Vector3& dir, float dist)
|
||||||
{
|
{
|
||||||
return Vector3i(TranslatePoint(point.ToVector3(), direction, distance));
|
return Vector3i(TranslatePoint(point.ToVector3(), dir, dist));
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector3 TranslatePoint(const Vector3& point, short headingAngle, float forward, float down, float right)
|
Vector3 TranslatePoint(const Vector3& point, short headingAngle, float forward, float down, float right)
|
||||||
|
@ -63,29 +63,29 @@ namespace TEN::Math::Geometry
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: Roll (Z axis) of EulerAngles orientation is disregarded.
|
// NOTE: Roll (Z axis) of EulerAngles orientation is disregarded.
|
||||||
Vector3 TranslatePoint(const Vector3& point, const EulerAngles& orient, float distance)
|
Vector3 TranslatePoint(const Vector3& point, const EulerAngles& orient, float dist)
|
||||||
{
|
{
|
||||||
if (distance == 0.0f)
|
if (dist == 0.0f)
|
||||||
return point;
|
return point;
|
||||||
|
|
||||||
auto direction = orient.ToDirection();
|
auto dir = orient.ToDirection();
|
||||||
return TranslatePoint(point, direction, distance);
|
return TranslatePoint(point, dir, dist);
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector3 TranslatePoint(const Vector3& point, const AxisAngle& orient, float distance)
|
Vector3 TranslatePoint(const Vector3& point, const AxisAngle& orient, float dist)
|
||||||
{
|
{
|
||||||
auto direction = orient.ToDirection();
|
auto dir = orient.ToDirection();
|
||||||
return TranslatePoint(point, direction, distance);
|
return TranslatePoint(point, dir, dist);
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector3 TranslatePoint(const Vector3& point, const Vector3& direction, float distance)
|
Vector3 TranslatePoint(const Vector3& point, const Vector3& dir, float dist)
|
||||||
{
|
{
|
||||||
if (distance == 0.0f)
|
if (dist == 0.0f)
|
||||||
return point;
|
return point;
|
||||||
|
|
||||||
auto directionNorm = direction;
|
auto normalizedDir = dir;
|
||||||
directionNorm.Normalize();
|
normalizedDir.Normalize();
|
||||||
return (point + (directionNorm * distance));
|
return (point + (normalizedDir * dist));
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector3 RotatePoint(const Vector3& point, const EulerAngles& rot)
|
Vector3 RotatePoint(const Vector3& point, const EulerAngles& rot)
|
||||||
|
@ -136,19 +136,19 @@ namespace TEN::Math::Geometry
|
||||||
if (linePoint0 == linePoint1)
|
if (linePoint0 == linePoint1)
|
||||||
return linePoint0;
|
return linePoint0;
|
||||||
|
|
||||||
auto direction = linePoint1 - linePoint0;
|
auto dir = linePoint1 - linePoint0;
|
||||||
float distanceAlpha = direction.Dot(origin - linePoint0) / direction.Dot(direction);
|
float distAlpha = dir.Dot(origin - linePoint0) / dir.Dot(dir);
|
||||||
|
|
||||||
if (distanceAlpha < 0.0f)
|
if (distAlpha < 0.0f)
|
||||||
{
|
{
|
||||||
return linePoint0;
|
return linePoint0;
|
||||||
}
|
}
|
||||||
else if (distanceAlpha > 1.0f)
|
else if (distAlpha > 1.0f)
|
||||||
{
|
{
|
||||||
return linePoint1;
|
return linePoint1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (linePoint0 + (direction * distanceAlpha));
|
return (linePoint0 + (dir * distAlpha));
|
||||||
}
|
}
|
||||||
|
|
||||||
EulerAngles GetOrientToPoint(const Vector3& origin, const Vector3& target)
|
EulerAngles GetOrientToPoint(const Vector3& origin, const Vector3& target)
|
||||||
|
@ -159,7 +159,7 @@ namespace TEN::Math::Geometry
|
||||||
return EulerAngles(target - origin);
|
return EulerAngles(target - origin);
|
||||||
}
|
}
|
||||||
|
|
||||||
EulerAngles GetRelOrientToNormal(short orient2D, const Vector3& normal, const Vector3& gravity)
|
EulerAngles GetRelOrientToNormal(short orient, const Vector3& normal, const Vector3& gravity)
|
||||||
{
|
{
|
||||||
// TODO: Consider gravity direction.
|
// TODO: Consider gravity direction.
|
||||||
|
|
||||||
|
@ -167,49 +167,50 @@ namespace TEN::Math::Geometry
|
||||||
short aspectAngle = Geometry::GetSurfaceAspectAngle(normal);
|
short aspectAngle = Geometry::GetSurfaceAspectAngle(normal);
|
||||||
short slopeAngle = Geometry::GetSurfaceSlopeAngle(normal);
|
short slopeAngle = Geometry::GetSurfaceSlopeAngle(normal);
|
||||||
|
|
||||||
short deltaAngle = Geometry::GetShortestAngle(orient2D, aspectAngle);
|
short deltaAngle = Geometry::GetShortestAngle(orient, aspectAngle);
|
||||||
float sinDeltaAngle = phd_sin(deltaAngle);
|
float sinDeltaAngle = phd_sin(deltaAngle);
|
||||||
float cosDeltaAngle = phd_cos(deltaAngle);
|
float cosDeltaAngle = phd_cos(deltaAngle);
|
||||||
|
|
||||||
// Calculate relative orientation adhering to normal.
|
// Calculate relative orientation adhering to normal.
|
||||||
return EulerAngles(
|
return EulerAngles(
|
||||||
-slopeAngle * cosDeltaAngle,
|
-slopeAngle * cosDeltaAngle,
|
||||||
orient2D,
|
orient,
|
||||||
slopeAngle * sinDeltaAngle);
|
slopeAngle * sinDeltaAngle);
|
||||||
}
|
}
|
||||||
|
|
||||||
Quaternion ConvertDirectionToQuat(const Vector3& direction)
|
Quaternion ConvertDirectionToQuat(const Vector3& dir)
|
||||||
{
|
{
|
||||||
constexpr auto SINGULARITY_THRESHOLD = 1.0f - EPSILON;
|
constexpr auto SINGULARITY_THRESHOLD = 1.0f - EPSILON;
|
||||||
|
|
||||||
static const auto REF_DIRECTION = Vector3::UnitZ;
|
static const auto REF_DIR = Vector3::UnitZ;
|
||||||
|
|
||||||
// If vectors are nearly opposite, return orientation 180 degrees around arbitrary axis.
|
// Vectors are nearly opposite; return orientation 180 degrees around arbitrary axis.
|
||||||
float dot = REF_DIRECTION.Dot(direction);
|
float dot = REF_DIR.Dot(dir);
|
||||||
if (dot < -SINGULARITY_THRESHOLD)
|
if (dot < -SINGULARITY_THRESHOLD)
|
||||||
{
|
{
|
||||||
auto axis = Vector3::UnitX.Cross(REF_DIRECTION);
|
auto axis = Vector3::UnitX.Cross(REF_DIR);
|
||||||
if (axis.LengthSquared() < EPSILON)
|
if (axis.LengthSquared() < EPSILON)
|
||||||
axis = Vector3::UnitY.Cross(REF_DIRECTION);
|
axis = Vector3::UnitY.Cross(REF_DIR);
|
||||||
axis.Normalize();
|
axis.Normalize();
|
||||||
|
|
||||||
auto axisAngle = AxisAngle(axis, FROM_RAD(PI));
|
auto axisAngle = AxisAngle(axis, FROM_RAD(PI));
|
||||||
return axisAngle.ToQuaternion();
|
return axisAngle.ToQuaternion();
|
||||||
}
|
}
|
||||||
|
|
||||||
// If vectors are nearly identical, return identity quaternion.
|
// Vectors are nearly identical; return identity quaternion.
|
||||||
if (dot > SINGULARITY_THRESHOLD)
|
if (dot > SINGULARITY_THRESHOLD)
|
||||||
return Quaternion::Identity;
|
return Quaternion::Identity;
|
||||||
|
|
||||||
// Calculate axis-angle and return converted quaternion.
|
// Calculate axis-angle and return converted quaternion.
|
||||||
auto axisAngle = AxisAngle(REF_DIRECTION.Cross(direction), FROM_RAD(acos(dot)));
|
auto axisAngle = AxisAngle(REF_DIR.Cross(dir), FROM_RAD(acos(dot)));
|
||||||
return axisAngle.ToQuaternion();
|
return axisAngle.ToQuaternion();
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector3 ConvertQuatToDirection(const Quaternion& quat)
|
Vector3 ConvertQuatToDirection(const Quaternion& quat)
|
||||||
{
|
{
|
||||||
static const auto refDirection = Vector3::UnitZ;
|
static const auto REF_DIR = Vector3::UnitZ;
|
||||||
return Vector3::Transform(refDirection, quat);
|
|
||||||
|
return Vector3::Transform(REF_DIR, quat);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsPointInFront(const Pose& pose, const Vector3& target)
|
bool IsPointInFront(const Pose& pose, const Vector3& target)
|
||||||
|
@ -225,11 +226,11 @@ namespace TEN::Math::Geometry
|
||||||
float sinY = phd_sin(orient.y);
|
float sinY = phd_sin(orient.y);
|
||||||
float cosY = phd_cos(orient.y);
|
float cosY = phd_cos(orient.y);
|
||||||
|
|
||||||
// The 2D heading direction vector: X = +sinY, Y = 0, Z = +cosY
|
// 2D heading direction vector: X = +sinY, Y = 0, Z = +cosY
|
||||||
auto headingDirection = Vector3(sinY, 0.0f, cosY);
|
auto headingDir = Vector3(sinY, 0.0f, cosY);
|
||||||
auto targetDirection = target - origin;
|
auto targetDir = target - origin;
|
||||||
|
|
||||||
float dot = headingDirection.Dot(targetDirection);
|
float dot = headingDir.Dot(targetDir);
|
||||||
if (dot > 0.0f)
|
if (dot > 0.0f)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
@ -241,13 +242,13 @@ namespace TEN::Math::Geometry
|
||||||
if (origin == target)
|
if (origin == target)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
auto refDirection = refPoint - origin;
|
auto refDir = refPoint - origin;
|
||||||
|
|
||||||
// The 2D heading direction vector to the 3D reference direction vector: X = +refDirection.x, Y = 0, Z = +refDirection.z
|
// 2D heading direction vector to 3D reference direction vector: X = +refDirection.x, Y = 0, Z = +refDirection.z
|
||||||
auto headingDirection = Vector3(refDirection.x, 0.0f, refDirection.z);
|
auto headingDir = Vector3(refDir.x, 0.0f, refDir.z);
|
||||||
auto targetDirection = target - origin;
|
auto targetDir = target - origin;
|
||||||
|
|
||||||
float dot = headingDirection.Dot(targetDirection);
|
float dot = headingDir.Dot(targetDir);
|
||||||
if (dot > 0.0f)
|
if (dot > 0.0f)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
@ -267,11 +268,11 @@ namespace TEN::Math::Geometry
|
||||||
float sinY = phd_sin(orient.y);
|
float sinY = phd_sin(orient.y);
|
||||||
float cosY = phd_cos(orient.y);
|
float cosY = phd_cos(orient.y);
|
||||||
|
|
||||||
// The 2D normal vector to the 2D heading direction vector: X = +cosY, Y = 0, Z = -sinY
|
// 2D normal vector to 2D heading direction vector: X = +cosY, Y = 0, Z = -sinY
|
||||||
auto headingNormal = Vector3(cosY, 0.0f, -sinY);
|
auto headingNormal = Vector3(cosY, 0.0f, -sinY);
|
||||||
auto targetDirection = target - origin;
|
auto targetDir = target - origin;
|
||||||
|
|
||||||
float dot = headingNormal.Dot(targetDirection);
|
float dot = headingNormal.Dot(targetDir);
|
||||||
if (dot > 0.0f)
|
if (dot > 0.0f)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
@ -283,13 +284,13 @@ namespace TEN::Math::Geometry
|
||||||
if (origin == target)
|
if (origin == target)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
auto refDirection = refPoint - origin;
|
auto refDir = refPoint - origin;
|
||||||
|
|
||||||
// The 2D normal vector to the 3D reference direction vector: X = +refDirection.z, Y = 0, Z = -refDirection.x
|
// 2D normal vector to 3D reference direction vector: X = +refDirection.z, Y = 0, Z = -refDirection.x
|
||||||
auto headingNormal = Vector3(refDirection.z, 0.0f, -refDirection.x);
|
auto headingNormal = Vector3(refDir.z, 0.0f, -refDir.x);
|
||||||
auto targetDirection = target - origin;
|
auto targetDir = target - origin;
|
||||||
|
|
||||||
float dot = headingNormal.Dot(targetDirection);
|
float dot = headingNormal.Dot(targetDir);
|
||||||
if (dot > 0.0f)
|
if (dot > 0.0f)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
|
|
@ -14,17 +14,17 @@ namespace TEN::Math::Geometry
|
||||||
Vector3i TranslatePoint(const Vector3i& point, short headingAngle, float forward, float down = 0.0f, float right = 0.0f);
|
Vector3i TranslatePoint(const Vector3i& point, short headingAngle, float forward, float down = 0.0f, float right = 0.0f);
|
||||||
Vector3i TranslatePoint(const Vector3i& point, short headingAngle, const Vector3i& relOffset);
|
Vector3i TranslatePoint(const Vector3i& point, short headingAngle, const Vector3i& relOffset);
|
||||||
Vector3i TranslatePoint(const Vector3i& point, const EulerAngles& orient, const Vector3i& relOffset);
|
Vector3i TranslatePoint(const Vector3i& point, const EulerAngles& orient, const Vector3i& relOffset);
|
||||||
Vector3i TranslatePoint(const Vector3i& point, const EulerAngles& orient, float distance);
|
Vector3i TranslatePoint(const Vector3i& point, const EulerAngles& orient, float dist);
|
||||||
Vector3i TranslatePoint(const Vector3i& point, const AxisAngle& orient, float distance);
|
Vector3i TranslatePoint(const Vector3i& point, const AxisAngle& orient, float dist);
|
||||||
Vector3i TranslatePoint(const Vector3i& point, const Vector3& direction, float distance);
|
Vector3i TranslatePoint(const Vector3i& point, const Vector3& dir, float dist);
|
||||||
|
|
||||||
// Float-based point translation
|
// Float-based point translation
|
||||||
Vector3 TranslatePoint(const Vector3& point, short headingAngle, float forward, float down = 0.0f, float right = 0.0f);
|
Vector3 TranslatePoint(const Vector3& point, short headingAngle, float forward, float down = 0.0f, float right = 0.0f);
|
||||||
Vector3 TranslatePoint(const Vector3& point, short headingAngle, const Vector3& relOffset);
|
Vector3 TranslatePoint(const Vector3& point, short headingAngle, const Vector3& relOffset);
|
||||||
Vector3 TranslatePoint(const Vector3& point, const EulerAngles& orient, const Vector3& relOffset);
|
Vector3 TranslatePoint(const Vector3& point, const EulerAngles& orient, const Vector3& relOffset);
|
||||||
Vector3 TranslatePoint(const Vector3& point, const EulerAngles& orient, float distance);
|
Vector3 TranslatePoint(const Vector3& point, const EulerAngles& orient, float dist);
|
||||||
Vector3 TranslatePoint(const Vector3& point, const AxisAngle& orient, float distance);
|
Vector3 TranslatePoint(const Vector3& point, const AxisAngle& orient, float dist);
|
||||||
Vector3 TranslatePoint(const Vector3& point, const Vector3& direction, float distance);
|
Vector3 TranslatePoint(const Vector3& point, const Vector3& dir, float dist);
|
||||||
|
|
||||||
// Rotation
|
// Rotation
|
||||||
Vector3 RotatePoint(const Vector3& point, const EulerAngles& rot);
|
Vector3 RotatePoint(const Vector3& point, const EulerAngles& rot);
|
||||||
|
@ -39,10 +39,10 @@ namespace TEN::Math::Geometry
|
||||||
float GetDistanceToLine(const Vector3& origin, const Vector3& linePoint0, const Vector3& linePoint1);
|
float GetDistanceToLine(const Vector3& origin, const Vector3& linePoint0, const Vector3& linePoint1);
|
||||||
Vector3 GetClosestPointOnLine(const Vector3& origin, const Vector3& linePoint0, const Vector3& linePoint1);
|
Vector3 GetClosestPointOnLine(const Vector3& origin, const Vector3& linePoint0, const Vector3& linePoint1);
|
||||||
EulerAngles GetOrientToPoint(const Vector3& origin, const Vector3& target);
|
EulerAngles GetOrientToPoint(const Vector3& origin, const Vector3& target);
|
||||||
EulerAngles GetRelOrientToNormal(short orient2D, const Vector3& normal, const Vector3& gravity = Vector3::UnitY);
|
EulerAngles GetRelOrientToNormal(short orient, const Vector3& normal, const Vector3& gravity = Vector3::UnitY);
|
||||||
|
|
||||||
// Converters
|
// Converters
|
||||||
Quaternion ConvertDirectionToQuat(const Vector3& direction);
|
Quaternion ConvertDirectionToQuat(const Vector3& dir);
|
||||||
Vector3 ConvertQuatToDirection(const Quaternion& quat);
|
Vector3 ConvertQuatToDirection(const Quaternion& quat);
|
||||||
|
|
||||||
// Point relation inquirers
|
// Point relation inquirers
|
||||||
|
|
|
@ -39,6 +39,16 @@ constexpr float TO_RAD(short shortAngle)
|
||||||
return ((shortAngle * DEGREES_TO_ONE_SHORT) * RADIAN);
|
return ((shortAngle * DEGREES_TO_ONE_SHORT) * RADIAN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constexpr float RAD_TO_DEG(float radians)
|
||||||
|
{
|
||||||
|
return ((radians * 180.0f) / PI);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr float DEG_TO_RAD(float degrees)
|
||||||
|
{
|
||||||
|
return ((degrees * PI) / 180.0f);
|
||||||
|
}
|
||||||
|
|
||||||
float phd_sin(short x);
|
float phd_sin(short x);
|
||||||
float phd_cos(short x);
|
float phd_cos(short x);
|
||||||
int phd_atan(int y, int x);
|
int phd_atan(int y, int x);
|
||||||
|
|
|
@ -14,10 +14,10 @@ using namespace TEN::Math;
|
||||||
|
|
||||||
AxisAngle::AxisAngle(const Vector3& axis, short angle)
|
AxisAngle::AxisAngle(const Vector3& axis, short angle)
|
||||||
{
|
{
|
||||||
auto axisNorm = axis;
|
auto normalizedAxis = axis;
|
||||||
axisNorm.Normalize();
|
normalizedAxis.Normalize();
|
||||||
|
|
||||||
Axis = axisNorm;
|
Axis = normalizedAxis;
|
||||||
Angle = angle;
|
Angle = angle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,10 +75,10 @@ using namespace TEN::Math;
|
||||||
|
|
||||||
void AxisAngle::SetAxis(const Vector3& axis)
|
void AxisAngle::SetAxis(const Vector3& axis)
|
||||||
{
|
{
|
||||||
auto axisNorm = axis;
|
auto normalizedAxis = axis;
|
||||||
axisNorm.Normalize();
|
normalizedAxis.Normalize();
|
||||||
|
|
||||||
Axis = axisNorm;
|
Axis = normalizedAxis;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AxisAngle::SetAngle(short angle)
|
void AxisAngle::SetAngle(short angle)
|
||||||
|
@ -118,8 +118,8 @@ using namespace TEN::Math;
|
||||||
Vector3 AxisAngle::ToDirection() const
|
Vector3 AxisAngle::ToDirection() const
|
||||||
{
|
{
|
||||||
// TODO: Works, but need to find a way without EulerAngles. -- Sezz 2023.03.08
|
// TODO: Works, but need to find a way without EulerAngles. -- Sezz 2023.03.08
|
||||||
auto refDirection = Geometry::RotatePoint(Vector3::Right, EulerAngles(Axis));
|
auto refDir = Geometry::RotatePoint(Vector3::Right, EulerAngles(Axis));
|
||||||
return Geometry::RotatePoint(refDirection, *this);
|
return Geometry::RotatePoint(refDir, *this);
|
||||||
}
|
}
|
||||||
|
|
||||||
EulerAngles AxisAngle::ToEulerAngles() const
|
EulerAngles AxisAngle::ToEulerAngles() const
|
||||||
|
|
|
@ -12,13 +12,13 @@ using namespace TEN::Math;
|
||||||
//{
|
//{
|
||||||
const EulerAngles EulerAngles::Zero = EulerAngles(0, 0, 0);
|
const EulerAngles EulerAngles::Zero = EulerAngles(0, 0, 0);
|
||||||
|
|
||||||
EulerAngles::EulerAngles(const Vector3& direction)
|
EulerAngles::EulerAngles(const Vector3& dir)
|
||||||
{
|
{
|
||||||
auto directionNorm = direction;
|
auto normalizedDir = dir;
|
||||||
directionNorm.Normalize();
|
normalizedDir.Normalize();
|
||||||
|
|
||||||
x = FROM_RAD(-asin(directionNorm.y));
|
x = FROM_RAD(-asin(normalizedDir.y));
|
||||||
y = FROM_RAD(atan2(directionNorm.x, directionNorm.z));
|
y = FROM_RAD(atan2(normalizedDir.x, normalizedDir.z));
|
||||||
z = 0;
|
z = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -191,19 +191,19 @@ using namespace TEN::Math;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
EulerAngles& EulerAngles::operator *=(float scale)
|
EulerAngles& EulerAngles::operator *=(float scalar)
|
||||||
{
|
{
|
||||||
x *= scale;
|
x *= scalar;
|
||||||
y *= scale;
|
y *= scalar;
|
||||||
z *= scale;
|
z *= scalar;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
EulerAngles& EulerAngles::operator /=(float scale)
|
EulerAngles& EulerAngles::operator /=(float scalar)
|
||||||
{
|
{
|
||||||
x /= scale;
|
x /= scalar;
|
||||||
y /= scale;
|
y /= scalar;
|
||||||
z /= scale;
|
z /= scalar;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -222,14 +222,14 @@ using namespace TEN::Math;
|
||||||
return EulerAngles(x * eulers.x, y * eulers.y, z * eulers.z);
|
return EulerAngles(x * eulers.x, y * eulers.y, z * eulers.z);
|
||||||
}
|
}
|
||||||
|
|
||||||
EulerAngles EulerAngles::operator *(float scale) const
|
EulerAngles EulerAngles::operator *(float scalar) const
|
||||||
{
|
{
|
||||||
return EulerAngles((short)round(x * scale), (short)round(y * scale), (short)round(z * scale));
|
return EulerAngles((short)round(x * scalar), (short)round(y * scalar), (short)round(z * scalar));
|
||||||
}
|
}
|
||||||
|
|
||||||
EulerAngles EulerAngles::operator /(float scale) const
|
EulerAngles EulerAngles::operator /(float scalar) const
|
||||||
{
|
{
|
||||||
return EulerAngles((short)round(x / scale), (short)round(y / scale), (short)round(z / scale));
|
return EulerAngles((short)round(x / scalar), (short)round(y / scalar), (short)round(z / scalar));
|
||||||
}
|
}
|
||||||
|
|
||||||
float EulerAngles::ClampAlpha(float alpha)
|
float EulerAngles::ClampAlpha(float alpha)
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
// Constructors
|
// Constructors
|
||||||
constexpr EulerAngles() {};
|
constexpr EulerAngles() {};
|
||||||
constexpr EulerAngles(short x, short y, short z) { this->x = x; this->y = y; this->z = z; };
|
constexpr EulerAngles(short x, short y, short z) { this->x = x; this->y = y; this->z = z; };
|
||||||
EulerAngles(const Vector3& direction);
|
EulerAngles(const Vector3& dir);
|
||||||
EulerAngles(const AxisAngle& axisAngle);
|
EulerAngles(const AxisAngle& axisAngle);
|
||||||
EulerAngles(const Quaternion& quat);
|
EulerAngles(const Quaternion& quat);
|
||||||
EulerAngles(const Matrix& rotMatrix);
|
EulerAngles(const Matrix& rotMatrix);
|
||||||
|
@ -44,13 +44,13 @@
|
||||||
EulerAngles& operator +=(const EulerAngles& eulers);
|
EulerAngles& operator +=(const EulerAngles& eulers);
|
||||||
EulerAngles& operator -=(const EulerAngles& eulers);
|
EulerAngles& operator -=(const EulerAngles& eulers);
|
||||||
EulerAngles& operator *=(const EulerAngles& eulers);
|
EulerAngles& operator *=(const EulerAngles& eulers);
|
||||||
EulerAngles& operator *=(float scale);
|
EulerAngles& operator *=(float scalar);
|
||||||
EulerAngles& operator /=(float scale);
|
EulerAngles& operator /=(float scalar);
|
||||||
EulerAngles operator +(const EulerAngles& eulers) const;
|
EulerAngles operator +(const EulerAngles& eulers) const;
|
||||||
EulerAngles operator -(const EulerAngles& eulers) const;
|
EulerAngles operator -(const EulerAngles& eulers) const;
|
||||||
EulerAngles operator *(const EulerAngles& eulers) const;
|
EulerAngles operator *(const EulerAngles& eulers) const;
|
||||||
EulerAngles operator *(float scale) const;
|
EulerAngles operator *(float scalar) const;
|
||||||
EulerAngles operator /(float scale) const;
|
EulerAngles operator /(float scalar) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Temporary. Will be integrated into eventual Angle class.
|
// Temporary. Will be integrated into eventual Angle class.
|
||||||
|
|
|
@ -66,14 +66,16 @@
|
||||||
return ((Vector3(X2, Y2, Z2) - Vector3(X1, Y1, Z1)) / 2);
|
return ((Vector3(X2, Y2, Z2) - Vector3(X1, Y1, Z1)) / 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameBoundingBox::RotateNoPersp(const EulerAngles& orient, const GameBoundingBox& bounds)
|
void GameBoundingBox::Rotate(const EulerAngles& rot)
|
||||||
{
|
{
|
||||||
auto rotMatrix = orient.ToRotationMatrix();
|
// Get box min and max values.
|
||||||
auto boxMin = Vector3(bounds.X1, bounds.Y1, bounds.Z1);
|
auto boxMax = Vector3(X2, Y2, Z2);
|
||||||
auto boxMax = Vector3(bounds.X2, bounds.Y2, bounds.Z2);
|
auto boxMin = Vector3(X1, Y1, Z1);
|
||||||
|
|
||||||
boxMin = Vector3::Transform(boxMin, rotMatrix);
|
// Rotate min and max values.
|
||||||
|
auto rotMatrix = rot.ToRotationMatrix();
|
||||||
boxMax = Vector3::Transform(boxMax, rotMatrix);
|
boxMax = Vector3::Transform(boxMax, rotMatrix);
|
||||||
|
boxMin = Vector3::Transform(boxMin, rotMatrix);
|
||||||
|
|
||||||
X1 = (int)round(boxMin.x);
|
X1 = (int)round(boxMin.x);
|
||||||
X2 = (int)round(boxMax.x);
|
X2 = (int)round(boxMax.x);
|
||||||
|
@ -90,7 +92,7 @@
|
||||||
|
|
||||||
BoundingOrientedBox GameBoundingBox::ToBoundingOrientedBox(const Vector3& pos, const Quaternion& orient) const
|
BoundingOrientedBox GameBoundingBox::ToBoundingOrientedBox(const Vector3& pos, const Quaternion& orient) const
|
||||||
{
|
{
|
||||||
BoundingOrientedBox box;
|
auto box = BoundingOrientedBox();
|
||||||
BoundingOrientedBox(GetCenter(), GetExtents(), Vector4::UnitY).Transform(box, 1.0f, orient, pos);
|
BoundingOrientedBox(GetCenter(), GetExtents(), Vector4::UnitY).Transform(box, 1.0f, orient, pos);
|
||||||
return box;
|
return box;
|
||||||
}
|
}
|
||||||
|
@ -127,19 +129,19 @@
|
||||||
Z1 - pose.Position.z, Z2 - pose.Position.z);
|
Z1 - pose.Position.z, Z2 - pose.Position.z);
|
||||||
}
|
}
|
||||||
|
|
||||||
GameBoundingBox GameBoundingBox::operator *(float scale) const
|
GameBoundingBox GameBoundingBox::operator *(float scalar) const
|
||||||
{
|
{
|
||||||
return GameBoundingBox(
|
return GameBoundingBox(
|
||||||
X1 * scale, X2 * scale,
|
X1 * scalar, X2 * scalar,
|
||||||
Y1 * scale, Y2 * scale,
|
Y1 * scalar, Y2 * scalar,
|
||||||
Z1 * scale, Z2 * scale);
|
Z1 * scalar, Z2 * scalar);
|
||||||
}
|
}
|
||||||
|
|
||||||
GameBoundingBox GameBoundingBox::operator /(float scale) const
|
GameBoundingBox GameBoundingBox::operator /(float scalar) const
|
||||||
{
|
{
|
||||||
return GameBoundingBox(
|
return GameBoundingBox(
|
||||||
X1 / scale, X2 / scale,
|
X1 / scalar, X2 / scalar,
|
||||||
Y1 / scale, Y2 / scale,
|
Y1 / scalar, Y2 / scalar,
|
||||||
Z1 / scale, Z2 / scale);
|
Z1 / scalar, Z2 / scalar);
|
||||||
}
|
}
|
||||||
//}
|
//}
|
||||||
|
|
|
@ -36,7 +36,7 @@ struct ObjectInfo;
|
||||||
Vector3 GetExtents() const;
|
Vector3 GetExtents() const;
|
||||||
|
|
||||||
// Utilities
|
// Utilities
|
||||||
void RotateNoPersp(const EulerAngles& orient, const GameBoundingBox& bounds);
|
void Rotate(const EulerAngles& rot);
|
||||||
|
|
||||||
// Converters
|
// Converters
|
||||||
BoundingOrientedBox ToBoundingOrientedBox(const Pose& pose) const;
|
BoundingOrientedBox ToBoundingOrientedBox(const Pose& pose) const;
|
||||||
|
@ -47,7 +47,7 @@ struct ObjectInfo;
|
||||||
GameBoundingBox operator +(const Pose& pose) const;
|
GameBoundingBox operator +(const Pose& pose) const;
|
||||||
GameBoundingBox operator -(const GameBoundingBox& bounds) const;
|
GameBoundingBox operator -(const GameBoundingBox& bounds) const;
|
||||||
GameBoundingBox operator -(const Pose& pose) const;
|
GameBoundingBox operator -(const Pose& pose) const;
|
||||||
GameBoundingBox operator *(float scale) const;
|
GameBoundingBox operator *(float scalar) const;
|
||||||
GameBoundingBox operator /(float scale) const;
|
GameBoundingBox operator /(float scalar) const;
|
||||||
};
|
};
|
||||||
//}
|
//}
|
||||||
|
|
|
@ -94,19 +94,19 @@
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
GameVector& GameVector::operator *=(float scale)
|
GameVector& GameVector::operator *=(float scalar)
|
||||||
{
|
{
|
||||||
x *= scale;
|
x *= scalar;
|
||||||
y *= scale;
|
y *= scalar;
|
||||||
z *= scale;
|
z *= scalar;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
GameVector& GameVector::operator /=(float scale)
|
GameVector& GameVector::operator /=(float scalar)
|
||||||
{
|
{
|
||||||
x /= scale;
|
x /= scalar;
|
||||||
y /= scale;
|
y /= scalar;
|
||||||
z /= scale;
|
z /= scalar;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -125,13 +125,13 @@
|
||||||
return GameVector(x * vector.x, y * vector.y, z * vector.z, RoomNumber);
|
return GameVector(x * vector.x, y * vector.y, z * vector.z, RoomNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
GameVector GameVector::operator *(float scale) const
|
GameVector GameVector::operator *(float scalar) const
|
||||||
{
|
{
|
||||||
return GameVector((int)round(x * scale), (int)round(y * scale), (int)round(z * scale), RoomNumber);
|
return GameVector((int)round(x * scalar), (int)round(y * scalar), (int)round(z * scalar), RoomNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
GameVector GameVector::operator /(float scale) const
|
GameVector GameVector::operator /(float scalar) const
|
||||||
{
|
{
|
||||||
return GameVector((int)round(x / scale), (int)round(y / scale), (int)round(z / scale), RoomNumber);
|
return GameVector((int)round(x / scalar), (int)round(y / scalar), (int)round(z / scalar), RoomNumber);
|
||||||
}
|
}
|
||||||
//}
|
//}
|
||||||
|
|
|
@ -35,12 +35,12 @@ class Vector3i;
|
||||||
GameVector& operator +=(const GameVector& vector);
|
GameVector& operator +=(const GameVector& vector);
|
||||||
GameVector& operator -=(const GameVector& vector);
|
GameVector& operator -=(const GameVector& vector);
|
||||||
GameVector& operator *=(const GameVector& vector);
|
GameVector& operator *=(const GameVector& vector);
|
||||||
GameVector& operator *=(float scale);
|
GameVector& operator *=(float scalar);
|
||||||
GameVector& operator /=(float scale);
|
GameVector& operator /=(float scalar);
|
||||||
GameVector operator +(const GameVector& vector) const;
|
GameVector operator +(const GameVector& vector) const;
|
||||||
GameVector operator -(const GameVector& vector) const;
|
GameVector operator -(const GameVector& vector) const;
|
||||||
GameVector operator *(const GameVector& vector) const;
|
GameVector operator *(const GameVector& vector) const;
|
||||||
GameVector operator *(float scale) const;
|
GameVector operator *(float scalar) const;
|
||||||
GameVector operator /(float scale) const;
|
GameVector operator /(float scalar) const;
|
||||||
};
|
};
|
||||||
//}
|
//}
|
||||||
|
|
|
@ -64,20 +64,21 @@ using namespace TEN::Math;
|
||||||
Position = Geometry::TranslatePoint(Position, headingAngle, forward, down, right);
|
Position = Geometry::TranslatePoint(Position, headingAngle, forward, down, right);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Pose::Translate(const EulerAngles& orient, float distance)
|
void Pose::Translate(const EulerAngles& orient, float dist)
|
||||||
{
|
{
|
||||||
Position = Geometry::TranslatePoint(Position, orient, distance);
|
Position = Geometry::TranslatePoint(Position, orient, dist);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Pose::Translate(const Vector3& direction, float distance)
|
void Pose::Translate(const Vector3& dir, float dist)
|
||||||
{
|
{
|
||||||
Position = Geometry::TranslatePoint(Position, direction, distance);
|
Position = Geometry::TranslatePoint(Position, dir, dist);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Pose::operator ==(const Pose& pose) const
|
bool Pose::operator ==(const Pose& pose) const
|
||||||
{
|
{
|
||||||
return ((Position == pose.Position) && (Orientation == pose.Orientation));
|
return ((Position == pose.Position) && (Orientation == pose.Orientation));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Pose::operator !=(const Pose& pose) const
|
bool Pose::operator !=(const Pose& pose) const
|
||||||
{
|
{
|
||||||
return !(*this == pose);
|
return !(*this == pose);
|
||||||
|
|
|
@ -27,8 +27,8 @@
|
||||||
|
|
||||||
// Utilities
|
// Utilities
|
||||||
void Translate(short headingAngle, float forward, float down = 0.0f, float right = 0.0f);
|
void Translate(short headingAngle, float forward, float down = 0.0f, float right = 0.0f);
|
||||||
void Translate(const EulerAngles& orient, float distance);
|
void Translate(const EulerAngles& orient, float dist);
|
||||||
void Translate(const Vector3& direction, float distance);
|
void Translate(const Vector3& dir, float dist);
|
||||||
|
|
||||||
// Operators
|
// Operators
|
||||||
bool operator ==(const Pose& pose) const;
|
bool operator ==(const Pose& pose) const;
|
||||||
|
|
|
@ -64,17 +64,17 @@ namespace TEN::Math
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector2i& Vector2i::operator *=(float scale)
|
Vector2i& Vector2i::operator *=(float scalar)
|
||||||
{
|
{
|
||||||
x *= scale;
|
x *= scalar;
|
||||||
y *= scale;
|
y *= scalar;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector2i& Vector2i::operator /=(float scale)
|
Vector2i& Vector2i::operator /=(float scalar)
|
||||||
{
|
{
|
||||||
x /= scale;
|
x /= scalar;
|
||||||
y /= scale;
|
y /= scalar;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,13 +93,13 @@ namespace TEN::Math
|
||||||
return Vector2i(x * vector.x, y * vector.y);
|
return Vector2i(x * vector.x, y * vector.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector2i Vector2i::operator *(float scale) const
|
Vector2i Vector2i::operator *(float scalar) const
|
||||||
{
|
{
|
||||||
return Vector2i((int)round(x * scale), (int)round(y * scale));
|
return Vector2i((int)round(x * scalar), (int)round(y * scalar));
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector2i Vector2i::operator /(float scale) const
|
Vector2i Vector2i::operator /(float scalar) const
|
||||||
{
|
{
|
||||||
return Vector2i((int)round(x / scale), (int)round(y / scale));
|
return Vector2i((int)round(x / scalar), (int)round(y / scalar));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,12 +31,12 @@ namespace TEN::Math
|
||||||
Vector2i& operator +=(const Vector2i& vector);
|
Vector2i& operator +=(const Vector2i& vector);
|
||||||
Vector2i& operator -=(const Vector2i& vector);
|
Vector2i& operator -=(const Vector2i& vector);
|
||||||
Vector2i& operator *=(const Vector2i& vector);
|
Vector2i& operator *=(const Vector2i& vector);
|
||||||
Vector2i& operator *=(float scale);
|
Vector2i& operator *=(float scalar);
|
||||||
Vector2i& operator /=(float scale);
|
Vector2i& operator /=(float scalar);
|
||||||
Vector2i operator +(const Vector2i& vector) const;
|
Vector2i operator +(const Vector2i& vector) const;
|
||||||
Vector2i operator -(const Vector2i& vector) const;
|
Vector2i operator -(const Vector2i& vector) const;
|
||||||
Vector2i operator *(const Vector2i& vector) const;
|
Vector2i operator *(const Vector2i& vector) const;
|
||||||
Vector2i operator *(float scale) const;
|
Vector2i operator *(float scalar) const;
|
||||||
Vector2i operator /(float scale) const;
|
Vector2i operator /(float scalar) const;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,19 +69,19 @@
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector3i& Vector3i::operator *=(float scale)
|
Vector3i& Vector3i::operator *=(float scalar)
|
||||||
{
|
{
|
||||||
x *= scale;
|
x *= scalar;
|
||||||
y *= scale;
|
y *= scalar;
|
||||||
z *= scale;
|
z *= scalar;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector3i& Vector3i::operator /=(float scale)
|
Vector3i& Vector3i::operator /=(float scalar)
|
||||||
{
|
{
|
||||||
x /= scale;
|
x /= scalar;
|
||||||
y /= scale;
|
y /= scalar;
|
||||||
z /= scale;
|
z /= scalar;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,13 +100,13 @@
|
||||||
return Vector3i(x * vector.x, y * vector.y, z * vector.z);
|
return Vector3i(x * vector.x, y * vector.y, z * vector.z);
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector3i Vector3i::operator *(float scale) const
|
Vector3i Vector3i::operator *(float scalar) const
|
||||||
{
|
{
|
||||||
return Vector3i((int)round(x * scale), (int)round(y * scale), (int)round(z * scale));
|
return Vector3i((int)round(x * scalar), (int)round(y * scalar), (int)round(z * scalar));
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector3i Vector3i::operator /(float scale) const
|
Vector3i Vector3i::operator /(float scalar) const
|
||||||
{
|
{
|
||||||
return Vector3i((int)round(x / scale), (int)round(y / scale), (int)round(z / scale));
|
return Vector3i((int)round(x / scalar), (int)round(y / scalar), (int)round(z / scalar));
|
||||||
}
|
}
|
||||||
//}
|
//}
|
||||||
|
|
|
@ -32,12 +32,12 @@
|
||||||
Vector3i& operator +=(const Vector3i& vector);
|
Vector3i& operator +=(const Vector3i& vector);
|
||||||
Vector3i& operator -=(const Vector3i& vector);
|
Vector3i& operator -=(const Vector3i& vector);
|
||||||
Vector3i& operator *=(const Vector3i& vector);
|
Vector3i& operator *=(const Vector3i& vector);
|
||||||
Vector3i& operator *=(float scale);
|
Vector3i& operator *=(float scalar);
|
||||||
Vector3i& operator /=(float scale);
|
Vector3i& operator /=(float scalar);
|
||||||
Vector3i operator +(const Vector3i& vector) const;
|
Vector3i operator +(const Vector3i& vector) const;
|
||||||
Vector3i operator -(const Vector3i& vector) const;
|
Vector3i operator -(const Vector3i& vector) const;
|
||||||
Vector3i operator *(const Vector3i& vector) const;
|
Vector3i operator *(const Vector3i& vector) const;
|
||||||
Vector3i operator *(float scale) const;
|
Vector3i operator *(float scalar) const;
|
||||||
Vector3i operator /(float scale) const;
|
Vector3i operator /(float scalar) const;
|
||||||
};
|
};
|
||||||
//}
|
//}
|
||||||
|
|
|
@ -30,19 +30,19 @@ namespace TEN::Math::Random
|
||||||
return Vector2(cos(angle), sin(angle));
|
return Vector2(cos(angle), sin(angle));
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector2 GeneratePoint2DInSquare(const Vector2& pos2D, short orient2D, float apothem)
|
Vector2 GeneratePoint2DInSquare(const Vector2& pos, short orient, float apothem)
|
||||||
{
|
{
|
||||||
auto rotMatrix = Matrix::CreateRotationZ(orient2D);
|
auto rotMatrix = Matrix::CreateRotationZ(orient);
|
||||||
auto relPoint = Vector2(
|
auto relPoint = Vector2(
|
||||||
GenerateFloat(-apothem, apothem),
|
GenerateFloat(-apothem, apothem),
|
||||||
GenerateFloat(-apothem, apothem));
|
GenerateFloat(-apothem, apothem));
|
||||||
|
|
||||||
return (pos2D + Vector2::Transform(relPoint, rotMatrix));
|
return (pos + Vector2::Transform(relPoint, rotMatrix));
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector2 GeneratePoint2DInCircle(const Vector2& pos2D, float radius)
|
Vector2 GeneratePoint2DInCircle(const Vector2& pos, float radius)
|
||||||
{
|
{
|
||||||
// Use rejection sampling.
|
// Use rejection sampling method.
|
||||||
auto relPoint = Vector2::Zero;
|
auto relPoint = Vector2::Zero;
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
|
@ -51,7 +51,7 @@ namespace TEN::Math::Random
|
||||||
GenerateFloat(-1.0f, 1.0f));
|
GenerateFloat(-1.0f, 1.0f));
|
||||||
} while (relPoint.LengthSquared() > 1.0f);
|
} while (relPoint.LengthSquared() > 1.0f);
|
||||||
|
|
||||||
return (pos2D + (relPoint * radius));
|
return (pos + (relPoint * radius));
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector3 GenerateDirection()
|
Vector3 GenerateDirection()
|
||||||
|
@ -59,24 +59,24 @@ namespace TEN::Math::Random
|
||||||
float theta = GenerateFloat(0.0f, PI_MUL_2); // Generate angle in full circle.
|
float theta = GenerateFloat(0.0f, PI_MUL_2); // Generate angle in full circle.
|
||||||
float phi = GenerateFloat(0.0f, PI); // Generate angle in sphere's upper half.
|
float phi = GenerateFloat(0.0f, PI); // Generate angle in sphere's upper half.
|
||||||
|
|
||||||
auto direction = Vector3(
|
auto dir = Vector3(
|
||||||
sin(phi) * cos(theta),
|
sin(phi) * cos(theta),
|
||||||
sin(phi) * sin(theta),
|
sin(phi) * sin(theta),
|
||||||
cos(phi));
|
cos(phi));
|
||||||
direction.Normalize();
|
dir.Normalize();
|
||||||
return direction;
|
return dir;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector3 GenerateDirectionInCone(const Vector3& direction, float semiangleInDeg)
|
Vector3 GenerateDirectionInCone(const Vector3& dir, float semiangleInDeg)
|
||||||
{
|
{
|
||||||
float x = GenerateFloat(-semiangleInDeg, semiangleInDeg) * RADIAN;
|
float x = GenerateFloat(-semiangleInDeg, semiangleInDeg) * RADIAN;
|
||||||
float y = GenerateFloat(-semiangleInDeg, semiangleInDeg) * RADIAN;
|
float y = GenerateFloat(-semiangleInDeg, semiangleInDeg) * RADIAN;
|
||||||
float z = GenerateFloat(-semiangleInDeg, semiangleInDeg) * RADIAN;
|
float z = GenerateFloat(-semiangleInDeg, semiangleInDeg) * RADIAN;
|
||||||
auto rotMatrix = Matrix::CreateRotationX(x) * Matrix::CreateRotationY(y) * Matrix::CreateRotationZ(z);
|
auto rotMatrix = Matrix::CreateRotationX(x) * Matrix::CreateRotationY(y) * Matrix::CreateRotationZ(z);
|
||||||
|
|
||||||
auto directionInCone = Vector3::TransformNormal(direction, rotMatrix);
|
auto dirInCone = Vector3::TransformNormal(dir, rotMatrix);
|
||||||
directionInCone.Normalize();
|
dirInCone.Normalize();
|
||||||
return directionInCone;
|
return dirInCone;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector3 GeneratePointInBox(const BoundingOrientedBox& box)
|
Vector3 GeneratePointInBox(const BoundingOrientedBox& box)
|
||||||
|
@ -92,7 +92,7 @@ namespace TEN::Math::Random
|
||||||
|
|
||||||
Vector3 GeneratePointInSphere(const BoundingSphere& sphere)
|
Vector3 GeneratePointInSphere(const BoundingSphere& sphere)
|
||||||
{
|
{
|
||||||
// Use rejection sampling.
|
// Use rejection sampling method.
|
||||||
auto relPoint = Vector3::Zero;
|
auto relPoint = Vector3::Zero;
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
|
@ -120,19 +120,19 @@ namespace TEN::Math::Random
|
||||||
return (sphere.Center + (relPoint * sphere.Radius));
|
return (sphere.Center + (relPoint * sphere.Radius));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TestProbability(float probability)
|
bool TestProbability(float prob)
|
||||||
{
|
{
|
||||||
probability = std::clamp(probability, 0.0f, 1.0f);
|
prob = std::clamp(prob, 0.0f, 1.0f);
|
||||||
|
|
||||||
if (probability == 0.0f)
|
if (prob == 0.0f)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else if (probability == 1.0f)
|
else if (prob == 1.0f)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (GenerateFloat(0.0f, 1.0f) < probability);
|
return (GenerateFloat(0.0f, 1.0f) < prob);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,15 +9,15 @@ namespace TEN::Math::Random
|
||||||
|
|
||||||
// 2D geometric generation
|
// 2D geometric generation
|
||||||
Vector2 GenerateDirection2D();
|
Vector2 GenerateDirection2D();
|
||||||
Vector2 GeneratePoint2DInSquare(const Vector2& pos2D, short orient2D, float apothem);
|
Vector2 GeneratePoint2DInSquare(const Vector2& pos, short orient, float apothem);
|
||||||
Vector2 GeneratePoint2DInCircle(const Vector2& pos2D, float radius);
|
Vector2 GeneratePoint2DInCircle(const Vector2& pos, float radius);
|
||||||
|
|
||||||
// 3D geometric generation
|
// 3D geometric generation
|
||||||
Vector3 GenerateDirection();
|
Vector3 GenerateDirection();
|
||||||
Vector3 GenerateDirectionInCone(const Vector3& direction, float semiangleInDeg);
|
Vector3 GenerateDirectionInCone(const Vector3& dir, float semiangleInDeg);
|
||||||
Vector3 GeneratePointInBox(const BoundingOrientedBox& box);
|
Vector3 GeneratePointInBox(const BoundingOrientedBox& box);
|
||||||
Vector3 GeneratePointInSphere(const BoundingSphere& sphere);
|
Vector3 GeneratePointInSphere(const BoundingSphere& sphere);
|
||||||
Vector3 GeneratePointOnSphere(const BoundingSphere& sphere);
|
Vector3 GeneratePointOnSphere(const BoundingSphere& sphere);
|
||||||
|
|
||||||
bool TestProbability(float probability);
|
bool TestProbability(float probab);
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,95 +5,108 @@
|
||||||
|
|
||||||
namespace TEN::Math::Solvers
|
namespace TEN::Math::Solvers
|
||||||
{
|
{
|
||||||
std::pair<float, float> SolveQuadratic(float a, float b, float c)
|
std::optional<QuadraticSolution> SolveQuadratic(float a, float b, float c)
|
||||||
{
|
{
|
||||||
auto result = std::pair(INFINITY, INFINITY);
|
if (abs(a) < EPSILON)
|
||||||
|
|
||||||
if (abs(a) < FLT_EPSILON)
|
|
||||||
{
|
{
|
||||||
if (abs(b) < FLT_EPSILON)
|
// 0 solutions.
|
||||||
return result; // Zero solutions.
|
if (abs(b) < EPSILON)
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
result.first = -c / b;
|
// 1 solution.
|
||||||
result.second = result.first;
|
float root = -c / b;
|
||||||
return result; // One solution.
|
return QuadraticSolution{ root, root };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 0 solutions.
|
||||||
float discriminant = SQUARE(b) - (4.0f * a * c);
|
float discriminant = SQUARE(b) - (4.0f * a * c);
|
||||||
if (discriminant < 0.0f)
|
if (discriminant < 0.0f)
|
||||||
return result; // Zero solutions.
|
return std::nullopt;
|
||||||
|
|
||||||
float inv2a = 1.0f / (2.0f * a);
|
float inv2a = 1.0f / (2.0f * a);
|
||||||
|
|
||||||
if (discriminant < FLT_EPSILON)
|
// 1 solution.
|
||||||
|
if (discriminant < EPSILON)
|
||||||
{
|
{
|
||||||
result.first = -b * inv2a;
|
float root = -b * inv2a;
|
||||||
result.second = result.first;
|
return QuadraticSolution{ root, root };
|
||||||
return result; // One solution.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 2 solutions.
|
||||||
discriminant = sqrt(discriminant);
|
discriminant = sqrt(discriminant);
|
||||||
result.first = (-b - discriminant) * inv2a;
|
float root0 = (-b - discriminant) * inv2a;
|
||||||
result.second = (-b + discriminant) * inv2a;
|
float root1 = (-b + discriminant) * inv2a;
|
||||||
return result; // Two solutions.
|
return QuadraticSolution{ root0, root1 };
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SolveIK2D(const Vector2& target, float length0, float length1, Vector2& middle)
|
IK2DSolution SolveIK2D(const Vector2& origin, const Vector2& target, float length0, float length1)
|
||||||
{
|
{
|
||||||
float length = target.Length();
|
auto scaledTarget = target;
|
||||||
if (length > (length0 + length1))
|
auto lengthMax = length0 + length1;
|
||||||
return false;
|
auto dir = target - origin;
|
||||||
|
dir.Normalize();
|
||||||
|
|
||||||
bool flipXY = (target.x < target.y);
|
// Check if target is within reach.
|
||||||
float a = flipXY ? target.y : target.x;
|
float dist = Vector2::Distance(origin, target);
|
||||||
float b = flipXY ? target.x : target.y;
|
if (dist > lengthMax)
|
||||||
assert(abs(a) > FLT_EPSILON);
|
scaledTarget = origin + (dir * lengthMax);
|
||||||
|
|
||||||
|
// Ensure line slope is well defined.
|
||||||
|
bool flipXY = (scaledTarget.x < scaledTarget.y);
|
||||||
|
|
||||||
|
float a = flipXY ? (scaledTarget.y - origin.y) : (scaledTarget.x - origin.x);
|
||||||
|
float b = flipXY ? (scaledTarget.x - origin.x) : (scaledTarget.y - origin.y);
|
||||||
|
assertion(abs(a) >= EPSILON, "SolveIK2D() failed.");
|
||||||
|
|
||||||
float m = ((SQUARE(length0) - SQUARE(length1)) + (SQUARE(a) + SQUARE(b))) / (2.0f * a);
|
float m = ((SQUARE(length0) - SQUARE(length1)) + (SQUARE(a) + SQUARE(b))) / (2.0f * a);
|
||||||
float n = b / a;
|
float n = b / a;
|
||||||
auto quadratic = SolveQuadratic(1.0f + SQUARE(n), -2.0f * (m * n), SQUARE(m) - SQUARE(length0));
|
|
||||||
|
|
||||||
if (quadratic.first != INFINITY && quadratic.second != INFINITY)
|
auto quadraticSol = SolveQuadratic(1.0f + SQUARE(n), -2.0f * (m * n), SQUARE(m) - SQUARE(length0));
|
||||||
|
auto middle = Vector2::Zero;
|
||||||
|
|
||||||
|
// Solution is valid; define middle accurately.
|
||||||
|
if (quadraticSol.has_value())
|
||||||
{
|
{
|
||||||
middle.x = flipXY ? quadratic.second : (m - (n * quadratic.second));
|
middle = origin + (flipXY ?
|
||||||
middle.y = flipXY ? (m - (n * quadratic.second)) : quadratic.second;
|
Vector2(quadraticSol->Root1, (m - (n * quadraticSol->Root1))) :
|
||||||
return true;
|
Vector2(quadraticSol->Root0, (m - (n * quadraticSol->Root0))));
|
||||||
|
}
|
||||||
|
// Solution is invalid: define middle as point on line segment between origin and target.
|
||||||
|
else
|
||||||
|
{
|
||||||
|
middle = origin + (dir * length0);
|
||||||
}
|
}
|
||||||
|
|
||||||
middle = target * (length0 / length);
|
return IK2DSolution{ origin, middle, scaledTarget };
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SolveIK3D(const Vector3& origin, const Vector3& target, const Vector3& pole, float length0, float length1, Vector3& middle)
|
IK3DSolution SolveIK3D(const Vector3& origin, const Vector3& target, const Vector3& pole, float length0, float length1)
|
||||||
{
|
{
|
||||||
auto directionNorm = target - origin;
|
auto dir = (target - origin);
|
||||||
directionNorm.Normalize();
|
dir.Normalize();
|
||||||
auto normal = directionNorm.Cross(pole - origin);
|
auto normal = dir.Cross(pole - origin);
|
||||||
normal.Normalize();
|
normal.Normalize();
|
||||||
|
|
||||||
// TODO: Check what this means.
|
// Construct transform matrix.
|
||||||
Matrix matrix;
|
auto tMatrix = Matrix(
|
||||||
auto normalCrossDirectionNorm = normal.Cross(directionNorm);
|
Vector4(normal.Cross(dir)),
|
||||||
matrix._11 = normalCrossDirectionNorm.x;
|
Vector4(dir),
|
||||||
matrix._12 = normalCrossDirectionNorm.y;
|
Vector4(normal),
|
||||||
matrix._13 = normalCrossDirectionNorm.z;
|
Vector4(origin.x, origin.y, origin.z, 1.0f));
|
||||||
matrix._21 = directionNorm.x;
|
|
||||||
matrix._22 = directionNorm.y;
|
|
||||||
matrix._23 = directionNorm.z;
|
|
||||||
matrix._31 = normal.x;
|
|
||||||
matrix._32 = normal.y;
|
|
||||||
matrix._33 = normal.z;
|
|
||||||
matrix._41 = origin.x;
|
|
||||||
matrix._42 = origin.y;
|
|
||||||
matrix._43 = origin.z;
|
|
||||||
matrix._44 = 1.0f;
|
|
||||||
|
|
||||||
auto middle2D = Vector2(middle);
|
// Get relative 2D IK solution.
|
||||||
bool result = SolveIK2D(Vector2(Vector3::Transform(target, matrix.Invert())), length0, length1, middle2D);
|
auto inverseMatrix = tMatrix.Invert();
|
||||||
|
auto relTarget = Vector3::Transform(target, inverseMatrix);
|
||||||
|
auto ikSolution2D = SolveIK2D(Vector2::Zero, Vector2(relTarget), length0, length1);
|
||||||
|
|
||||||
middle = Vector3(middle2D);
|
// Calculate absolute middle position.
|
||||||
middle = Vector3::Transform(middle, matrix);
|
auto relMiddle = Vector3(ikSolution2D.Middle.x, ikSolution2D.Middle.y, 0.0f);
|
||||||
|
auto middle = Vector3::Transform(relMiddle, tMatrix);
|
||||||
|
|
||||||
return result;
|
// Calculate absolute end position.
|
||||||
|
auto localEnd = Vector3(ikSolution2D.End.x, ikSolution2D.End.y, 0.0f);
|
||||||
|
auto end = Vector3::Transform(localEnd, tMatrix);
|
||||||
|
|
||||||
|
return IK3DSolution{ origin, middle, end };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,27 @@
|
||||||
|
|
||||||
namespace TEN::Math::Solvers
|
namespace TEN::Math::Solvers
|
||||||
{
|
{
|
||||||
std::pair<float, float> SolveQuadratic(float a, float b, float c);
|
struct QuadraticSolution
|
||||||
|
{
|
||||||
|
float Root0 = 0.0f;
|
||||||
|
float Root1 = 0.0f;
|
||||||
|
};
|
||||||
|
|
||||||
bool SolveIK2D(const Vector2& target, float length0, float length1, Vector2& middle);
|
struct IK2DSolution
|
||||||
bool SolveIK3D(const Vector3& origin, const Vector3& target, const Vector3& pole, float length0, float length1, Vector3& middle);
|
{
|
||||||
|
Vector2 Base = Vector2::Zero;
|
||||||
|
Vector2 Middle = Vector2::Zero;
|
||||||
|
Vector2 End = Vector2::Zero;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct IK3DSolution
|
||||||
|
{
|
||||||
|
Vector3 Base = Vector3::Zero;
|
||||||
|
Vector3 Middle = Vector3::Zero;
|
||||||
|
Vector3 End = Vector3::Zero;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::optional<QuadraticSolution> SolveQuadratic(float a, float b, float c);
|
||||||
|
IK2DSolution SolveIK2D(const Vector2& origin, const Vector2& target, float length0, float length1);
|
||||||
|
IK3DSolution SolveIK3D(const Vector3& origin, const Vector3& target, const Vector3& pole, float length0, float length1);
|
||||||
}
|
}
|
||||||
|
|
|
@ -207,7 +207,7 @@ void ElectricityWiresControl(short itemNumber)
|
||||||
if ((GetRandomControl() & 127) < 16)
|
if ((GetRandomControl() & 127) < 16)
|
||||||
{
|
{
|
||||||
SpawnRipple(
|
SpawnRipple(
|
||||||
Vector3(pos.x, floor->GetSurfaceHeight(pos.x, pos.y, pos.z, true), pos.z),
|
Vector3(pos.x, floor->GetSurfaceHeight(pos, true), pos.z),
|
||||||
roomNumber,
|
roomNumber,
|
||||||
Random::GenerateFloat(32.0f, 40.0f),
|
Random::GenerateFloat(32.0f, 40.0f),
|
||||||
(int)RippleFlags::LowOpacity);
|
(int)RippleFlags::LowOpacity);
|
||||||
|
|
|
@ -275,7 +275,7 @@ namespace TEN::Entities::Doors
|
||||||
doorItem->ItemFlags[0]--;
|
doorItem->ItemFlags[0]--;
|
||||||
doorItem->Pose.Position.y -= TEN::Entities::Switches::COG_DOOR_SPEED;
|
doorItem->Pose.Position.y -= TEN::Entities::Switches::COG_DOOR_SPEED;
|
||||||
|
|
||||||
int y = bounds.Y1 + doorItem->ItemFlags[2] - STEP_SIZE;
|
int y = bounds.Y1 + doorItem->ItemFlags[2] - CLICK(1);
|
||||||
if (doorItem->Pose.Position.y < y)
|
if (doorItem->Pose.Position.y < y)
|
||||||
{
|
{
|
||||||
doorItem->Pose.Position.y = y;
|
doorItem->Pose.Position.y = y;
|
||||||
|
@ -362,7 +362,7 @@ namespace TEN::Entities::Doors
|
||||||
{
|
{
|
||||||
if (!item->itemFlags[0])
|
if (!item->itemFlags[0])
|
||||||
SoundEffect(SFX_TR5_LIFT_DOORS, &item->pos);
|
SoundEffect(SFX_TR5_LIFT_DOORS, &item->pos);
|
||||||
item->itemFlags[0] += STEP_SIZE;
|
item->itemFlags[0] += CLICK(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -371,7 +371,7 @@ namespace TEN::Entities::Doors
|
||||||
{
|
{
|
||||||
if (item->itemFlags[0] == BLOCK(4))
|
if (item->itemFlags[0] == BLOCK(4))
|
||||||
SoundEffect(SFX_TR5_LIFT_DOORS, &item->pos);
|
SoundEffect(SFX_TR5_LIFT_DOORS, &item->pos);
|
||||||
item->itemFlags[0] -= STEP_SIZE;
|
item->itemFlags[0] -= CLICK(1);
|
||||||
}
|
}
|
||||||
if (!door->opened)
|
if (!door->opened)
|
||||||
{
|
{
|
||||||
|
|
112
TombEngine/Objects/Generic/Object/Pushable/PushableBridge.cpp
Normal file
112
TombEngine/Objects/Generic/Object/Pushable/PushableBridge.cpp
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
#include "framework.h"
|
||||||
|
#include "Objects/Generic/Object/Pushable/PushableBridge.h"
|
||||||
|
|
||||||
|
#include "Game/collision/floordata.h"
|
||||||
|
#include "Objects/Generic/Object/Pushable/PushableObject.h"
|
||||||
|
#include "Specific/level.h"
|
||||||
|
|
||||||
|
using namespace TEN::Collision::Floordata;
|
||||||
|
|
||||||
|
namespace TEN::Entities::Generic
|
||||||
|
{
|
||||||
|
std::optional<int> PushableBridgeFloor(int itemNumber, int x, int y, int z)
|
||||||
|
{
|
||||||
|
auto& pushableItem = g_Level.Items[itemNumber];
|
||||||
|
const auto& pushable = GetPushableInfo(pushableItem);
|
||||||
|
|
||||||
|
auto boxHeight = GetBridgeItemIntersect(Vector3i(x, y, z), itemNumber, false);
|
||||||
|
|
||||||
|
if (pushableItem.Active &&pushableItem.Status != ITEM_INVISIBLE &&
|
||||||
|
pushable.UseRoomCollision &&
|
||||||
|
boxHeight.has_value())
|
||||||
|
{
|
||||||
|
int height = pushableItem.Pose.Position.y - GetPushableHeight(pushableItem);
|
||||||
|
return height;
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<int> PushableBridgeCeiling(int itemNumber, int x, int y, int z)
|
||||||
|
{
|
||||||
|
auto& pushableItem = g_Level.Items[itemNumber];
|
||||||
|
const auto& pushable = GetPushableInfo(pushableItem);
|
||||||
|
|
||||||
|
auto boxHeight = GetBridgeItemIntersect(Vector3i(x, y, z), itemNumber, true);
|
||||||
|
|
||||||
|
if (pushableItem.Active && pushableItem.Status != ITEM_INVISIBLE &&
|
||||||
|
pushable.UseRoomCollision &&
|
||||||
|
boxHeight.has_value())
|
||||||
|
{
|
||||||
|
return pushableItem.Pose.Position.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
int PushableBridgeFloorBorder(int itemNumber)
|
||||||
|
{
|
||||||
|
auto& pushableItem = g_Level.Items[itemNumber];
|
||||||
|
|
||||||
|
return (pushableItem.Pose.Position.y - GetPushableHeight(pushableItem));
|
||||||
|
}
|
||||||
|
|
||||||
|
int PushableBridgeCeilingBorder(int itemNumber)
|
||||||
|
{
|
||||||
|
auto& pushableItem = g_Level.Items[itemNumber];
|
||||||
|
|
||||||
|
return pushableItem.Pose.Position.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddPushableBridge(ItemInfo& pushableItem)
|
||||||
|
{
|
||||||
|
auto& pushable = GetPushableInfo(pushableItem);
|
||||||
|
|
||||||
|
if (pushable.UseRoomCollision)
|
||||||
|
{
|
||||||
|
AddBridge(pushableItem.Index);
|
||||||
|
pushable.UseBridgeCollision = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddPushableStackBridge(ItemInfo& pushableItem, bool addBridge)
|
||||||
|
{
|
||||||
|
auto* pushableItemPtr = &g_Level.Items[pushableItem.Index];
|
||||||
|
const auto* pushablePtr = &GetPushableInfo(*pushableItemPtr);
|
||||||
|
|
||||||
|
// NOTE: Can't have stacked items on bridge.
|
||||||
|
if (!pushablePtr->UseRoomCollision)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (pushablePtr->UseBridgeCollision)
|
||||||
|
addBridge ? AddBridge(pushableItem.Index) : RemoveBridge(pushableItem.Index);
|
||||||
|
|
||||||
|
while (pushablePtr->Stack.ItemNumberAbove != NO_ITEM)
|
||||||
|
{
|
||||||
|
pushableItemPtr = &g_Level.Items[pushablePtr->Stack.ItemNumberAbove];
|
||||||
|
pushablePtr = &GetPushableInfo(*pushableItemPtr);
|
||||||
|
|
||||||
|
if (pushablePtr->UseBridgeCollision)
|
||||||
|
addBridge ? AddBridge(pushableItemPtr->Index) : RemoveBridge(pushableItemPtr->Index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RemovePushableBridge(ItemInfo& pushableItem)
|
||||||
|
{
|
||||||
|
auto& pushable = GetPushableInfo(pushableItem);
|
||||||
|
|
||||||
|
if (pushable.UseRoomCollision)
|
||||||
|
{
|
||||||
|
RemoveBridge(pushableItem.Index);
|
||||||
|
pushable.UseBridgeCollision = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UpdatePushableBridge(const ItemInfo& pushableItem)
|
||||||
|
{
|
||||||
|
const auto& pushable = GetPushableInfo(pushableItem);
|
||||||
|
|
||||||
|
if (pushable.UseRoomCollision)
|
||||||
|
UpdateBridgeItem(pushableItem.Index);
|
||||||
|
}
|
||||||
|
}
|
16
TombEngine/Objects/Generic/Object/Pushable/PushableBridge.h
Normal file
16
TombEngine/Objects/Generic/Object/Pushable/PushableBridge.h
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
struct ItemInfo;
|
||||||
|
|
||||||
|
namespace TEN::Entities::Generic
|
||||||
|
{
|
||||||
|
std::optional<int> PushableBridgeFloor(int itemNumber, int x, int y, int z);
|
||||||
|
std::optional<int> PushableBridgeCeiling(int itemNumber, int x, int y, int z);
|
||||||
|
int PushableBridgeFloorBorder(int itemNumber);
|
||||||
|
int PushableBridgeCeilingBorder(int itemNumber);
|
||||||
|
|
||||||
|
void AddPushableBridge(ItemInfo& pushableItem);
|
||||||
|
void AddPushableStackBridge(ItemInfo& pushableItem, bool addBridge);
|
||||||
|
void RemovePushableBridge(ItemInfo& pushableItem);
|
||||||
|
void UpdatePushableBridge(const ItemInfo& pushableItem);
|
||||||
|
}
|
382
TombEngine/Objects/Generic/Object/Pushable/PushableCollision.cpp
Normal file
382
TombEngine/Objects/Generic/Object/Pushable/PushableCollision.cpp
Normal file
|
@ -0,0 +1,382 @@
|
||||||
|
#include "framework.h"
|
||||||
|
#include "Objects/Generic/Object/Pushable/PushableCollision.h"
|
||||||
|
|
||||||
|
#include "Game/collision/collide_item.h"
|
||||||
|
#include "Game/collision/floordata.h"
|
||||||
|
#include "Game/Lara/lara.h"
|
||||||
|
#include "Game/Setup.h"
|
||||||
|
#include "Objects/Generic/Object/Pushable/PushableBridge.h"
|
||||||
|
#include "Objects/Generic/Object/Pushable/PushableObject.h"
|
||||||
|
#include "Objects/Generic/Object/Pushable/PushableStack.h"
|
||||||
|
#include "Specific/Input/Input.h"
|
||||||
|
#include "Specific/level.h"
|
||||||
|
|
||||||
|
using namespace TEN::Collision::Floordata;
|
||||||
|
using namespace TEN::Input;
|
||||||
|
|
||||||
|
namespace TEN::Entities::Generic
|
||||||
|
{
|
||||||
|
constexpr auto PUSHABLE_FLOOR_HEIGHT_TOLERANCE = 32;
|
||||||
|
|
||||||
|
bool IsPushableValid(ItemInfo& pushableItem)
|
||||||
|
{
|
||||||
|
auto& pushable = GetPushableInfo(pushableItem);
|
||||||
|
|
||||||
|
// NOTE: Requires positive OCB value for interaction.
|
||||||
|
if (pushableItem.Status == ITEM_INVISIBLE || pushableItem.TriggerFlags < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
auto pointColl = CollisionResult{};
|
||||||
|
if (pushable.UseRoomCollision)
|
||||||
|
{
|
||||||
|
RemovePushableBridge(pushableItem);
|
||||||
|
pointColl = GetCollision(&pushableItem);
|
||||||
|
AddPushableBridge(pushableItem);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pointColl = GetCollision(&pushableItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1) Check for wall.
|
||||||
|
if (pointColl.Block->IsWall(pushableItem.Pose.Position.x, pushableItem.Pose.Position.z))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// 2) Test height from floor.
|
||||||
|
int heightFromFloor = abs(pointColl.Position.Floor - pushableItem.Pose.Position.y);
|
||||||
|
if (heightFromFloor >= PUSHABLE_FLOOR_HEIGHT_TOLERANCE)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// 3) Test height from player. Prevents player from grabbing pushable at wrong elevation in stack.
|
||||||
|
int heightFromPlayer = abs(LaraItem->Pose.Position.y - pushableItem.Pose.Position.y);
|
||||||
|
if (heightFromPlayer >= PUSHABLE_FLOOR_HEIGHT_TOLERANCE)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsPushableObjectMoveAllowed(ItemInfo& pushableItem, const Vector3i& targetPos, int targetRoomNumber)
|
||||||
|
{
|
||||||
|
auto& pushable = GetPushableInfo(pushableItem);
|
||||||
|
|
||||||
|
pushable.IsOnEdge = false;
|
||||||
|
|
||||||
|
auto pointColl = GetCollision(targetPos, targetRoomNumber);
|
||||||
|
|
||||||
|
// 1) Check for wall.
|
||||||
|
if (pointColl.Block->IsWall(targetPos.x, targetPos.z))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// 2) Check for gaps or steps.
|
||||||
|
int heightFromFloor = abs(pointColl.Position.Floor - pushableItem.Pose.Position.y);
|
||||||
|
if (heightFromFloor >= PUSHABLE_FLOOR_HEIGHT_TOLERANCE)
|
||||||
|
{
|
||||||
|
// Step.
|
||||||
|
if (pointColl.Position.Floor < pushableItem.Pose.Position.y)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Gap.
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pushable.IsOnEdge = true;
|
||||||
|
if (!pushable.CanFall || pushable.Stack.ItemNumberAbove != NO_ITEM)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3) Check for slippery floor slope.
|
||||||
|
if (pointColl.Position.FloorSlope)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// 4) Check for diagonal floor step.
|
||||||
|
if (pointColl.Position.DiagonalStep)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// 5) Test floor slope. TODO: Check slope angles of normals directly.
|
||||||
|
if ((pointColl.Block->GetSurfaceSlope(0, true) != Vector2::Zero) || (pointColl.Block->GetSurfaceSlope(1, true) != Vector2::Zero))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Check for stopper flag.
|
||||||
|
/*if (collisionResult.Block->Stopper)
|
||||||
|
{
|
||||||
|
if (collisionResult.Position.Floor <= pushableItem.Pose.Position.y)
|
||||||
|
return false;
|
||||||
|
}*/
|
||||||
|
|
||||||
|
// Is ceiling (square or diagonal) high enough?
|
||||||
|
int distFromCeil = abs(pointColl.Position.Ceiling - pointColl.Position.Floor);
|
||||||
|
int blockHeight = GetStackHeight(pushableItem.Index) - PUSHABLE_FLOOR_HEIGHT_TOLERANCE;
|
||||||
|
if (distFromCeil < blockHeight)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// TODO: Don't modify item.
|
||||||
|
// Test object collision.
|
||||||
|
auto prevPos = pushableItem.Pose.Position;
|
||||||
|
pushableItem.Pose.Position = targetPos;
|
||||||
|
GetCollidedObjects(&pushableItem, BLOCK(0.25f), true, &CollidedItems[0], &CollidedMeshes[0], true);
|
||||||
|
pushableItem.Pose.Position = prevPos;
|
||||||
|
|
||||||
|
if (CollidedMeshes[0])
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (int i = 0; i < MAX_COLLIDED_OBJECTS; i++)
|
||||||
|
{
|
||||||
|
if (!CollidedItems[i])
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (Objects[CollidedItems[i]->ObjectNumber].isPickup)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (Objects[CollidedItems[i]->ObjectNumber].floor == nullptr) //??
|
||||||
|
return false;
|
||||||
|
|
||||||
|
auto& object = Objects[CollidedItems[i]->ObjectNumber];
|
||||||
|
int collidedIndex = CollidedItems[i] - g_Level.Items.data(); // Index of CollidedItems[i].
|
||||||
|
|
||||||
|
auto colPos = CollidedItems[i]->Pose.Position;
|
||||||
|
|
||||||
|
// Check if floor function returns nullopt.
|
||||||
|
if (object.floor(collidedIndex, colPos.x, colPos.y, colPos.z) == std::nullopt)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsValidForPlayer(const ItemInfo& pushableItem)
|
||||||
|
{
|
||||||
|
auto playerOffset = Vector3i::Zero;
|
||||||
|
|
||||||
|
int quadrant = GetQuadrant(LaraItem->Pose.Orientation.y);
|
||||||
|
switch (quadrant)
|
||||||
|
{
|
||||||
|
case NORTH:
|
||||||
|
playerOffset.z = GetBestFrame(*LaraItem).Offset.z - BLOCK(1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EAST:
|
||||||
|
playerOffset.x = GetBestFrame(*LaraItem).Offset.z - BLOCK(1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SOUTH:
|
||||||
|
playerOffset.z = -GetBestFrame(*LaraItem).Offset.z + BLOCK(1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case WEST:
|
||||||
|
playerOffset.x = -GetBestFrame(*LaraItem).Offset.z + BLOCK(1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto laraDetectionPos = LaraItem->Pose.Position + playerOffset;
|
||||||
|
auto pointColl = GetCollision(laraDetectionPos.x, laraDetectionPos.y, laraDetectionPos.z, LaraItem->RoomNumber);
|
||||||
|
|
||||||
|
//This collisionResult is the point where Lara would be at the end of the pushable pull.
|
||||||
|
|
||||||
|
// If is a wall
|
||||||
|
if (pointColl.Block->IsWall(laraDetectionPos.x, laraDetectionPos.z))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// If floor is not flat
|
||||||
|
int floorDifference = abs(pointColl.Position.Floor - LaraItem->Pose.Position.y);
|
||||||
|
if (floorDifference >= PUSHABLE_FLOOR_HEIGHT_TOLERANCE)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Is ceiling (square or diagonal) high enough?
|
||||||
|
int distanceToCeiling = abs(pointColl.Position.Ceiling - pointColl.Position.Floor);
|
||||||
|
if (distanceToCeiling < LARA_HEIGHT)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Is there any enemy or object?
|
||||||
|
auto prevPos = LaraItem->Pose.Position;
|
||||||
|
LaraItem->Pose.Position = laraDetectionPos;
|
||||||
|
GetCollidedObjects(LaraItem, LARA_RADIUS, true, &CollidedItems[0], &CollidedMeshes[0], true);
|
||||||
|
LaraItem->Pose.Position = prevPos;
|
||||||
|
|
||||||
|
if (CollidedMeshes[0])
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (int i = 0; i < MAX_COLLIDED_OBJECTS; i++)
|
||||||
|
{
|
||||||
|
if (!CollidedItems[i])
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (CollidedItems[i] == &pushableItem)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (Objects[CollidedItems[i]->ObjectNumber].isPickup)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (Objects[CollidedItems[i]->ObjectNumber].floor == nullptr)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const auto& object = Objects[CollidedItems[i]->ObjectNumber];
|
||||||
|
int collidedItemNumber = CollidedItems[i] - g_Level.Items.data();
|
||||||
|
|
||||||
|
auto pos = CollidedItems[i]->Pose.Position;
|
||||||
|
if (object.floor(collidedItemNumber, pos.x, pos.y, pos.z) == std::nullopt)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PushableIdleConditions(ItemInfo& pushableItem)
|
||||||
|
{
|
||||||
|
// If player is grabbing, check the push pull actions.
|
||||||
|
if (LaraItem->Animation.ActiveState != LS_PUSHABLE_GRAB ||
|
||||||
|
!TestLastFrame(LaraItem, LA_PUSHABLE_GRAB))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hasPushAction = IsHeld(In::Forward);
|
||||||
|
bool hasPullAction = IsHeld(In::Back);
|
||||||
|
|
||||||
|
//Cond 1: Is pressing Forward or Back?
|
||||||
|
if (!hasPushAction && !hasPullAction)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
//Cond 2: Can do the interaction with that side of the pushable?
|
||||||
|
auto& pushable = GetPushableInfo(pushableItem);
|
||||||
|
|
||||||
|
int quadrant = GetQuadrant(LaraItem->Pose.Orientation.y);
|
||||||
|
auto& pushableSidesAttributes = pushable.EdgeAttribs[quadrant]; //0 North, 1 East, 2 South or 3 West.
|
||||||
|
|
||||||
|
if ((hasPushAction && !pushableSidesAttributes.IsPushable) ||
|
||||||
|
(hasPullAction && !pushableSidesAttributes.IsPullable))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Cond 3: Is its stacked pushables under the limit?
|
||||||
|
if (!IsWithinStackLimit(pushableItem.Index))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
//Cond 4: Does it comply with the room collision conditions?.
|
||||||
|
if (!PushableMovementConditions(pushableItem, hasPushAction, hasPullAction))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PushableMovementConditions(ItemInfo& pushableItem, bool hasPushAction, bool hasPullAction)
|
||||||
|
{
|
||||||
|
const auto& pushable = GetPushableInfo(pushableItem);
|
||||||
|
|
||||||
|
int quadrant = GetQuadrant(LaraItem->Pose.Orientation.y);
|
||||||
|
auto targetPos = pushableItem.Pose.Position;
|
||||||
|
|
||||||
|
int dir = (hasPushAction) ? 1 : -1;
|
||||||
|
|
||||||
|
switch (quadrant)
|
||||||
|
{
|
||||||
|
case NORTH:
|
||||||
|
targetPos.z = targetPos.z + dir * BLOCK(1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EAST:
|
||||||
|
targetPos.x = targetPos.x + dir * BLOCK(1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SOUTH:
|
||||||
|
targetPos.z = targetPos.z - dir * BLOCK(1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case WEST:
|
||||||
|
targetPos.x = targetPos.x - dir * BLOCK(1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto targetRoom = pushableItem.RoomNumber;
|
||||||
|
|
||||||
|
if (!IsPushableObjectMoveAllowed(pushableItem, targetPos, targetRoom))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (hasPullAction && !IsValidForPlayer(pushableItem))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
SetPushableStopperFlag(true, targetPos, targetRoom);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
PushableCollisionData GetPushableCollision(ItemInfo& item)
|
||||||
|
{
|
||||||
|
auto& pushable = GetPushableInfo(item);
|
||||||
|
|
||||||
|
auto pointColl = CollisionResult{};
|
||||||
|
int waterHeight = NO_HEIGHT;
|
||||||
|
|
||||||
|
if (pushable.UseBridgeCollision)
|
||||||
|
{
|
||||||
|
RemovePushableBridge(item);
|
||||||
|
|
||||||
|
pointColl = GetCollision(item);
|
||||||
|
waterHeight = GetWaterSurface(item.Pose.Position.x, item.Pose.Position.y, item.Pose.Position.z, item.RoomNumber);
|
||||||
|
|
||||||
|
AddPushableBridge(item);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pointColl = GetCollision(item);
|
||||||
|
waterHeight = GetWaterSurface(item.Pose.Position.x, item.Pose.Position.y, item.Pose.Position.z, item.RoomNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto pushableColl = PushableCollisionData{};
|
||||||
|
pushableColl.FloorHeight = pointColl.Position.Floor;
|
||||||
|
pushableColl.CeilingHeight = pointColl.Position.Ceiling;
|
||||||
|
|
||||||
|
// Above water.
|
||||||
|
if (TestEnvironment(ENV_FLAG_WATER, item.RoomNumber))
|
||||||
|
{
|
||||||
|
pushable.WaterSurfaceHeight = waterHeight;
|
||||||
|
|
||||||
|
if (pushableColl.FloorHeight > (item.Pose.Position.y + item.Animation.Velocity.y) &&
|
||||||
|
abs(item.Pose.Position.y - pushableColl.FloorHeight) >= PUSHABLE_FLOOR_HEIGHT_TOLERANCE)
|
||||||
|
{
|
||||||
|
pushableColl.EnvType = PushableEnvironmentType::Water;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pushableColl.EnvType = PushableEnvironmentType::WaterFloor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Above dry ground.
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pushable.WaterSurfaceHeight = NO_HEIGHT;
|
||||||
|
|
||||||
|
// Airborne.
|
||||||
|
if (pushableColl.FloorHeight > (item.Pose.Position.y + item.Animation.Velocity.y) &&
|
||||||
|
abs(item.Pose.Position.y - pushableColl.FloorHeight) >= PUSHABLE_FLOOR_HEIGHT_TOLERANCE)
|
||||||
|
{
|
||||||
|
pushableColl.EnvType = PushableEnvironmentType::Air;
|
||||||
|
}
|
||||||
|
// Grounded.
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto floorNormal = GetSurfaceNormal(pointColl.FloorTilt, true);
|
||||||
|
auto floorSlopeAngle = Geometry::GetSurfaceSlopeAngle(floorNormal);
|
||||||
|
|
||||||
|
if (floorSlopeAngle == 0)
|
||||||
|
{
|
||||||
|
pushableColl.EnvType = PushableEnvironmentType::FlatFloor;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pushableColl.EnvType = PushableEnvironmentType::SlopedFloor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pushableColl;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
class Vector3i;
|
||||||
|
struct ItemInfo;
|
||||||
|
|
||||||
|
namespace TEN::Entities::Generic
|
||||||
|
{
|
||||||
|
enum class PushableEnvironmentType
|
||||||
|
{
|
||||||
|
Air,
|
||||||
|
FlatFloor,
|
||||||
|
SlopedFloor,
|
||||||
|
Water,
|
||||||
|
WaterFloor
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PushableCollisionData
|
||||||
|
{
|
||||||
|
PushableEnvironmentType EnvType = PushableEnvironmentType::Air;
|
||||||
|
|
||||||
|
int FloorHeight = 0;
|
||||||
|
int CeilingHeight = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool IsPushableValid(ItemInfo& pushableItem);
|
||||||
|
bool IsPushableObjectMoveAllowed(ItemInfo& pushableItem, const Vector3i& targetPos, int targetRoomNumber);
|
||||||
|
bool IsValidForPlayer(const ItemInfo& pushableItem);
|
||||||
|
|
||||||
|
bool PushableIdleConditions(ItemInfo& pushableItem);
|
||||||
|
bool PushableMovementConditions(ItemInfo& pushableItem, bool hasPushAction, bool hasPullAction);
|
||||||
|
|
||||||
|
PushableCollisionData GetPushableCollision(ItemInfo& item);
|
||||||
|
}
|
148
TombEngine/Objects/Generic/Object/Pushable/PushableEffects.cpp
Normal file
148
TombEngine/Objects/Generic/Object/Pushable/PushableEffects.cpp
Normal file
|
@ -0,0 +1,148 @@
|
||||||
|
#include "framework.h"
|
||||||
|
#include "Objects/Generic/Object/Pushable/PushableEffects.h"
|
||||||
|
|
||||||
|
#include "Game/effects/Bubble.h"
|
||||||
|
#include "Game/effects/effects.h"
|
||||||
|
#include "Game/effects/Ripple.h"
|
||||||
|
#include "Game/Setup.h"
|
||||||
|
#include "Objects/Generic/Object/Pushable/PushableObject.h"
|
||||||
|
|
||||||
|
using namespace TEN::Effects::Bubble;
|
||||||
|
using namespace TEN::Effects::Ripple;
|
||||||
|
|
||||||
|
namespace TEN::Entities::Generic
|
||||||
|
{
|
||||||
|
void HandlePushableRippleEffect(ItemInfo& pushableItem)
|
||||||
|
{
|
||||||
|
constexpr auto FRAMES_BETWEEN_RIPPLES = 8;
|
||||||
|
constexpr auto FRAMES_BETWEEN_RIPPLES_SOUNDS = 30;
|
||||||
|
|
||||||
|
auto& pushable = GetPushableInfo(pushableItem);
|
||||||
|
|
||||||
|
// TODO: cleanup.
|
||||||
|
// TODO: Enhace the effect to make the ripples increase their size through the time.
|
||||||
|
if (pushable.WaterSurfaceHeight != NO_HEIGHT)
|
||||||
|
{
|
||||||
|
if (fmod(GameTimer, FRAMES_BETWEEN_RIPPLES) <= 0.0f)
|
||||||
|
SpawnRipple(
|
||||||
|
Vector3(pushableItem.Pose.Position.x, pushable.WaterSurfaceHeight, pushableItem.Pose.Position.z),
|
||||||
|
pushableItem.RoomNumber,
|
||||||
|
GameBoundingBox(&pushableItem).GetWidth() + (GetRandomControl() & 15),
|
||||||
|
(int)RippleFlags::SlowFade | (int)RippleFlags::LowOpacity);
|
||||||
|
|
||||||
|
if (fmod(GameTimer, FRAMES_BETWEEN_RIPPLES_SOUNDS) <= 0.0f)
|
||||||
|
pushable.SoundState = PushableSoundState::Wade;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpawnPushableSplash(ItemInfo& pushableItem)
|
||||||
|
{
|
||||||
|
auto& pushable = GetPushableInfo(pushableItem);
|
||||||
|
|
||||||
|
SplashSetup.y = pushable.WaterSurfaceHeight - 1;
|
||||||
|
SplashSetup.x = pushableItem.Pose.Position.x;
|
||||||
|
SplashSetup.z = pushableItem.Pose.Position.z;
|
||||||
|
SplashSetup.splashPower = pushableItem.Animation.Velocity.y * 2;
|
||||||
|
SplashSetup.innerRadius = 250;
|
||||||
|
|
||||||
|
SetupSplash(&SplashSetup, pushableItem.RoomNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpawnPushableBubbles(const ItemInfo& pushableItem)
|
||||||
|
{
|
||||||
|
constexpr auto FRAMES_BETWEEN_BUBBLES = 8.0f;
|
||||||
|
|
||||||
|
if (fmod(GameTimer, FRAMES_BETWEEN_BUBBLES) <= 0.0f)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 32; i++)
|
||||||
|
{
|
||||||
|
auto pos = Vector3(
|
||||||
|
(GetRandomControl() & 0x1FF) + pushableItem.Pose.Position.x - 256,
|
||||||
|
(GetRandomControl() & 0x7F) + pushableItem.Pose.Position.y - 64,
|
||||||
|
(GetRandomControl() & 0x1FF) + pushableItem.Pose.Position.z - 256);
|
||||||
|
SpawnBubble(pos, pushableItem.RoomNumber, (int)BubbleFlags::HighAmplitude | (int)BubbleFlags::LargeScale);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandlePushableOscillation(ItemInfo& pushableItem)
|
||||||
|
{
|
||||||
|
constexpr auto BOX_VOLUME_MIN = BLOCK(0.5f);
|
||||||
|
|
||||||
|
const auto& pushable = GetPushableInfo(pushableItem);
|
||||||
|
|
||||||
|
auto time = GameTimer + pushableItem.Animation.Velocity.y;
|
||||||
|
|
||||||
|
// Calculate bounding box volume scaling factor.
|
||||||
|
auto bounds = GameBoundingBox(&pushableItem);
|
||||||
|
float boxVolume = bounds.GetWidth() * bounds.GetDepth() * bounds.GetHeight();
|
||||||
|
float boxScale = std::sqrt(std::min(BOX_VOLUME_MIN, boxVolume)) / 32.0f;
|
||||||
|
boxScale *= pushable.Oscillation;
|
||||||
|
|
||||||
|
float xOsc = (std::sin(time * 0.05f) * 0.5f) * boxScale;
|
||||||
|
float zOsc = (std::sin(time * 0.1f) * 0.75f) * boxScale;
|
||||||
|
|
||||||
|
short xAngle = ANGLE(xOsc * 20.0f);
|
||||||
|
short zAngle = ANGLE(zOsc * 20.0f);
|
||||||
|
pushableItem.Pose.Orientation = EulerAngles(xAngle, pushableItem.Pose.Orientation.y, zAngle);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandlePushableBridgeOscillation(ItemInfo& pushableItem)
|
||||||
|
{
|
||||||
|
constexpr auto BOX_VOLUME_MIN = BLOCK(0.5f);
|
||||||
|
|
||||||
|
const auto& pushable = GetPushableInfo(pushableItem);
|
||||||
|
auto time = GameTimer + pushableItem.Animation.Velocity.y;
|
||||||
|
|
||||||
|
// Calculate bounding box volume scaling factor.
|
||||||
|
auto bounds = GameBoundingBox(&pushableItem);
|
||||||
|
float boxVolume = bounds.GetWidth() * bounds.GetDepth() * bounds.GetHeight();
|
||||||
|
float boxScale = std::sqrt(std::min(BOX_VOLUME_MIN, boxVolume)) / 32.0f;
|
||||||
|
boxScale *= pushable.Oscillation;
|
||||||
|
|
||||||
|
// Vertical oscillation.
|
||||||
|
float verticalOsc = (std::sin(time * 0.2f) * 0.5f) * boxScale * 32;
|
||||||
|
short verticalTranslation = (short)verticalOsc;
|
||||||
|
|
||||||
|
pushableItem.Pose.Position.y += verticalTranslation;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandlePushableFallRotation(ItemInfo& item)
|
||||||
|
{
|
||||||
|
auto& pushableItem = item;
|
||||||
|
|
||||||
|
// Check if orientation is outside threeshold.
|
||||||
|
short orientThreshold = 1;
|
||||||
|
float correctionStep = 40.0f / 30; // 40 deg / 30 frames to do the correction in 1 sec approx.
|
||||||
|
|
||||||
|
if (abs(pushableItem.Pose.Orientation.x) >= orientThreshold ||
|
||||||
|
abs(pushableItem.Pose.Orientation.y) >= orientThreshold)
|
||||||
|
{
|
||||||
|
if (pushableItem.Pose.Orientation.x > 0)
|
||||||
|
{
|
||||||
|
pushableItem.Pose.Orientation.x -= correctionStep;
|
||||||
|
if (pushableItem.Pose.Orientation.x < 0)
|
||||||
|
pushableItem.Pose.Orientation.x = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pushableItem.Pose.Orientation.x += correctionStep;
|
||||||
|
if (pushableItem.Pose.Orientation.x > 0)
|
||||||
|
pushableItem.Pose.Orientation.x = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pushableItem.Pose.Orientation.z > 0)
|
||||||
|
{
|
||||||
|
pushableItem.Pose.Orientation.z -= correctionStep;
|
||||||
|
if (pushableItem.Pose.Orientation.z < 0)
|
||||||
|
pushableItem.Pose.Orientation.z = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pushableItem.Pose.Orientation.z += correctionStep;
|
||||||
|
if (pushableItem.Pose.Orientation.z > 0)
|
||||||
|
pushableItem.Pose.Orientation.z = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
15
TombEngine/Objects/Generic/Object/Pushable/PushableEffects.h
Normal file
15
TombEngine/Objects/Generic/Object/Pushable/PushableEffects.h
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
struct ItemInfo;
|
||||||
|
|
||||||
|
namespace TEN::Entities::Generic
|
||||||
|
{
|
||||||
|
void HandlePushableRippleEffect(ItemInfo& pushableItem);
|
||||||
|
void SpawnPushableSplash(ItemInfo& pushableItem);
|
||||||
|
void SpawnPushableBubbles(const ItemInfo& pushableItem);
|
||||||
|
|
||||||
|
// TODO: Move. Not effects.
|
||||||
|
void HandlePushableOscillation(ItemInfo& pushableItem);
|
||||||
|
void HandlePushableBridgeOscillation(ItemInfo& pushableItem);
|
||||||
|
void HandlePushableFallRotation(ItemInfo& pushableItem);
|
||||||
|
}
|
60
TombEngine/Objects/Generic/Object/Pushable/PushableInfo.cpp
Normal file
60
TombEngine/Objects/Generic/Object/Pushable/PushableInfo.cpp
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
#include "framework.h"
|
||||||
|
#include "Objects/Generic/Object/Pushable/PushableInfo.h"
|
||||||
|
|
||||||
|
#include "Game/control/control.h"
|
||||||
|
#include "Math/Math.h"
|
||||||
|
|
||||||
|
using namespace TEN::Math;
|
||||||
|
|
||||||
|
namespace TEN::Entities::Generic
|
||||||
|
{
|
||||||
|
const auto PUSHABLE_EDGE_ATTRIBS_DEFAULT = PushableEdgeAttribs(true, true, false);
|
||||||
|
|
||||||
|
PushableEdgeAttribs::PushableEdgeAttribs()
|
||||||
|
{
|
||||||
|
*this = PUSHABLE_EDGE_ATTRIBS_DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
PushableEdgeAttribs::PushableEdgeAttribs(bool isPullable, bool isPushable, bool isClimbable)
|
||||||
|
{
|
||||||
|
IsPullable = isPullable;
|
||||||
|
IsPushable = isPushable;
|
||||||
|
IsClimbable = isClimbable;
|
||||||
|
}
|
||||||
|
|
||||||
|
PushableInfo::PushableInfo()
|
||||||
|
{
|
||||||
|
constexpr auto DEFAULT_HEIGHT = BLOCK(1);
|
||||||
|
constexpr auto DEFAULT_GRAVITY = 8.0f;
|
||||||
|
constexpr auto DEFAULT_OSC = 0.75f;
|
||||||
|
constexpr auto DEFAULT_STACK_LIMIT = 3;
|
||||||
|
|
||||||
|
BehaviorState = PushableBehaviourState::Idle;
|
||||||
|
SoundState = PushableSoundState::None;
|
||||||
|
AnimSetID = 0;
|
||||||
|
|
||||||
|
Height = DEFAULT_HEIGHT;
|
||||||
|
WaterSurfaceHeight = NO_HEIGHT;
|
||||||
|
IsOnEdge = false;
|
||||||
|
Gravity = DEFAULT_GRAVITY;
|
||||||
|
Oscillation = DEFAULT_OSC;
|
||||||
|
|
||||||
|
Stack.Limit = DEFAULT_STACK_LIMIT;
|
||||||
|
Stack.ItemNumberAbove = NO_ITEM;
|
||||||
|
Stack.ItemNumberBelow = NO_ITEM;
|
||||||
|
|
||||||
|
CanFall = false;
|
||||||
|
DoCenterAlign = true;
|
||||||
|
IsBuoyant = false;
|
||||||
|
UseRoomCollision = false;
|
||||||
|
UseBridgeCollision = false;
|
||||||
|
|
||||||
|
EdgeAttribs =
|
||||||
|
{
|
||||||
|
{ CardinalDirection::NORTH, PushableEdgeAttribs() },
|
||||||
|
{ CardinalDirection::EAST, PushableEdgeAttribs() },
|
||||||
|
{ CardinalDirection::SOUTH, PushableEdgeAttribs() },
|
||||||
|
{ CardinalDirection::WEST, PushableEdgeAttribs() }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
59
TombEngine/Objects/Generic/Object/Pushable/PushableInfo.h
Normal file
59
TombEngine/Objects/Generic/Object/Pushable/PushableInfo.h
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
#pragma once
|
||||||
|
#include "Math/Math.h"
|
||||||
|
#include "Objects/Generic/Object/Pushable/PushableStates.h"
|
||||||
|
|
||||||
|
using namespace TEN::Math;
|
||||||
|
|
||||||
|
namespace TEN::Entities::Generic
|
||||||
|
{
|
||||||
|
enum class PushableSoundState
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
Move,
|
||||||
|
Stop,
|
||||||
|
Fall,
|
||||||
|
Wade
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PushableEdgeAttribs
|
||||||
|
{
|
||||||
|
bool IsPullable = false;
|
||||||
|
bool IsPushable = false;
|
||||||
|
bool IsClimbable = false;
|
||||||
|
|
||||||
|
PushableEdgeAttribs();
|
||||||
|
PushableEdgeAttribs(bool isPullable, bool isPushable, bool isClimbable);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PushableStackData
|
||||||
|
{
|
||||||
|
int Limit = 0; // Max number of pushables in stack that can be pushed.
|
||||||
|
int ItemNumberAbove = 0;
|
||||||
|
int ItemNumberBelow = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PushableInfo
|
||||||
|
{
|
||||||
|
PushableBehaviourState BehaviorState = PushableBehaviourState::Idle;
|
||||||
|
PushableSoundState SoundState = PushableSoundState::None;
|
||||||
|
int AnimSetID = 0;
|
||||||
|
|
||||||
|
GameVector StartPos = GameVector::Zero; // ?? XZ used by movement code. Y used to store water height level.
|
||||||
|
PushableStackData Stack = {};
|
||||||
|
|
||||||
|
int Height = 0;
|
||||||
|
int WaterSurfaceHeight = 0; // Used for spawning effects.
|
||||||
|
bool IsOnEdge = false;
|
||||||
|
float Gravity = 0.0f;
|
||||||
|
float Oscillation = 0.0f; // Used when floating on water surface. Recomended range: (0.0f, 2.0f].
|
||||||
|
|
||||||
|
bool CanFall = false; // OCB 0.
|
||||||
|
bool DoCenterAlign = false; // OCB 1.
|
||||||
|
bool IsBuoyant = false; // OCB 2.
|
||||||
|
bool UseRoomCollision = false;
|
||||||
|
bool UseBridgeCollision = false;
|
||||||
|
std::map<int, PushableEdgeAttribs> EdgeAttribs = {};
|
||||||
|
|
||||||
|
PushableInfo();
|
||||||
|
};
|
||||||
|
}
|
233
TombEngine/Objects/Generic/Object/Pushable/PushableObject.cpp
Normal file
233
TombEngine/Objects/Generic/Object/Pushable/PushableObject.cpp
Normal file
|
@ -0,0 +1,233 @@
|
||||||
|
#include "framework.h"
|
||||||
|
#include "Objects/Generic/Object/Pushable/PushableObject.h"
|
||||||
|
|
||||||
|
#include "Game/animation.h"
|
||||||
|
#include "Game/collision/collide_item.h"
|
||||||
|
#include "Game/collision/collide_room.h"
|
||||||
|
#include "Game/collision/floordata.h"
|
||||||
|
#include "Game/control/box.h"
|
||||||
|
#include "Game/control/flipeffect.h"
|
||||||
|
#include "Game/items.h"
|
||||||
|
#include "Game/Lara/lara.h"
|
||||||
|
#include "Game/Lara/lara_helpers.h"
|
||||||
|
#include "Game/Setup.h"
|
||||||
|
#include "Objects/Generic/Object/Pushable/PushableBridge.h"
|
||||||
|
#include "Objects/Generic/Object/Pushable/PushableCollision.h"
|
||||||
|
#include "Objects/Generic/Object/Pushable/PushableInfo.h"
|
||||||
|
#include "Objects/Generic/Object/Pushable/PushableSound.h"
|
||||||
|
#include "Objects/Generic/Object/Pushable/PushableStack.h"
|
||||||
|
#include "Objects/Generic/Object/Pushable/PushableStates.h"
|
||||||
|
#include "Sound/sound.h"
|
||||||
|
#include "Specific/Input/Input.h"
|
||||||
|
#include "Specific/level.h"
|
||||||
|
|
||||||
|
using namespace TEN::Collision::Floordata;
|
||||||
|
using namespace TEN::Input;
|
||||||
|
|
||||||
|
namespace TEN::Entities::Generic
|
||||||
|
{
|
||||||
|
auto PushableBlockPos = Vector3i::Zero;
|
||||||
|
|
||||||
|
auto PushableBlockBounds = ObjectCollisionBounds
|
||||||
|
{
|
||||||
|
GameBoundingBox(
|
||||||
|
0, 0,
|
||||||
|
-CLICK(0.25f), 0,
|
||||||
|
0, 0),
|
||||||
|
std::pair(
|
||||||
|
EulerAngles(ANGLE(-10.0f), -LARA_GRAB_THRESHOLD, ANGLE(-10.0f)),
|
||||||
|
EulerAngles(ANGLE(10.0f), LARA_GRAB_THRESHOLD, ANGLE(10.0f)))
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<PushableAnimSet> PushableAnimSets =
|
||||||
|
{
|
||||||
|
// Pushable object (TR4-5).
|
||||||
|
{ LA_PUSHABLE_OBJECT_PULL, LA_PUSHABLE_OBJECT_PUSH, LA_PUSHABLE_OBJECT_PUSH_EDGE_SLIP, true },
|
||||||
|
|
||||||
|
// Pushable block (TR1-3).
|
||||||
|
{ LA_PUSHABLE_BLOCK_PULL, LA_PUSHABLE_BLOCK_PUSH, LA_PUSHABLE_BLOCK_PUSH_EDGE_SLIP, false }
|
||||||
|
};
|
||||||
|
|
||||||
|
PushableInfo& GetPushableInfo(const ItemInfo& item)
|
||||||
|
{
|
||||||
|
return (PushableInfo&)item.Data;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InitializePushableBlock(int itemNumber)
|
||||||
|
{
|
||||||
|
auto& pushableItem = g_Level.Items[itemNumber];
|
||||||
|
if (pushableItem.Data == NULL) // First pushableItem in initialize.
|
||||||
|
InitializePushableStacks();
|
||||||
|
|
||||||
|
auto& pushable = GetPushableInfo(pushableItem);
|
||||||
|
|
||||||
|
pushable.StartPos = GameVector(pushableItem.Pose.Position, pushableItem.RoomNumber);
|
||||||
|
pushable.Height = GetPushableHeight(pushableItem);
|
||||||
|
|
||||||
|
if (pushableItem.ObjectNumber >= ID_PUSHABLE_OBJECT_CLIMBABLE1 &&
|
||||||
|
pushableItem.ObjectNumber <= ID_PUSHABLE_OBJECT_CLIMBABLE10)
|
||||||
|
{
|
||||||
|
pushable.UseRoomCollision = true;
|
||||||
|
AddPushableBridge(pushableItem);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pushable.UseRoomCollision = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
SetPushableStopperFlag(true, pushableItem.Pose.Position, pushableItem.RoomNumber);
|
||||||
|
|
||||||
|
// Read OCB flags.
|
||||||
|
int ocb = pushableItem.TriggerFlags;
|
||||||
|
pushable.CanFall = (ocb & (1 << 0)) != 0; // Bit 0.
|
||||||
|
pushable.DoCenterAlign = (ocb & (1 << 1)) != 1; // Bit 1.
|
||||||
|
pushable.IsBuoyant = (ocb & (1 << 2)) != 0; // Bit 2.
|
||||||
|
pushable.AnimSetID = ((ocb & (1 << 3)) != 0) ? 1 : 0; // Bit 3.
|
||||||
|
|
||||||
|
pushableItem.Status = ITEM_ACTIVE;
|
||||||
|
AddActiveItem(itemNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PushableBlockControl(int itemNumber)
|
||||||
|
{
|
||||||
|
auto& pushableItem = g_Level.Items[itemNumber];
|
||||||
|
auto& pushable = GetPushableInfo(pushableItem);
|
||||||
|
|
||||||
|
if (Lara.Context.InteractedItem == itemNumber && Lara.Control.IsMoving)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Handle behaviour and sound state.
|
||||||
|
HandlePushableBehaviorState(pushableItem);
|
||||||
|
HandlePushableSoundState(pushableItem);
|
||||||
|
|
||||||
|
// Update bridge.
|
||||||
|
AddPushableStackBridge(pushableItem, false);
|
||||||
|
int probeRoomNumber = GetCollision(&pushableItem).RoomNumber;
|
||||||
|
AddPushableStackBridge(pushableItem, true);
|
||||||
|
|
||||||
|
// Update room number.
|
||||||
|
if (pushableItem.RoomNumber != probeRoomNumber)
|
||||||
|
{
|
||||||
|
ItemNewRoom(itemNumber, probeRoomNumber);
|
||||||
|
pushable.StartPos.RoomNumber = pushableItem.RoomNumber;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If player is holding Action, initiates object interaction.
|
||||||
|
// Otherwise, activates normal object collision.
|
||||||
|
void PushableBlockCollision(int itemNumber, ItemInfo* playerItem, CollisionInfo* coll)
|
||||||
|
{
|
||||||
|
auto& pushableItem = g_Level.Items[itemNumber];
|
||||||
|
auto& pushable = GetPushableInfo(pushableItem);
|
||||||
|
auto& player = *GetLaraInfo(playerItem);
|
||||||
|
|
||||||
|
int quadrant = GetQuadrant(LaraItem->Pose.Orientation.y);
|
||||||
|
auto& pushableSidesAttributes = pushable.EdgeAttribs[quadrant]; // NOTE: 0 = north, 1 = east, 2 = south, 3 = west.
|
||||||
|
|
||||||
|
// Align player to pushable.
|
||||||
|
if ((IsHeld(In::Action) &&
|
||||||
|
!IsHeld(In::Forward) &&
|
||||||
|
playerItem->Animation.ActiveState == LS_IDLE &&
|
||||||
|
playerItem->Animation.AnimNumber == LA_STAND_IDLE &&
|
||||||
|
!playerItem->Animation.IsAirborne &&
|
||||||
|
player.Control.HandStatus == HandStatus::Free &&
|
||||||
|
IsPushableValid(pushableItem)) &&
|
||||||
|
pushable.BehaviorState == PushableBehaviourState::Idle &&
|
||||||
|
(pushableSidesAttributes.IsPushable || pushableSidesAttributes.IsPullable) || // Can interact with this side.
|
||||||
|
(player.Control.IsMoving && player.Context.InteractedItem == itemNumber)) // Already interacting.
|
||||||
|
{
|
||||||
|
// Set pushable bounds.
|
||||||
|
auto bounds = GameBoundingBox(&pushableItem);
|
||||||
|
PushableBlockBounds.BoundingBox.X1 = (bounds.X1 / 2) - 100;
|
||||||
|
PushableBlockBounds.BoundingBox.X2 = (bounds.X2 / 2) + 100;
|
||||||
|
PushableBlockBounds.BoundingBox.Z1 = bounds.Z1 - 200;
|
||||||
|
PushableBlockBounds.BoundingBox.Z2 = 0;
|
||||||
|
|
||||||
|
short yOrient = pushableItem.Pose.Orientation.y;
|
||||||
|
pushableItem.Pose.Orientation.y = GetQuadrant(playerItem->Pose.Orientation.y) * ANGLE(90.0f);
|
||||||
|
|
||||||
|
// Within interaction range, calculate target position for alignment.
|
||||||
|
if (TestLaraPosition(PushableBlockBounds, &pushableItem, playerItem))
|
||||||
|
{
|
||||||
|
int quadrant = GetQuadrant(pushableItem.Pose.Orientation.y);
|
||||||
|
switch (quadrant)
|
||||||
|
{
|
||||||
|
case NORTH:
|
||||||
|
PushableBlockPos.z = bounds.Z1 - CLICK(0.4f);
|
||||||
|
PushableBlockPos.x = pushable.DoCenterAlign ? 0 : LaraItem->Pose.Position.x - pushableItem.Pose.Position.x;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SOUTH:
|
||||||
|
PushableBlockPos.z = (bounds.Z2 + CLICK(0.4f)) * (-1);
|
||||||
|
PushableBlockPos.x = pushable.DoCenterAlign ? 0 : pushableItem.Pose.Position.x - LaraItem->Pose.Position.x;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EAST:
|
||||||
|
PushableBlockPos.z = bounds.X1 - CLICK(0.4f);
|
||||||
|
PushableBlockPos.x = pushable.DoCenterAlign ? 0 : pushableItem.Pose.Position.z - LaraItem->Pose.Position.z;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case WEST:
|
||||||
|
PushableBlockPos.z = (bounds.X2 + CLICK(0.4f)) * (- 1);
|
||||||
|
PushableBlockPos.x = pushable.DoCenterAlign ? 0 : LaraItem->Pose.Position.z - pushableItem.Pose.Position.z;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Align player.
|
||||||
|
if (MoveLaraPosition(PushableBlockPos, &pushableItem, playerItem))
|
||||||
|
{
|
||||||
|
SetAnimation(playerItem, LA_PUSHABLE_GRAB);
|
||||||
|
playerItem->Pose.Orientation = pushableItem.Pose.Orientation;
|
||||||
|
player.Control.IsMoving = false;
|
||||||
|
player.Control.HandStatus = HandStatus::Busy;
|
||||||
|
player.Context.NextCornerPos.Position.x = itemNumber; // TODO: Do this differently.
|
||||||
|
}
|
||||||
|
|
||||||
|
player.Context.InteractedItem = itemNumber;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (player.Control.IsMoving && player.Context.InteractedItem == itemNumber)
|
||||||
|
{
|
||||||
|
player.Control.IsMoving = false;
|
||||||
|
player.Control.HandStatus = HandStatus::Free;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pushableItem.Pose.Orientation.y = yOrient;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Not holding Action; do normal collision routine.
|
||||||
|
if (playerItem->Animation.ActiveState != LS_PUSHABLE_GRAB ||
|
||||||
|
!TestLastFrame(playerItem, LA_PUSHABLE_GRAB) ||
|
||||||
|
player.Context.NextCornerPos.Position.x != itemNumber)
|
||||||
|
{
|
||||||
|
// NOTE: If using room collision, use bridge collision.
|
||||||
|
if (!pushable.UseRoomCollision)
|
||||||
|
ObjectCollision(itemNumber, playerItem, coll);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int GetPushableHeight(ItemInfo& item)
|
||||||
|
{
|
||||||
|
int heightBoundingBox = -GameBoundingBox(&item).Y1;
|
||||||
|
int heightWorldAligned = (heightBoundingBox / CLICK(0.5)) * CLICK(0.5);
|
||||||
|
return heightWorldAligned;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetPushableStopperFlag(bool isStopper, const Vector3i& pos, int roomNumber)
|
||||||
|
{
|
||||||
|
auto pointColl = GetCollision(pos, roomNumber);
|
||||||
|
pointColl.Block->Stopper = isStopper;
|
||||||
|
|
||||||
|
// TODO: There is a problem, it also has to set/reset the flag in the flipped room.
|
||||||
|
// Because when flipmaps happens, it forgets about the old flag.
|
||||||
|
}
|
||||||
|
}
|
40
TombEngine/Objects/Generic/Object/Pushable/PushableObject.h
Normal file
40
TombEngine/Objects/Generic/Object/Pushable/PushableObject.h
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
#pragma once
|
||||||
|
#include "Game/Lara/lara_struct.h"
|
||||||
|
|
||||||
|
// TODO: Merge with Info.h.
|
||||||
|
|
||||||
|
struct CollisionInfo;
|
||||||
|
struct ItemInfo;
|
||||||
|
|
||||||
|
namespace TEN::Entities::Generic
|
||||||
|
{
|
||||||
|
struct PushableInfo;
|
||||||
|
|
||||||
|
struct PushableAnimSet
|
||||||
|
{
|
||||||
|
int PullAnimNumber = 0;
|
||||||
|
int PushAnimNumber = 0;
|
||||||
|
int EdgeAnimNumber = 0;
|
||||||
|
bool EnableAnimLoop = 0;
|
||||||
|
|
||||||
|
PushableAnimSet(int pullAnimNumber, int pushAnimNumber, int edgeAnimNumber, bool enableAnimLoop)
|
||||||
|
{
|
||||||
|
PullAnimNumber = pullAnimNumber;
|
||||||
|
PushAnimNumber = pushAnimNumber;
|
||||||
|
EdgeAnimNumber = edgeAnimNumber;
|
||||||
|
EnableAnimLoop = enableAnimLoop;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
extern std::vector<PushableAnimSet> PushableAnimSets;
|
||||||
|
|
||||||
|
PushableInfo& GetPushableInfo(const ItemInfo& item);
|
||||||
|
|
||||||
|
void InitializePushableBlock(int itemNumber);
|
||||||
|
void PushableBlockControl(int itemNumber);
|
||||||
|
void PushableBlockCollision(int itemNumber, ItemInfo* playerItem, CollisionInfo* coll);
|
||||||
|
|
||||||
|
int GetPushableHeight(ItemInfo& item);
|
||||||
|
|
||||||
|
void SetPushableStopperFlag(bool isStopper, const Vector3i& pos, int roomNumber);
|
||||||
|
}
|
124
TombEngine/Objects/Generic/Object/Pushable/PushableSound.cpp
Normal file
124
TombEngine/Objects/Generic/Object/Pushable/PushableSound.cpp
Normal file
|
@ -0,0 +1,124 @@
|
||||||
|
#include "framework.h"
|
||||||
|
#include "Objects/Generic/Object/Pushable/PushableSound.h"
|
||||||
|
|
||||||
|
#include "Objects/Generic/Object/Pushable/PushableObject.h"
|
||||||
|
#include "Sound/sound.h"
|
||||||
|
#include "Specific/level.h"
|
||||||
|
|
||||||
|
namespace TEN::Entities::Generic
|
||||||
|
{
|
||||||
|
enum PushableSoundType
|
||||||
|
{
|
||||||
|
Loop,
|
||||||
|
Stop,
|
||||||
|
Fall
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PushableSoundData
|
||||||
|
{
|
||||||
|
int LoopSoundID = 0;
|
||||||
|
int StopSoundID = 0;
|
||||||
|
int FallImpactSoundID = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
static PushableSoundData GetPushableSoundData(MaterialType material)
|
||||||
|
{
|
||||||
|
static const auto DEFAULT_SOUND_DATA = PushableSoundData
|
||||||
|
{
|
||||||
|
SFX_TR4_PUSHABLE_SOUND, SFX_TR4_PUSH_BLOCK_END, SFX_TR4_BOULDER_FALL
|
||||||
|
};
|
||||||
|
|
||||||
|
static const auto SOUND_DATA_MAP = std::unordered_map<MaterialType, PushableSoundData>
|
||||||
|
{
|
||||||
|
{ MaterialType::Mud, PushableSoundData{ SFX_TEN_PUSHABLES_MOVE_MUD, SFX_TEN_PUSHABLES_STOP_MUD, SFX_TEN_PUSHABLES_COLLIDE_MUD } },
|
||||||
|
{ MaterialType::Snow, PushableSoundData{ SFX_TEN_PUSHABLES_MOVE_SNOW, SFX_TEN_PUSHABLES_STOP_SNOW, SFX_TEN_PUSHABLES_COLLIDE_SNOW } },
|
||||||
|
{ MaterialType::Sand, PushableSoundData{ SFX_TEN_PUSHABLES_MOVE_SAND, SFX_TEN_PUSHABLES_STOP_SAND, SFX_TEN_PUSHABLES_COLLIDE_SAND } },
|
||||||
|
{ MaterialType::Gravel, PushableSoundData{ SFX_TEN_PUSHABLES_MOVE_GRAVEL, SFX_TEN_PUSHABLES_STOP_GRAVEL, SFX_TEN_PUSHABLES_COLLIDE_GRAVEL } },
|
||||||
|
{ MaterialType::Ice, PushableSoundData{ SFX_TEN_PUSHABLES_MOVE_ICE, SFX_TEN_PUSHABLES_STOP_ICE, SFX_TEN_PUSHABLES_COLLIDE_ICE } },
|
||||||
|
{ MaterialType::Water, PushableSoundData{ SFX_TEN_PUSHABLES_MOVE_WATER, SFX_TEN_PUSHABLES_STOP_WATER, SFX_TEN_PUSHABLES_COLLIDE_WATER } },
|
||||||
|
{ MaterialType::Stone, PushableSoundData{ SFX_TEN_PUSHABLES_STOP_MOVE_STONE, SFX_TEN_PUSHABLES_STOP_STONE, SFX_TEN_PUSHABLES_COLLIDE_STONE } },
|
||||||
|
{ MaterialType::Wood, PushableSoundData{ SFX_TEN_PUSHABLES_MOVE_WOOD, SFX_TEN_PUSHABLES_STOP_WOOD, SFX_TEN_PUSHABLES_COLLIDE_WOOD } },
|
||||||
|
{ MaterialType::Metal, PushableSoundData{ SFX_TEN_PUSHABLES_MOVE_METAL, SFX_TEN_PUSHABLES_STOP_METAL, SFX_TEN_PUSHABLES_COLLIDE_METAL } },
|
||||||
|
{ MaterialType::Marble, PushableSoundData{ SFX_TEN_PUSHABLES_MOVE_MARBLE, SFX_TEN_PUSHABLES_STOP_MARBLE, SFX_TEN_PUSHABLES_COLLIDE_MARBLE } },
|
||||||
|
{ MaterialType::Grass, PushableSoundData{ SFX_TEN_PUSHABLES_MOVE_GRASS, SFX_TEN_PUSHABLES_STOP_GRASS, SFX_TEN_PUSHABLES_COLLIDE_GRASS } },
|
||||||
|
{ MaterialType::Concrete, PushableSoundData{ SFX_TEN_PUSHABLES_MOVE_CONCRETE, SFX_TEN_PUSHABLES_STOP_CONCRETE, SFX_TEN_PUSHABLES_COLLIDE_CONCRETE } },
|
||||||
|
{ MaterialType::OldWood, PushableSoundData{ SFX_TR4_PUSHABLE_SOUND, SFX_TR4_PUSH_BLOCK_END, SFX_TR4_BOULDER_FALL } },
|
||||||
|
{ MaterialType::OldMetal, PushableSoundData{ SFX_TR4_PUSHABLE_SOUND, SFX_TR4_PUSH_BLOCK_END, SFX_TR4_BOULDER_FALL } },
|
||||||
|
{ MaterialType::Custom1, PushableSoundData{ SFX_TR4_PUSHABLE_SOUND, SFX_TR4_PUSH_BLOCK_END, SFX_TR4_BOULDER_FALL } },
|
||||||
|
{ MaterialType::Custom2, PushableSoundData{ SFX_TR4_PUSHABLE_SOUND, SFX_TR4_PUSH_BLOCK_END, SFX_TR4_BOULDER_FALL } },
|
||||||
|
{ MaterialType::Custom3, PushableSoundData{ SFX_TR4_PUSHABLE_SOUND, SFX_TR4_PUSH_BLOCK_END, SFX_TR4_BOULDER_FALL } },
|
||||||
|
{ MaterialType::Custom4, PushableSoundData{ SFX_TR4_PUSHABLE_SOUND, SFX_TR4_PUSH_BLOCK_END, SFX_TR4_BOULDER_FALL } },
|
||||||
|
{ MaterialType::Custom5, PushableSoundData{ SFX_TR4_PUSHABLE_SOUND, SFX_TR4_PUSH_BLOCK_END, SFX_TR4_BOULDER_FALL } },
|
||||||
|
{ MaterialType::Custom6, PushableSoundData{ SFX_TR4_PUSHABLE_SOUND, SFX_TR4_PUSH_BLOCK_END, SFX_TR4_BOULDER_FALL } },
|
||||||
|
{ MaterialType::Custom7, PushableSoundData{ SFX_TR4_PUSHABLE_SOUND, SFX_TR4_PUSH_BLOCK_END, SFX_TR4_BOULDER_FALL } },
|
||||||
|
{ MaterialType::Custom8, PushableSoundData{ SFX_TR4_PUSHABLE_SOUND, SFX_TR4_PUSH_BLOCK_END, SFX_TR4_BOULDER_FALL } }
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: Uncomment when we get actual sounds for them.
|
||||||
|
//auto it = SOUND_DATA_MAP.find(material);
|
||||||
|
//return ((it != SOUND_DATA_MAP.end()) ? it->second : SOUND_DATA_DEFAULT);
|
||||||
|
|
||||||
|
return DEFAULT_SOUND_DATA;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::optional<int> GetPushableSoundID(const ItemInfo& pushableItem, PushableSoundType soundType)
|
||||||
|
{
|
||||||
|
// Get floor material.
|
||||||
|
auto pointColl = GetCollision(pushableItem);
|
||||||
|
auto material = pointColl.BottomBlock->Material;
|
||||||
|
|
||||||
|
switch (soundType)
|
||||||
|
{
|
||||||
|
case PushableSoundType::Loop:
|
||||||
|
return GetPushableSoundData(material).LoopSoundID;
|
||||||
|
|
||||||
|
case PushableSoundType::Stop:
|
||||||
|
return GetPushableSoundData(material).StopSoundID;
|
||||||
|
|
||||||
|
case PushableSoundType::Fall:
|
||||||
|
return GetPushableSoundData(material).FallImpactSoundID;
|
||||||
|
|
||||||
|
default:
|
||||||
|
TENLog("Missing pushable sound.", LogLevel::Error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandlePushableSoundState(ItemInfo& pushableItem)
|
||||||
|
{
|
||||||
|
auto& pushable = GetPushableInfo(pushableItem);
|
||||||
|
|
||||||
|
auto soundID = std::optional<int>(std::nullopt);
|
||||||
|
switch (pushable.SoundState)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
case PushableSoundState::None:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PushableSoundState::Move:
|
||||||
|
soundID = GetPushableSoundID(pushableItem, PushableSoundType::Loop);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PushableSoundState::Stop:
|
||||||
|
soundID = GetPushableSoundID(pushableItem, PushableSoundType::Stop);
|
||||||
|
pushable.SoundState = PushableSoundState::None;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PushableSoundState::Fall:
|
||||||
|
soundID = GetPushableSoundID(pushableItem, PushableSoundType::Fall);
|
||||||
|
pushable.SoundState = PushableSoundState::None;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PushableSoundState::Wade:
|
||||||
|
soundID = SFX_TR4_LARA_WADE;
|
||||||
|
pushable.SoundState = PushableSoundState::None;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!soundID.has_value())
|
||||||
|
return;
|
||||||
|
|
||||||
|
SoundEffect(*soundID, &pushableItem.Pose, SoundEnvironment::Always);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
struct ItemInfo;
|
||||||
|
|
||||||
|
namespace TEN::Entities::Generic
|
||||||
|
{
|
||||||
|
void HandlePushableSoundState(ItemInfo& pushableItem);
|
||||||
|
}
|
324
TombEngine/Objects/Generic/Object/Pushable/PushableStack.cpp
Normal file
324
TombEngine/Objects/Generic/Object/Pushable/PushableStack.cpp
Normal file
|
@ -0,0 +1,324 @@
|
||||||
|
#include "framework.h"
|
||||||
|
#include "Objects/Generic/Object/Pushable/PushableStack.h"
|
||||||
|
|
||||||
|
#include "Game/collision/floordata.h"
|
||||||
|
#include "Game/Setup.h"
|
||||||
|
#include "Objects/Generic/Object/Pushable/PushableBridge.h"
|
||||||
|
#include "Objects/Generic/Object/Pushable/PushableObject.h"
|
||||||
|
#include "Specific/level.h"
|
||||||
|
|
||||||
|
using namespace TEN::Collision::Floordata;
|
||||||
|
|
||||||
|
namespace TEN::Entities::Generic
|
||||||
|
{
|
||||||
|
// NOTE: Required to use Vector3i in std::unordered_map.
|
||||||
|
struct Vector3iHasher
|
||||||
|
{
|
||||||
|
std::size_t operator()(const Vector3i& vector) const
|
||||||
|
{
|
||||||
|
std::size_t h1 = std::hash<int>()(vector.x);
|
||||||
|
std::size_t h2 = std::hash<int>()(vector.y);
|
||||||
|
std::size_t h3 = std::hash<int>()(vector.z);
|
||||||
|
return (h1 ^ (h2 << 1) ^ (h3 << 2));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void InitializePushableStacks()
|
||||||
|
{
|
||||||
|
// 1) Collect all pushables in level.
|
||||||
|
auto& pushableItemNumbers = FindAllPushables(g_Level.Items);
|
||||||
|
if (pushableItemNumbers.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// 2) Prepare data to create several lists per each stack group (one for each XZ and ground floor height).
|
||||||
|
// PROBLEM: Missing hash function in Vector3i, creating custom version Vector3iHasher at the top of this source code.
|
||||||
|
std::unordered_map < Vector3i, std::vector<int>, Vector3iHasher> stackGroups; // stack Position - pushable itemNumber
|
||||||
|
|
||||||
|
// 3) Iterate through the pushables list, to put them in their different stack groups. According to their XZ and ground floor height).
|
||||||
|
//Extra, I moved also the .Data initialization here, to can store data in all the pushables objects (even the ones not initialized yet).
|
||||||
|
for (int itemNumber : pushableItemNumbers)
|
||||||
|
{
|
||||||
|
auto& pushableItem = g_Level.Items[itemNumber];
|
||||||
|
pushableItem.Data = PushableInfo();
|
||||||
|
|
||||||
|
int x = pushableItem.Pose.Position.x;
|
||||||
|
int z = pushableItem.Pose.Position.z;
|
||||||
|
|
||||||
|
auto pointColl = GetCollision(&pushableItem);
|
||||||
|
int y = pointColl.Position.Floor;
|
||||||
|
|
||||||
|
stackGroups.emplace(Vector3i(x, y, z), std::vector<int>()).first->second.push_back(itemNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4) Iterate through stack groups lists, sort each by vertical position, and iterate to make stack links.
|
||||||
|
for (auto& group : stackGroups)
|
||||||
|
{
|
||||||
|
auto& pushablesInGroup = group.second;
|
||||||
|
|
||||||
|
// If only 1 pushable in that position, no stack check required.
|
||||||
|
if (pushablesInGroup.size() <= 1)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// If 2 or more pushables in group, sort from bottom to top (highest Y to Lowest Y).
|
||||||
|
std::sort(pushablesInGroup.begin(), pushablesInGroup.end(), [](int pushableItemNumber0, int pushableItemNumber1)
|
||||||
|
{
|
||||||
|
const auto& pushable0 = g_Level.Items[pushableItemNumber0];
|
||||||
|
const auto& pushable1 = g_Level.Items[pushableItemNumber1];
|
||||||
|
|
||||||
|
return (pushable0.Pose.Position.y > pushable1.Pose.Position.y);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Iterate through each group to set the stack links).
|
||||||
|
for (int i = 0; i < (pushablesInGroup.size() - 1); ++i)
|
||||||
|
{
|
||||||
|
// Set stackUpperItem and stackLowerItem variables accordingly.
|
||||||
|
int lowerItemNumber = pushablesInGroup[i];
|
||||||
|
auto& lowerPushableItem = g_Level.Items[lowerItemNumber];
|
||||||
|
auto& lowerPushable = GetPushableInfo(lowerPushableItem);
|
||||||
|
|
||||||
|
int upperItemNumber = pushablesInGroup[i + 1];
|
||||||
|
auto& upperPushableItem = g_Level.Items[upperItemNumber];
|
||||||
|
auto& upperPushable = GetPushableInfo(upperPushableItem);
|
||||||
|
|
||||||
|
lowerPushable.Stack.ItemNumberAbove = upperItemNumber;
|
||||||
|
upperPushable.Stack.ItemNumberBelow = lowerItemNumber;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<int> FindAllPushables(const std::vector<ItemInfo>& items)
|
||||||
|
{
|
||||||
|
auto pushableItemNumbers = std::vector<int>{};
|
||||||
|
for (int i = 0; i < items.size(); i++)
|
||||||
|
{
|
||||||
|
auto& item = items[i];
|
||||||
|
|
||||||
|
if ((item.ObjectNumber >= ID_PUSHABLE_OBJECT1 && item.ObjectNumber <= ID_PUSHABLE_OBJECT10) ||
|
||||||
|
(item.ObjectNumber >= ID_PUSHABLE_OBJECT_CLIMBABLE1 && item.ObjectNumber <= ID_PUSHABLE_OBJECT_CLIMBABLE10))
|
||||||
|
{
|
||||||
|
pushableItemNumbers.push_back(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pushableItemNumbers;
|
||||||
|
}
|
||||||
|
|
||||||
|
void StackPushable(int itemNumber, int targetItemNumber)
|
||||||
|
{
|
||||||
|
if (targetItemNumber == NO_ITEM)
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto& pushableItem = g_Level.Items[itemNumber];
|
||||||
|
auto& pushable = GetPushableInfo(pushableItem);
|
||||||
|
|
||||||
|
pushable.Stack.ItemNumberBelow = targetItemNumber;
|
||||||
|
|
||||||
|
auto& lowerPushableItem = g_Level.Items[targetItemNumber];
|
||||||
|
auto& lowerPushable = GetPushableInfo(lowerPushableItem);
|
||||||
|
|
||||||
|
lowerPushable.Stack.ItemNumberAbove = itemNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UnstackPushable(int itemNumber)
|
||||||
|
{
|
||||||
|
auto& pushableItem = g_Level.Items[itemNumber];
|
||||||
|
auto& pushable = GetPushableInfo(pushableItem);
|
||||||
|
|
||||||
|
if (pushable.Stack.ItemNumberBelow == NO_ITEM)
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto& lowerPushableItem = g_Level.Items[pushable.Stack.ItemNumberBelow];
|
||||||
|
auto& lowerPushable = GetPushableInfo(lowerPushableItem);
|
||||||
|
|
||||||
|
pushable.Stack.ItemNumberBelow = NO_ITEM;
|
||||||
|
lowerPushable.Stack.ItemNumberAbove = NO_ITEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
int SearchNearPushablesStack(int itemNumber)
|
||||||
|
{
|
||||||
|
auto& pushableItem = g_Level.Items[itemNumber];
|
||||||
|
|
||||||
|
int pushabelStackFound = FindPushableStackInRoom(itemNumber, pushableItem.RoomNumber);
|
||||||
|
|
||||||
|
if (pushabelStackFound != NO_ITEM)
|
||||||
|
return pushabelStackFound;
|
||||||
|
|
||||||
|
// Otherwise, check room below.
|
||||||
|
//auto collisionResult = GetCollision(pushableItem.Pose.Position.x, pushableItem.Pose.Position.y, pushableItem.Pose.Position.z, pushableItem.RoomNumber);
|
||||||
|
//auto roomNumberBelow = collisionResult.Block->GetRoomNumberBelow(pushableItem.Pose.Position.x, pushableItem.Pose.Position.y, pushableItem.Pose.Position.z).value();
|
||||||
|
//pushabelStackFound = FindPushableStackInRoom(itemNumber, roomNumberBelow);
|
||||||
|
|
||||||
|
return NO_ITEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
int FindPushableStackInRoom(int itemNumber, int roomNumber)
|
||||||
|
{
|
||||||
|
auto& pushableItem = g_Level.Items[itemNumber];
|
||||||
|
auto& pushable = GetPushableInfo(pushableItem);
|
||||||
|
|
||||||
|
if (roomNumber != NO_ROOM)
|
||||||
|
{
|
||||||
|
short currentItemNumber = g_Level.Rooms[roomNumber].itemNumber;
|
||||||
|
while (currentItemNumber != NO_ITEM)
|
||||||
|
{
|
||||||
|
auto& currentItem = g_Level.Items[currentItemNumber];
|
||||||
|
|
||||||
|
// If climbable pushable, is in the same XZ position and is at a lower height.
|
||||||
|
if ((currentItem.ObjectNumber >= ID_PUSHABLE_OBJECT_CLIMBABLE1 && currentItem.ObjectNumber <= ID_PUSHABLE_OBJECT_CLIMBABLE10) &&
|
||||||
|
(currentItem.Pose.Position.x == pushableItem.Pose.Position.x) && (currentItem.Pose.Position.z == pushableItem.Pose.Position.z) &&
|
||||||
|
(currentItem.Pose.Position.y > pushableItem.Pose.Position.y))
|
||||||
|
{
|
||||||
|
// Find top item.
|
||||||
|
if (pushable.Stack.ItemNumberAbove == NO_ITEM)
|
||||||
|
{
|
||||||
|
return currentItemNumber;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int topItemNumber = pushable.Stack.ItemNumberAbove;
|
||||||
|
while (topItemNumber != NO_ITEM)
|
||||||
|
{
|
||||||
|
auto& topItem = g_Level.Items[topItemNumber];
|
||||||
|
auto& topPushable = GetPushableInfo(topItem);
|
||||||
|
|
||||||
|
if (topPushable.Stack.ItemNumberAbove == NO_ITEM)
|
||||||
|
{
|
||||||
|
return topItemNumber;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
topItemNumber = topPushable.Stack.ItemNumberAbove;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
currentItemNumber = currentItem.NextItem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NO_ITEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
int GetPushableCountInStack(int itemNumber)
|
||||||
|
{
|
||||||
|
auto pushableItemCopy = g_Level.Items[itemNumber];
|
||||||
|
auto& pushableCopy = GetPushableInfo(pushableItemCopy);
|
||||||
|
|
||||||
|
int count = 1;
|
||||||
|
while (pushableCopy.Stack.ItemNumberAbove != NO_ITEM)
|
||||||
|
{
|
||||||
|
// Filter out current pushable item.
|
||||||
|
if (pushableCopy.Stack.ItemNumberAbove == itemNumber)
|
||||||
|
break;
|
||||||
|
|
||||||
|
pushableItemCopy = g_Level.Items[pushableCopy.Stack.ItemNumberAbove];
|
||||||
|
pushableCopy = GetPushableInfo(pushableItemCopy);
|
||||||
|
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsWithinStackLimit(int itemNumber)
|
||||||
|
{
|
||||||
|
auto& pushableItem = g_Level.Items[itemNumber];
|
||||||
|
auto& pushable = GetPushableInfo(pushableItem);
|
||||||
|
|
||||||
|
auto count = GetPushableCountInStack(itemNumber);
|
||||||
|
return (count <= pushable.Stack.Limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
int GetStackHeight(int itemNumber)
|
||||||
|
{
|
||||||
|
auto pushableItemCopy = g_Level.Items[itemNumber];
|
||||||
|
auto& pushableCopy = GetPushableInfo(pushableItemCopy);
|
||||||
|
|
||||||
|
int totalHeight = pushableCopy.Height;
|
||||||
|
|
||||||
|
while (pushableCopy.Stack.ItemNumberAbove != NO_ITEM)
|
||||||
|
{
|
||||||
|
pushableItemCopy = g_Level.Items[pushableCopy.Stack.ItemNumberAbove];
|
||||||
|
pushableCopy = GetPushableInfo(pushableItemCopy);
|
||||||
|
|
||||||
|
totalHeight = totalHeight + pushableCopy.Height;
|
||||||
|
}
|
||||||
|
|
||||||
|
return totalHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
void StartMovePushableStack(int itemNumber)
|
||||||
|
{
|
||||||
|
auto& pushableItem = g_Level.Items[itemNumber];
|
||||||
|
auto& pushable = GetPushableInfo(pushableItem);
|
||||||
|
|
||||||
|
int currentItemNumber = pushable.Stack.ItemNumberAbove;
|
||||||
|
while (currentItemNumber != NO_ITEM)
|
||||||
|
{
|
||||||
|
auto& currentPushableItem = g_Level.Items[currentItemNumber];
|
||||||
|
auto& currentPushable = GetPushableInfo(currentPushableItem);
|
||||||
|
|
||||||
|
// Deactivate collision.
|
||||||
|
if (currentPushable.UseRoomCollision)
|
||||||
|
RemovePushableBridge(currentPushableItem);
|
||||||
|
|
||||||
|
currentPushable.BehaviorState = PushableBehaviourState::MoveStackHorizontal;
|
||||||
|
|
||||||
|
currentItemNumber = currentPushable.Stack.ItemNumberAbove;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void StopMovePushableStack(int itemNumber)
|
||||||
|
{
|
||||||
|
auto& pushableItem = g_Level.Items[itemNumber];
|
||||||
|
auto& pushable = GetPushableInfo(pushableItem);
|
||||||
|
|
||||||
|
int currentItemNumber = pushable.Stack.ItemNumberAbove;
|
||||||
|
while (currentItemNumber != NO_ITEM)
|
||||||
|
{
|
||||||
|
auto& currentPushableItem = g_Level.Items[currentItemNumber];
|
||||||
|
auto& currentPushable = GetPushableInfo(currentPushableItem);
|
||||||
|
|
||||||
|
currentPushableItem.Pose.Position = GetNearestSectorCenter(currentPushableItem.Pose.Position);
|
||||||
|
|
||||||
|
// Activate collision.
|
||||||
|
if (currentPushable.UseRoomCollision)
|
||||||
|
AddPushableBridge(currentPushableItem);
|
||||||
|
|
||||||
|
currentPushable.BehaviorState = PushableBehaviourState::Idle;
|
||||||
|
|
||||||
|
currentItemNumber = currentPushable.Stack.ItemNumberAbove;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void StartFallPushableStack(int itemNumber)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void StopFallPushableStack(int itemNumber)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Problems with bridge collision.
|
||||||
|
void SetPushableVerticalPos(const ItemInfo& pushableItem, int relHeight)
|
||||||
|
{
|
||||||
|
auto* pushableItemPtr = &g_Level.Items[pushableItem.Index];
|
||||||
|
const auto* pushablePtr = &GetPushableInfo(*pushableItemPtr);
|
||||||
|
|
||||||
|
while (pushablePtr->Stack.ItemNumberAbove != NO_ITEM)
|
||||||
|
{
|
||||||
|
if (pushablePtr->Stack.ItemNumberAbove == pushableItem.Index)
|
||||||
|
break;
|
||||||
|
|
||||||
|
pushableItemPtr->Pose.Position.y += relHeight;
|
||||||
|
|
||||||
|
pushableItemPtr = &g_Level.Items[pushablePtr->Stack.ItemNumberAbove];
|
||||||
|
pushablePtr = &GetPushableInfo(*pushableItemPtr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
28
TombEngine/Objects/Generic/Object/Pushable/PushableStack.h
Normal file
28
TombEngine/Objects/Generic/Object/Pushable/PushableStack.h
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
struct ItemInfo;
|
||||||
|
|
||||||
|
namespace TEN::Entities::Generic
|
||||||
|
{
|
||||||
|
void InitializePushableStacks();
|
||||||
|
std::vector<int> FindAllPushables(const std::vector<ItemInfo>& items);
|
||||||
|
|
||||||
|
void StackPushable(int itemNumber, int targetItemNumber);
|
||||||
|
void UnstackPushable(int itemNumber);
|
||||||
|
|
||||||
|
int SearchNearPushablesStack(int itemNumber);
|
||||||
|
int FindPushableStackInRoom(int itemNumber, int roomNumber);
|
||||||
|
|
||||||
|
int GetPushableCountInStack(int itemNumber);
|
||||||
|
bool IsWithinStackLimit(int itemNumber);
|
||||||
|
|
||||||
|
int GetStackHeight(int itemNumber);
|
||||||
|
|
||||||
|
void StartMovePushableStack(int itemNumber);
|
||||||
|
void StopMovePushableStack(int itemNumber);
|
||||||
|
|
||||||
|
void StartFallPushableStack(int itemNumber);
|
||||||
|
void StopFallPushableStack(int itemNumber);
|
||||||
|
|
||||||
|
void SetPushableVerticalPos(const ItemInfo& pushableItem, int deltaY);
|
||||||
|
}
|
901
TombEngine/Objects/Generic/Object/Pushable/PushableStates.cpp
Normal file
901
TombEngine/Objects/Generic/Object/Pushable/PushableStates.cpp
Normal file
|
@ -0,0 +1,901 @@
|
||||||
|
#include "framework.h"
|
||||||
|
#include "Objects/Generic/Object/Pushable/PushableStates.h"
|
||||||
|
|
||||||
|
#include "Game/animation.h"
|
||||||
|
#include "Game/control/flipeffect.h"
|
||||||
|
#include "Game/Lara/lara.h"
|
||||||
|
#include "Game/Lara/lara_helpers.h"
|
||||||
|
#include "Objects/Generic/Object/Pushable/PushableBridge.h"
|
||||||
|
#include "Objects/Generic/Object/Pushable/PushableCollision.h"
|
||||||
|
#include "Objects/Generic/Object/Pushable/PushableEffects.h"
|
||||||
|
#include "Objects/Generic/Object/Pushable/PushableObject.h"
|
||||||
|
#include "Objects/Generic/Object/Pushable/PushableStack.h"
|
||||||
|
#include "Specific/Input/Input.h"
|
||||||
|
#include "Specific/level.h"
|
||||||
|
|
||||||
|
using namespace TEN::Input;
|
||||||
|
|
||||||
|
namespace TEN::Entities::Generic
|
||||||
|
{
|
||||||
|
constexpr auto PUSHABLE_FALL_VELOCITY_MAX = BLOCK(1 / 8.0f);
|
||||||
|
constexpr auto PUSHABLE_WATER_VELOCITY_MAX = BLOCK(1 / 16.0f);
|
||||||
|
|
||||||
|
constexpr auto PUSHABLE_RUMBLE_FALL_VELOCITY = 96.0f;
|
||||||
|
constexpr auto PUSHABLE_EDGE_SLIP_VELOCITY = 0.8f;
|
||||||
|
|
||||||
|
constexpr auto PUSHABLE_GRAVITY_AIR = 8.0f;
|
||||||
|
constexpr auto PUSHABLE_GRAVITY_WATER = 4.0f;
|
||||||
|
constexpr auto PUSHABLE_GRAVITY_ACCEL = 0.5f;
|
||||||
|
|
||||||
|
constexpr auto PUSHABLE_WATER_SURFACE_DISTANCE = CLICK(0.5f);
|
||||||
|
|
||||||
|
static void HandleIdleState(ItemInfo& pushableItem)
|
||||||
|
{
|
||||||
|
auto& pushable = GetPushableInfo(pushableItem);
|
||||||
|
auto& playerItem = *LaraItem;
|
||||||
|
auto& player = GetLaraInfo(playerItem);
|
||||||
|
|
||||||
|
// 1) Check if player is interacting.
|
||||||
|
if (player.Context.InteractedItem == pushableItem.Index)
|
||||||
|
{
|
||||||
|
if (PushableIdleConditions(pushableItem))
|
||||||
|
{
|
||||||
|
// Pushing.
|
||||||
|
if (IsHeld(In::Forward))
|
||||||
|
{
|
||||||
|
int pushAnimNumber = (pushable.IsOnEdge) ?
|
||||||
|
PushableAnimSets[pushable.AnimSetID].EdgeAnimNumber :
|
||||||
|
PushableAnimSets[pushable.AnimSetID].PushAnimNumber;
|
||||||
|
SetAnimation(LaraItem, pushAnimNumber);
|
||||||
|
}
|
||||||
|
// Pulling.
|
||||||
|
else if (IsHeld(In::Back))
|
||||||
|
{
|
||||||
|
int pullAnimNumber = PushableAnimSets[pushable.AnimSetID].PullAnimNumber;
|
||||||
|
SetAnimation(LaraItem, pullAnimNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
pushable.StartPos = pushableItem.Pose.Position;
|
||||||
|
pushable.StartPos.RoomNumber = pushableItem.RoomNumber;
|
||||||
|
pushable.BehaviorState = PushableBehaviourState::Move;
|
||||||
|
|
||||||
|
// Unstack lower pushables.
|
||||||
|
UnstackPushable(pushableItem.Index);
|
||||||
|
|
||||||
|
// Prepare upper pushables in stack for movement.
|
||||||
|
StartMovePushableStack(pushableItem.Index);
|
||||||
|
|
||||||
|
ResetPlayerFlex(LaraItem);
|
||||||
|
RemovePushableBridge(pushableItem);
|
||||||
|
}
|
||||||
|
else if (playerItem.Animation.ActiveState != LS_PUSHABLE_GRAB &&
|
||||||
|
playerItem.Animation.ActiveState != LS_PUSHABLE_PULL &&
|
||||||
|
playerItem.Animation.ActiveState != LS_PUSHABLE_PUSH &&
|
||||||
|
playerItem.Animation.ActiveState != LS_PUSHABLE_EDGE_SLIP)
|
||||||
|
{
|
||||||
|
player.Context.InteractedItem = NO_ITEM;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get pushable collision.
|
||||||
|
auto pushableColl = GetPushableCollision(pushableItem);
|
||||||
|
|
||||||
|
switch (pushableColl.EnvType)
|
||||||
|
{
|
||||||
|
case PushableEnvironmentType::FlatFloor:
|
||||||
|
if (pushableColl.FloorHeight != pushableItem.Pose.Position.y)
|
||||||
|
{
|
||||||
|
pushableItem.Pose.Position.y = pushableColl.FloorHeight;
|
||||||
|
|
||||||
|
int relHeight = pushableColl.FloorHeight - pushableItem.Pose.Position.y;
|
||||||
|
SetPushableVerticalPos(pushableItem, relHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PushableEnvironmentType::WaterFloor:
|
||||||
|
{
|
||||||
|
if (pushableColl.FloorHeight != pushableItem.Pose.Position.y)
|
||||||
|
{
|
||||||
|
pushableItem.Pose.Position.y = pushableColl.FloorHeight;
|
||||||
|
|
||||||
|
int relHeight = pushableColl.FloorHeight - pushableItem.Pose.Position.y;
|
||||||
|
SetPushableVerticalPos(pushableItem, relHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
int waterheight = abs(pushableColl.FloorHeight - pushable.WaterSurfaceHeight);
|
||||||
|
if (waterheight > GetPushableHeight(pushableItem))
|
||||||
|
{
|
||||||
|
if (pushable.IsBuoyant && pushable.Stack.ItemNumberAbove == NO_ITEM)
|
||||||
|
{
|
||||||
|
pushable.BehaviorState = PushableBehaviourState::Float;
|
||||||
|
pushable.Gravity = 0.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HandlePushableRippleEffect(pushableItem);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PushableEnvironmentType::Air:
|
||||||
|
// Pass to fall state if distance to floor is beyond threshold.
|
||||||
|
if (abs(pushableColl.FloorHeight - pushableItem.Pose.Position.y) > CLICK(0.75f))
|
||||||
|
{
|
||||||
|
pushable.BehaviorState = PushableBehaviourState::Fall;
|
||||||
|
SetPushableStopperFlag(false, pushableItem.Pose.Position, pushableItem.RoomNumber);
|
||||||
|
|
||||||
|
RemovePushableBridge(pushableItem);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pushableItem.Pose.Position.y = pushableColl.FloorHeight;
|
||||||
|
|
||||||
|
int relHeight = pushableColl.FloorHeight - pushableItem.Pose.Position.y;
|
||||||
|
SetPushableVerticalPos(pushableItem, relHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PushableEnvironmentType::Water:
|
||||||
|
RemovePushableBridge(pushableItem);
|
||||||
|
SetPushableStopperFlag(false, pushableItem.Pose.Position, pushableItem.RoomNumber);
|
||||||
|
|
||||||
|
if (pushable.IsBuoyant && pushable.Stack.ItemNumberAbove == NO_ITEM)
|
||||||
|
{
|
||||||
|
pushable.BehaviorState = PushableBehaviourState::Float;
|
||||||
|
pushable.Gravity = 0.0f;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pushable.BehaviorState = PushableBehaviourState::Sink;
|
||||||
|
pushable.Gravity = PUSHABLE_GRAVITY_WATER;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
TENLog("Error handling pushable collision in idle state for pushable item " + std::to_string(pushableItem.Index), LogLevel::Warning, LogConfig::All);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void HandleMoveState(ItemInfo& pushableItem)
|
||||||
|
{
|
||||||
|
auto& pushable = GetPushableInfo(pushableItem);
|
||||||
|
auto& playerItem = *LaraItem;
|
||||||
|
auto& player = GetLaraInfo(playerItem);
|
||||||
|
|
||||||
|
bool isPlayerPulling = playerItem.Animation.ActiveState == LS_PUSHABLE_PULL;
|
||||||
|
|
||||||
|
int quadrant = GetQuadrant(playerItem.Pose.Orientation.y);
|
||||||
|
int newPosX = pushable.StartPos.x;
|
||||||
|
int newPosZ = pushable.StartPos.z;
|
||||||
|
|
||||||
|
int displaceDepth = 0;
|
||||||
|
int displaceBox = GameBoundingBox(LaraItem).Z2;
|
||||||
|
|
||||||
|
if (pushable.SoundState == PushableSoundState::Move)
|
||||||
|
pushable.SoundState = PushableSoundState::Stop;
|
||||||
|
|
||||||
|
displaceDepth = GetLastFrame(GAME_OBJECT_ID::ID_LARA, playerItem.Animation.AnimNumber)->BoundingBox.Z2;
|
||||||
|
|
||||||
|
if (isPlayerPulling)
|
||||||
|
{
|
||||||
|
displaceBox -= (displaceDepth + BLOCK(1));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
displaceBox -= displaceDepth - (pushable.IsOnEdge ? BLOCK(0.5f) : BLOCK(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Player is pushing or pulling.
|
||||||
|
if (playerItem.Animation.FrameNumber != (g_Level.Anims[playerItem.Animation.AnimNumber].frameEnd - 1))
|
||||||
|
{
|
||||||
|
// 1) Determine displacement.
|
||||||
|
switch (quadrant)
|
||||||
|
{
|
||||||
|
case NORTH:
|
||||||
|
newPosZ += displaceBox;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EAST:
|
||||||
|
newPosX += displaceBox;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SOUTH:
|
||||||
|
newPosZ -= displaceBox;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case WEST:
|
||||||
|
newPosX -= displaceBox;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Distance too far; return early. NOTE: May happen as animation bounds change.
|
||||||
|
if (abs(pushableItem.Pose.Position.z - newPosZ) > BLOCK(0.75f))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (abs(pushableItem.Pose.Position.x - newPosX) > BLOCK(0.75f))
|
||||||
|
return;
|
||||||
|
|
||||||
|
int travelledDist = Vector3i::Distance(pushableItem.Pose.Position, pushable.StartPos.ToVector3i());
|
||||||
|
if (pushable.IsOnEdge && travelledDist >= BLOCK(0.5f))
|
||||||
|
{
|
||||||
|
pushable.BehaviorState = PushableBehaviourState::EdgeSlip;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2) Move pushable.
|
||||||
|
// Move only if the movement direction aligns with push/pull action.
|
||||||
|
|
||||||
|
// Z axis.
|
||||||
|
if (isPlayerPulling)
|
||||||
|
{
|
||||||
|
if ((quadrant == NORTH && pushableItem.Pose.Position.z > newPosZ) ||
|
||||||
|
(quadrant == SOUTH && pushableItem.Pose.Position.z < newPosZ))
|
||||||
|
{
|
||||||
|
pushableItem.Pose.Position.z = newPosZ;
|
||||||
|
pushable.SoundState = PushableSoundState::Move;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ((quadrant == NORTH && pushableItem.Pose.Position.z < newPosZ) ||
|
||||||
|
(quadrant == SOUTH && pushableItem.Pose.Position.z > newPosZ))
|
||||||
|
{
|
||||||
|
pushableItem.Pose.Position.z = newPosZ;
|
||||||
|
pushable.SoundState = PushableSoundState::Move;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// X axis.
|
||||||
|
if (isPlayerPulling)
|
||||||
|
{
|
||||||
|
if ((quadrant == EAST && pushableItem.Pose.Position.x > newPosX) ||
|
||||||
|
(quadrant == WEST && pushableItem.Pose.Position.x < newPosX))
|
||||||
|
{
|
||||||
|
pushableItem.Pose.Position.x = newPosX;
|
||||||
|
pushable.SoundState = PushableSoundState::Move;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ((quadrant == EAST && pushableItem.Pose.Position.x < newPosX) ||
|
||||||
|
(quadrant == WEST && pushableItem.Pose.Position.x > newPosX))
|
||||||
|
{
|
||||||
|
pushableItem.Pose.Position.x = newPosX;
|
||||||
|
pushable.SoundState = PushableSoundState::Move;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pushable.WaterSurfaceHeight != NO_HEIGHT)
|
||||||
|
HandlePushableRippleEffect(pushableItem);
|
||||||
|
}
|
||||||
|
// Push/pull animation finished.
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 1) Realign with sector center.
|
||||||
|
pushableItem.Pose.Position = GetNearestSectorCenter(pushableItem.Pose.Position);
|
||||||
|
|
||||||
|
// 2) Connect to stack if applicable.
|
||||||
|
int foundStack = SearchNearPushablesStack(pushableItem.Index);
|
||||||
|
StackPushable(pushableItem.Index, foundStack);
|
||||||
|
SetPushableStopperFlag(false, pushable.StartPos.ToVector3i(), pushable.StartPos.RoomNumber);
|
||||||
|
|
||||||
|
pushable.StartPos = pushableItem.Pose.Position;
|
||||||
|
pushable.StartPos.RoomNumber = pushableItem.RoomNumber;
|
||||||
|
|
||||||
|
// Get pushable collision.
|
||||||
|
auto pushableColl = GetPushableCollision(pushableItem);
|
||||||
|
|
||||||
|
switch (pushableColl.EnvType)
|
||||||
|
{
|
||||||
|
case PushableEnvironmentType::FlatFloor:
|
||||||
|
case PushableEnvironmentType::WaterFloor:
|
||||||
|
// Activate trigger.
|
||||||
|
TestTriggers(&pushableItem, true, pushableItem.Flags & IFLAG_ACTIVATION_MASK);
|
||||||
|
|
||||||
|
// Check if push/pull must stop.
|
||||||
|
if (!PushableAnimSets[pushable.AnimSetID].EnableAnimLoop ||
|
||||||
|
!IsHeld(In::Action) ||
|
||||||
|
!PushableMovementConditions(pushableItem, !isPlayerPulling, isPlayerPulling) ||
|
||||||
|
!IsPushableValid(pushableItem))
|
||||||
|
{
|
||||||
|
playerItem.Animation.TargetState = LS_IDLE;
|
||||||
|
pushable.BehaviorState = PushableBehaviourState::Idle;
|
||||||
|
|
||||||
|
// Set upper pushables back to normal(?).
|
||||||
|
StopMovePushableStack(pushableItem.Index);
|
||||||
|
AddPushableBridge(pushableItem);
|
||||||
|
|
||||||
|
// Connect to another stack.
|
||||||
|
int foundStack = SearchNearPushablesStack(pushableItem.Index);
|
||||||
|
StackPushable(pushableItem.Index, foundStack);
|
||||||
|
|
||||||
|
pushable.SoundState = PushableSoundState::Stop;
|
||||||
|
}
|
||||||
|
else if (playerItem.Animation.ActiveState == LS_PUSHABLE_PUSH && pushable.IsOnEdge)
|
||||||
|
{
|
||||||
|
playerItem.Animation.TargetState = LS_PUSHABLE_EDGE_SLIP;
|
||||||
|
|
||||||
|
auto movementDir = pushableItem.Pose.Position.ToVector3() - playerItem.Pose.Position.ToVector3();
|
||||||
|
movementDir.Normalize();
|
||||||
|
playerItem.Pose.Position = playerItem.Pose.Position + movementDir * BLOCK(1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PushableEnvironmentType::Air:
|
||||||
|
pushable.BehaviorState = PushableBehaviourState::Fall;
|
||||||
|
pushable.SoundState = PushableSoundState::None;
|
||||||
|
playerItem.Animation.TargetState = LS_IDLE;
|
||||||
|
player.Context.InteractedItem = NO_ITEM;
|
||||||
|
return;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PushableEnvironmentType::SlopedFloor:
|
||||||
|
// TODO: If slippery slope, link to slide state.
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PushableEnvironmentType::Water:
|
||||||
|
pushable.BehaviorState = PushableBehaviourState::Sink;
|
||||||
|
pushable.SoundState = PushableSoundState::None;
|
||||||
|
playerItem.Animation.TargetState = LS_IDLE;
|
||||||
|
player.Context.InteractedItem = NO_ITEM;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
TENLog("Error handling pushable collision state in move state for pushable item number " + std::to_string(pushableItem.Index), LogLevel::Warning, LogConfig::All, false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void HandleEdgeSlipState(ItemInfo& pushableItem)
|
||||||
|
{
|
||||||
|
constexpr auto LEAN_ANGLE_MAX = ANGLE(40.0f);
|
||||||
|
|
||||||
|
auto& pushable = GetPushableInfo(pushableItem);
|
||||||
|
|
||||||
|
// Get pushable collision.
|
||||||
|
auto pushableColl = GetPushableCollision(pushableItem);
|
||||||
|
|
||||||
|
// Calculate movement direction.
|
||||||
|
auto moveDir = (pushableItem.Pose.Position - pushable.StartPos.ToVector3i()).ToVector3();
|
||||||
|
moveDir.y = 0.0f;
|
||||||
|
moveDir.Normalize();
|
||||||
|
|
||||||
|
// Define origin and target.
|
||||||
|
auto origin = Geometry::TranslatePoint(pushable.StartPos.ToVector3(), moveDir, BLOCK(0.5f));
|
||||||
|
auto target = Geometry::TranslatePoint(pushable.StartPos.ToVector3(), moveDir, BLOCK(1));
|
||||||
|
target.y = pushable.StartPos.y + BLOCK(1);
|
||||||
|
|
||||||
|
// Calculate current position based on interpolation.
|
||||||
|
auto currentPos = pushableItem.Pose.Position.ToVector3();
|
||||||
|
|
||||||
|
float& elapsedTime = pushableItem.Animation.Velocity.y;
|
||||||
|
float alpha = std::clamp(elapsedTime / PUSHABLE_EDGE_SLIP_VELOCITY, 0.0f, 1.0f);
|
||||||
|
|
||||||
|
// TODO: Try Lerp() instead.
|
||||||
|
currentPos = Vector3(
|
||||||
|
InterpolateCubic(origin.x, origin.x, target.x, target.x, alpha),
|
||||||
|
InterpolateCubic(origin.y, origin.y, target.y - 700, target.y, alpha),
|
||||||
|
InterpolateCubic(origin.z, origin.z, target.z, target.z, alpha));
|
||||||
|
|
||||||
|
// Calculate lean angle based on movement direction.
|
||||||
|
float leanAngle = LEAN_ANGLE_MAX * alpha;
|
||||||
|
|
||||||
|
if (currentPos.y > pushableColl.FloorHeight)
|
||||||
|
{
|
||||||
|
currentPos.y = pushableColl.FloorHeight;
|
||||||
|
pushableItem.Pose.Orientation = EulerAngles(0, pushableItem.Pose.Orientation.y, 0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Handle slip lean.
|
||||||
|
int pushableQuadrant = GetQuadrant(pushableItem.Pose.Orientation.y);
|
||||||
|
int movementQuadrant = GetQuadrant(FROM_RAD(atan2(moveDir.z, moveDir.x)));
|
||||||
|
|
||||||
|
movementQuadrant = (movementQuadrant + pushableQuadrant) % 4;
|
||||||
|
switch (movementQuadrant)
|
||||||
|
{
|
||||||
|
// TODO: Use CardinalDirection enum.
|
||||||
|
case 0: //EAST
|
||||||
|
pushableItem.Pose.Orientation = EulerAngles(0, pushableItem.Pose.Orientation.y, leanAngle);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 1: //NORTH
|
||||||
|
pushableItem.Pose.Orientation = EulerAngles(-leanAngle, pushableItem.Pose.Orientation.y, 0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2: //WEST
|
||||||
|
pushableItem.Pose.Orientation = EulerAngles(0, pushableItem.Pose.Orientation.y, -leanAngle);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 3: //SOUTH
|
||||||
|
pushableItem.Pose.Orientation = EulerAngles(leanAngle, pushableItem.Pose.Orientation.y, 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pushableItem.Pose.Position = currentPos;
|
||||||
|
elapsedTime += DELTA_TIME;
|
||||||
|
|
||||||
|
// Handle sounds.
|
||||||
|
if (alpha <= 0.5f)
|
||||||
|
{
|
||||||
|
pushable.SoundState = PushableSoundState::Move;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (pushable.SoundState == PushableSoundState::Move)
|
||||||
|
pushable.SoundState = PushableSoundState::Stop;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if movement is completed.
|
||||||
|
if (alpha >= 1.0f)
|
||||||
|
{
|
||||||
|
currentPos = GetNearestSectorCenter(pushableItem.Pose.Position).ToVector3();
|
||||||
|
|
||||||
|
switch (pushableColl.EnvType)
|
||||||
|
{
|
||||||
|
case PushableEnvironmentType::Air:
|
||||||
|
pushable.BehaviorState = PushableBehaviourState::Fall;
|
||||||
|
pushableItem.Animation.Velocity.y = PUSHABLE_FALL_VELOCITY_MAX / 2;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PushableEnvironmentType::FlatFloor:
|
||||||
|
case PushableEnvironmentType::WaterFloor:
|
||||||
|
pushableItem.Animation.Velocity.y = 0.0f;
|
||||||
|
pushableItem.Pose.Position.y = pushableColl.FloorHeight;
|
||||||
|
pushableItem.Pose.Orientation = EulerAngles(0, pushableItem.Pose.Orientation.y, 0);
|
||||||
|
pushable.BehaviorState = PushableBehaviourState::Idle;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PushableEnvironmentType::Water:
|
||||||
|
pushableItem.Animation.Velocity.y = PUSHABLE_WATER_VELOCITY_MAX / 2;
|
||||||
|
pushable.BehaviorState = PushableBehaviourState::Sink;
|
||||||
|
|
||||||
|
SpawnPushableSplash(pushableItem);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PushableEnvironmentType::SlopedFloor:
|
||||||
|
pushableItem.Animation.Velocity.y = 0.0f;
|
||||||
|
pushable.BehaviorState = PushableBehaviourState::Idle;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
TENLog("Error handling pushable collision in edge slip state for pushable item number " + std::to_string(pushableItem.Index), LogLevel::Warning, LogConfig::All);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void HandleFallState(ItemInfo& pushableItem)
|
||||||
|
{
|
||||||
|
auto& pushable = GetPushableInfo(pushableItem);
|
||||||
|
|
||||||
|
// Get pushable collision.
|
||||||
|
auto pushableColl = GetPushableCollision(pushableItem);
|
||||||
|
|
||||||
|
int foundStack = NO_ITEM;
|
||||||
|
|
||||||
|
switch (pushableColl.EnvType)
|
||||||
|
{
|
||||||
|
case PushableEnvironmentType::Air:
|
||||||
|
pushableItem.Pose.Position.y += pushableItem.Animation.Velocity.y;
|
||||||
|
pushableItem.Animation.Velocity.y = std::min(pushableItem.Animation.Velocity.y + pushable.Gravity, PUSHABLE_FALL_VELOCITY_MAX);
|
||||||
|
HandlePushableFallRotation(pushableItem);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PushableEnvironmentType::FlatFloor:
|
||||||
|
case PushableEnvironmentType::WaterFloor:
|
||||||
|
// Connect to another stack.
|
||||||
|
foundStack = SearchNearPushablesStack(pushableItem.Index);
|
||||||
|
StackPushable(pushableItem.Index, foundStack);
|
||||||
|
|
||||||
|
pushableItem.Pose.Orientation = EulerAngles(0, pushableItem.Pose.Orientation.y, 0);
|
||||||
|
|
||||||
|
// Set stopper flag.
|
||||||
|
if (pushable.Stack.ItemNumberBelow == NO_ITEM)
|
||||||
|
SetPushableStopperFlag(true, pushableItem.Pose.Position, pushableItem.RoomNumber);
|
||||||
|
|
||||||
|
// Activate trigger.
|
||||||
|
TestTriggers(&pushableItem, true, pushableItem.Flags & IFLAG_ACTIVATION_MASK);
|
||||||
|
|
||||||
|
// Shake floor if pushable landed at high enough velocity.
|
||||||
|
if (pushableItem.Animation.Velocity.y >= PUSHABLE_RUMBLE_FALL_VELOCITY)
|
||||||
|
{
|
||||||
|
FloorShake(&pushableItem);
|
||||||
|
pushable.SoundState = PushableSoundState::Fall;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Place on floor.
|
||||||
|
pushableItem.Animation.Velocity.y = 0.0f;
|
||||||
|
pushableItem.Pose.Position.y = pushableColl.FloorHeight;
|
||||||
|
pushable.BehaviorState = PushableBehaviourState::Idle;
|
||||||
|
|
||||||
|
AddPushableBridge(pushableItem);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PushableEnvironmentType::SlopedFloor:
|
||||||
|
pushableItem.Animation.Velocity.y = 0.0f;
|
||||||
|
pushableItem.Pose.Position.y = pushableColl.FloorHeight;
|
||||||
|
pushable.BehaviorState = PushableBehaviourState::Slide;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PushableEnvironmentType::Water:
|
||||||
|
pushable.BehaviorState = PushableBehaviourState::Sink;
|
||||||
|
SpawnPushableSplash(pushableItem);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
TENLog("Error handling pushable collision in fall state for pushable item " + std::to_string(pushableItem.Index), LogLevel::Warning, LogConfig::All, false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void HandleSinkState(ItemInfo& pushableItem)
|
||||||
|
{
|
||||||
|
auto& pushable = GetPushableInfo(pushableItem);
|
||||||
|
|
||||||
|
// Get pushable collision.
|
||||||
|
auto pushableColl = GetPushableCollision(pushableItem);
|
||||||
|
|
||||||
|
int foundStack = NO_ITEM;
|
||||||
|
|
||||||
|
switch (pushableColl.EnvType)
|
||||||
|
{
|
||||||
|
case PushableEnvironmentType::Air:
|
||||||
|
case PushableEnvironmentType::FlatFloor:
|
||||||
|
case PushableEnvironmentType::SlopedFloor:
|
||||||
|
// Set stopper flag.
|
||||||
|
if (pushable.Stack.ItemNumberBelow == NO_ITEM)
|
||||||
|
SetPushableStopperFlag(true, pushableItem.Pose.Position, pushableItem.RoomNumber);
|
||||||
|
|
||||||
|
pushable.BehaviorState = PushableBehaviourState::Fall;
|
||||||
|
pushable.Gravity = PUSHABLE_GRAVITY_AIR;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PushableEnvironmentType::Water:
|
||||||
|
{
|
||||||
|
// Manage influence of gravity.
|
||||||
|
if (pushable.IsBuoyant && pushable.Stack.ItemNumberAbove == NO_ITEM)
|
||||||
|
{
|
||||||
|
pushable.Gravity = pushable.Gravity - PUSHABLE_GRAVITY_ACCEL;
|
||||||
|
if (pushable.Gravity <= 0.0f)
|
||||||
|
{
|
||||||
|
pushable.BehaviorState = PushableBehaviourState::Float;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pushable.Gravity = std::max(pushable.Gravity - PUSHABLE_GRAVITY_ACCEL, PUSHABLE_GRAVITY_WATER);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move object.
|
||||||
|
pushableItem.Pose.Position.y += pushableItem.Animation.Velocity.y;
|
||||||
|
pushableItem.Animation.Velocity.y = std::min(pushableItem.Animation.Velocity.y + pushable.Gravity, PUSHABLE_WATER_VELOCITY_MAX);
|
||||||
|
HandlePushableFallRotation(pushableItem);
|
||||||
|
|
||||||
|
// Shallow water.
|
||||||
|
int waterheight = abs(pushableColl.FloorHeight - pushable.WaterSurfaceHeight);
|
||||||
|
if (waterheight > GetPushableHeight(pushableItem))
|
||||||
|
{
|
||||||
|
HandlePushableRippleEffect(pushableItem);
|
||||||
|
}
|
||||||
|
// Deep water.
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SpawnPushableBubbles(pushableItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PushableEnvironmentType::WaterFloor:
|
||||||
|
if (pushable.IsBuoyant && pushable.Stack.ItemNumberAbove == NO_ITEM)
|
||||||
|
{
|
||||||
|
pushableItem.Pose.Position.y = pushableColl.FloorHeight;
|
||||||
|
pushable.BehaviorState = PushableBehaviourState::Float;
|
||||||
|
pushable.Gravity = 0.0f;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pushableItem.Pose.Position.y = pushableColl.FloorHeight;
|
||||||
|
pushableItem.Animation.Velocity.y = 0.0f;
|
||||||
|
pushable.BehaviorState = PushableBehaviourState::UnderwaterIdle;
|
||||||
|
pushable.Gravity = PUSHABLE_GRAVITY_WATER;
|
||||||
|
AddPushableBridge(pushableItem);
|
||||||
|
|
||||||
|
pushableItem.Pose.Orientation = EulerAngles(0, pushableItem.Pose.Orientation.y, 0);
|
||||||
|
|
||||||
|
// Activate trigger.
|
||||||
|
TestTriggers(&pushableItem, true, pushableItem.Flags & IFLAG_ACTIVATION_MASK);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
TENLog("Error handling pushable collision in sink state for pushable item " + std::to_string(pushableItem.Index), LogLevel::Warning, LogConfig::All, false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void HandleFloatState(ItemInfo& pushableItem)
|
||||||
|
{
|
||||||
|
auto& pushable = GetPushableInfo(pushableItem);
|
||||||
|
|
||||||
|
// Get pushable collision.
|
||||||
|
auto pushableColl = GetPushableCollision(pushableItem);
|
||||||
|
|
||||||
|
int targetHeight = 0;
|
||||||
|
|
||||||
|
switch (pushableColl.EnvType)
|
||||||
|
{
|
||||||
|
case PushableEnvironmentType::Air:
|
||||||
|
case PushableEnvironmentType::FlatFloor:
|
||||||
|
case PushableEnvironmentType::SlopedFloor:
|
||||||
|
|
||||||
|
// Set stopper flag.
|
||||||
|
if (pushable.Stack.ItemNumberBelow == NO_ITEM)
|
||||||
|
SetPushableStopperFlag(true, pushableItem.Pose.Position, pushableItem.RoomNumber);
|
||||||
|
|
||||||
|
pushable.BehaviorState = PushableBehaviourState::Fall;
|
||||||
|
pushable.Gravity = PUSHABLE_GRAVITY_AIR;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PushableEnvironmentType::Water:
|
||||||
|
case PushableEnvironmentType::WaterFloor:
|
||||||
|
|
||||||
|
// Determine target height.
|
||||||
|
if (pushable.WaterSurfaceHeight == NO_HEIGHT)
|
||||||
|
{
|
||||||
|
if (abs(pushableColl.CeilingHeight - pushableColl.FloorHeight) >= (GetPushableHeight(pushableItem) + PUSHABLE_WATER_SURFACE_DISTANCE))
|
||||||
|
{
|
||||||
|
targetHeight = (pushableColl.CeilingHeight + pushable.Height) + PUSHABLE_WATER_SURFACE_DISTANCE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
targetHeight = pushableItem.Pose.Position.y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// No ceiling; rise above water surface.
|
||||||
|
else
|
||||||
|
{
|
||||||
|
targetHeight = pushable.WaterSurfaceHeight - PUSHABLE_WATER_SURFACE_DISTANCE + pushable.Height;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Manage influence of gravity.
|
||||||
|
pushable.Gravity = std::max(pushable.Gravity - PUSHABLE_GRAVITY_ACCEL, -PUSHABLE_GRAVITY_WATER);
|
||||||
|
|
||||||
|
// Move up.
|
||||||
|
if (targetHeight < pushableItem.Pose.Position.y)
|
||||||
|
{
|
||||||
|
pushableItem.Pose.Position.y += pushableItem.Animation.Velocity.y;
|
||||||
|
pushableItem.Animation.Velocity.y = std::min(pushableItem.Animation.Velocity.y + pushable.Gravity, PUSHABLE_WATER_VELOCITY_MAX);
|
||||||
|
HandlePushableFallRotation(pushableItem);
|
||||||
|
}
|
||||||
|
// Reached target height.
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pushableItem.Animation.Velocity.y = 0.0f;
|
||||||
|
pushableItem.Pose.Position.y = targetHeight;
|
||||||
|
pushableItem.Pose.Orientation = EulerAngles(0, pushableItem.Pose.Orientation.y, 0);
|
||||||
|
pushable.BehaviorState = PushableBehaviourState::WaterSurfaceIdle;
|
||||||
|
pushable.Gravity = PUSHABLE_GRAVITY_WATER;
|
||||||
|
AddPushableBridge(pushableItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
TENLog("Error handling pushable collision in float state for pushable item " + std::to_string(pushableItem.Index), LogLevel::Warning, LogConfig::All, false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void HandleUnderwaterState(ItemInfo& pushableItem)
|
||||||
|
{
|
||||||
|
auto& pushable = GetPushableInfo(pushableItem);
|
||||||
|
|
||||||
|
// Get pushable collision.
|
||||||
|
auto pushableColl = GetPushableCollision(pushableItem);
|
||||||
|
|
||||||
|
switch (pushableColl.EnvType)
|
||||||
|
{
|
||||||
|
case PushableEnvironmentType::FlatFloor:
|
||||||
|
case PushableEnvironmentType::SlopedFloor:
|
||||||
|
case PushableEnvironmentType::Air:
|
||||||
|
// Set stopper flag.
|
||||||
|
if (pushable.Stack.ItemNumberBelow == NO_ITEM)
|
||||||
|
SetPushableStopperFlag(true, pushableItem.Pose.Position, pushableItem.RoomNumber);
|
||||||
|
|
||||||
|
pushableItem.Animation.Velocity.y = 0.0f;
|
||||||
|
pushableItem.Pose.Position.y = pushableColl.FloorHeight;
|
||||||
|
pushable.BehaviorState = PushableBehaviourState::Idle;
|
||||||
|
pushable.Gravity = PUSHABLE_GRAVITY_AIR;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PushableEnvironmentType::WaterFloor:
|
||||||
|
{
|
||||||
|
// Reached water floor; change to idle.
|
||||||
|
if (pushable.WaterSurfaceHeight != NO_HEIGHT)
|
||||||
|
{
|
||||||
|
int waterheight = abs(pushableColl.FloorHeight - pushable.WaterSurfaceHeight);
|
||||||
|
if (waterheight < GetPushableHeight(pushableItem))
|
||||||
|
{
|
||||||
|
pushableItem.Animation.Velocity.y = 0.0f;
|
||||||
|
pushableItem.Pose.Position.y = pushableColl.FloorHeight;
|
||||||
|
pushable.BehaviorState = PushableBehaviourState::Idle;
|
||||||
|
pushable.Gravity = PUSHABLE_GRAVITY_AIR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (pushable.IsBuoyant && pushable.Stack.ItemNumberAbove == NO_ITEM)
|
||||||
|
{
|
||||||
|
pushableItem.Animation.Velocity.y = 0.0f;
|
||||||
|
pushable.BehaviorState = PushableBehaviourState::Float;
|
||||||
|
pushable.Gravity = 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remain on floor.
|
||||||
|
pushableItem.Pose.Position.y = pushableColl.FloorHeight;
|
||||||
|
int relHeight = pushableColl.FloorHeight - pushableItem.Pose.Position.y;
|
||||||
|
SetPushableVerticalPos(pushableItem, relHeight);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PushableEnvironmentType::Water:
|
||||||
|
if (pushable.IsBuoyant && pushable.Stack.ItemNumberAbove == NO_ITEM)
|
||||||
|
{
|
||||||
|
pushableItem.Animation.Velocity.y = 0.0f;
|
||||||
|
pushable.BehaviorState = PushableBehaviourState::Float;
|
||||||
|
pushable.Gravity = 0.0f;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Only pass to sinking if distance to noticeable. If is small, just stuck it to the ground.
|
||||||
|
if (abs(pushableColl.FloorHeight - pushableItem.Pose.Position.y) > CLICK(0.75f))
|
||||||
|
{
|
||||||
|
//Reset Stopper Flag
|
||||||
|
if (pushable.Stack.ItemNumberBelow == NO_ITEM)
|
||||||
|
SetPushableStopperFlag(false, pushableItem.Pose.Position, pushableItem.RoomNumber);
|
||||||
|
|
||||||
|
pushableItem.Animation.Velocity.y = 0.0f;
|
||||||
|
pushable.BehaviorState = PushableBehaviourState::Sink;
|
||||||
|
pushable.Gravity = 0.0f;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pushableItem.Pose.Position.y = pushableColl.FloorHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
TENLog("Error handling pushable collision in underwater state for pushable item " + std::to_string(pushableItem.Index), LogLevel::Warning, LogConfig::All, false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void HandleWaterSurfaceState(ItemInfo& pushableItem)
|
||||||
|
{
|
||||||
|
auto& pushable = GetPushableInfo(pushableItem);
|
||||||
|
|
||||||
|
// Get pushable collision.
|
||||||
|
auto pushableColl = GetPushableCollision(pushableItem);
|
||||||
|
|
||||||
|
switch (pushableColl.EnvType)
|
||||||
|
{
|
||||||
|
case PushableEnvironmentType::FlatFloor:
|
||||||
|
case PushableEnvironmentType::SlopedFloor:
|
||||||
|
case PushableEnvironmentType::Air:
|
||||||
|
pushableItem.Animation.Velocity.y = 0.0f;
|
||||||
|
pushableItem.Pose.Orientation = EulerAngles(0, pushableItem.Pose.Orientation.y, 0);
|
||||||
|
pushable.BehaviorState = PushableBehaviourState::Fall;
|
||||||
|
pushable.Gravity = PUSHABLE_GRAVITY_AIR;
|
||||||
|
|
||||||
|
RemovePushableBridge(pushableItem);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PushableEnvironmentType::Water:
|
||||||
|
pushable.UseRoomCollision ? HandlePushableBridgeOscillation(pushableItem) : HandlePushableOscillation(pushableItem);
|
||||||
|
HandlePushableRippleEffect(pushableItem);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PushableEnvironmentType::WaterFloor:
|
||||||
|
{
|
||||||
|
// If shallow water, change to idle state.
|
||||||
|
int waterheight = abs(pushableColl.FloorHeight - pushable.WaterSurfaceHeight);
|
||||||
|
if (waterheight < GetPushableHeight(pushableItem))
|
||||||
|
{
|
||||||
|
pushable.BehaviorState = PushableBehaviourState::Idle;
|
||||||
|
pushable.Gravity = PUSHABLE_GRAVITY_AIR;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pushable.BehaviorState = PushableBehaviourState::UnderwaterIdle;
|
||||||
|
pushable.Gravity = PUSHABLE_GRAVITY_WATER;
|
||||||
|
}
|
||||||
|
|
||||||
|
pushableItem.Animation.Velocity.y = 0.0f;
|
||||||
|
pushableItem.Pose.Position.y = pushableColl.FloorHeight;
|
||||||
|
pushableItem.Pose.Orientation = EulerAngles(0, pushableItem.Pose.Orientation.y, 0);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
TENLog("Error handling pushable collision in water surface state for pushable item " + std::to_string(pushableItem.Index), LogLevel::Warning, LogConfig::All, false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void HandleSlideState(ItemInfo& pushableItem)
|
||||||
|
{
|
||||||
|
auto& pushable = GetPushableInfo(pushableItem);
|
||||||
|
|
||||||
|
// TODO:
|
||||||
|
// 1. Get floor surface's slope and aspect angles.
|
||||||
|
// 2. Calculate speed.
|
||||||
|
// 3. Move object.
|
||||||
|
// 4. Assess oncoming sector.
|
||||||
|
// Is slope -> keep sliding.
|
||||||
|
// Is flat floor -> stop sliding.
|
||||||
|
// Is ledge -> fall.
|
||||||
|
// Is forbidden sector -> freeze.
|
||||||
|
// 5. Incorporate effects.
|
||||||
|
}
|
||||||
|
|
||||||
|
static void HandleMoveStackHorizontalState(ItemInfo& pushableItem)
|
||||||
|
{
|
||||||
|
auto& pushable = GetPushableInfo(pushableItem);
|
||||||
|
const auto& playerItem = *LaraItem;
|
||||||
|
const auto& player = GetLaraInfo(playerItem);
|
||||||
|
|
||||||
|
auto& movingPushableItem = g_Level.Items[player.Context.InteractedItem];
|
||||||
|
pushableItem.Pose.Position.x = movingPushableItem.Pose.Position.x;
|
||||||
|
pushableItem.Pose.Position.z = movingPushableItem.Pose.Position.z;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
/*void HandleStackFallState(ItemInfo& pushableItem)
|
||||||
|
{
|
||||||
|
auto& pushableItem = g_Level.Items[pushableItem.Index];
|
||||||
|
auto& pushable = GetPushableInfo(pushableItem);
|
||||||
|
const auto& playerItem = *LaraItem;
|
||||||
|
const auto& player = GetLaraInfo(playerItem);
|
||||||
|
|
||||||
|
auto& movingPushableItem = g_Level.Items[player.Context.InteractedItem];
|
||||||
|
pushableItem.Pose.Position.y = movingPushableItem.Pose.Position.y;
|
||||||
|
}*/
|
||||||
|
|
||||||
|
void HandlePushableBehaviorState(ItemInfo& pushableItem)
|
||||||
|
{
|
||||||
|
static const auto BEHAVIOR_STATE_MAP = std::unordered_map<PushableBehaviourState, std::function<void(ItemInfo& pushableItem)>>
|
||||||
|
{
|
||||||
|
{ PushableBehaviourState::Idle, &HandleIdleState },
|
||||||
|
{ PushableBehaviourState::Move, &HandleMoveState },
|
||||||
|
{ PushableBehaviourState::EdgeSlip, &HandleEdgeSlipState },
|
||||||
|
{ PushableBehaviourState::Fall, &HandleFallState },
|
||||||
|
{ PushableBehaviourState::Sink, &HandleSinkState },
|
||||||
|
{ PushableBehaviourState::Float, &HandleFloatState },
|
||||||
|
{ PushableBehaviourState::UnderwaterIdle, &HandleUnderwaterState },
|
||||||
|
{ PushableBehaviourState::WaterSurfaceIdle, &HandleWaterSurfaceState },
|
||||||
|
{ PushableBehaviourState::Slide, &HandleSlideState },
|
||||||
|
{ PushableBehaviourState::MoveStackHorizontal, &HandleMoveStackHorizontalState }
|
||||||
|
};
|
||||||
|
|
||||||
|
auto& pushable = GetPushableInfo(pushableItem);
|
||||||
|
|
||||||
|
auto it = BEHAVIOR_STATE_MAP.find(pushable.BehaviorState);
|
||||||
|
if (it == BEHAVIOR_STATE_MAP.end())
|
||||||
|
{
|
||||||
|
TENLog("Attempted to handle missing pushable state " + std::to_string((int)pushable.BehaviorState), LogLevel::Error, LogConfig::All);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
it->second(pushableItem);
|
||||||
|
}
|
||||||
|
}
|
22
TombEngine/Objects/Generic/Object/Pushable/PushableStates.h
Normal file
22
TombEngine/Objects/Generic/Object/Pushable/PushableStates.h
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
struct ItemInfo;
|
||||||
|
|
||||||
|
namespace TEN::Entities::Generic
|
||||||
|
{
|
||||||
|
enum class PushableBehaviourState
|
||||||
|
{
|
||||||
|
Idle,
|
||||||
|
Move,
|
||||||
|
EdgeSlip,
|
||||||
|
Fall,
|
||||||
|
Sink,
|
||||||
|
Float,
|
||||||
|
UnderwaterIdle,
|
||||||
|
WaterSurfaceIdle,
|
||||||
|
Slide,
|
||||||
|
MoveStackHorizontal
|
||||||
|
};
|
||||||
|
|
||||||
|
void HandlePushableBehaviorState(ItemInfo& pushableItem);
|
||||||
|
};
|
|
@ -14,7 +14,7 @@ std::optional<int> BridgeFloor(short itemNumber, int x, int y, int z)
|
||||||
{
|
{
|
||||||
const auto& item = g_Level.Items[itemNumber];
|
const auto& item = g_Level.Items[itemNumber];
|
||||||
|
|
||||||
auto boxHeight = GetBridgeItemIntersect(itemNumber, x, y, z, false);
|
auto boxHeight = GetBridgeItemIntersect(Vector3i(x, y, z), itemNumber, false);
|
||||||
if (boxHeight.has_value() && tiltGrade != 0)
|
if (boxHeight.has_value() && tiltGrade != 0)
|
||||||
{
|
{
|
||||||
int height = item.Pose.Position.y + (tiltGrade * ((GetOffset(item.Pose.Orientation.y, x, z) / 4) + (BLOCK(1) / 8)));
|
int height = item.Pose.Position.y + (tiltGrade * ((GetOffset(item.Pose.Orientation.y, x, z) / 4) + (BLOCK(1) / 8)));
|
||||||
|
@ -29,7 +29,7 @@ std::optional<int> BridgeCeiling(short itemNumber, int x, int y, int z)
|
||||||
{
|
{
|
||||||
const auto& item = g_Level.Items[itemNumber];
|
const auto& item = g_Level.Items[itemNumber];
|
||||||
|
|
||||||
auto boxHeight = GetBridgeItemIntersect(itemNumber, x, y, z, true);
|
auto boxHeight = GetBridgeItemIntersect(Vector3i(x, y, z), itemNumber, true);
|
||||||
if (boxHeight.has_value() && tiltGrade != 0)
|
if (boxHeight.has_value() && tiltGrade != 0)
|
||||||
{
|
{
|
||||||
int height = item.Pose.Position.y + (tiltGrade * ((GetOffset(item.Pose.Orientation.y, x, z) / 4) + (BLOCK(1) / 8)));
|
int height = item.Pose.Position.y + (tiltGrade * ((GetOffset(item.Pose.Orientation.y, x, z) / 4) + (BLOCK(1) / 8)));
|
||||||
|
|
|
@ -232,7 +232,7 @@ std::optional<int> TrapDoorFloor(short itemNumber, int x, int y, int z)
|
||||||
if (!trapDoorItem->MeshBits.TestAny() || trapDoorItem->ItemFlags[2] == 0)
|
if (!trapDoorItem->MeshBits.TestAny() || trapDoorItem->ItemFlags[2] == 0)
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
|
||||||
return GetBridgeItemIntersect(itemNumber, x, y, z, false);
|
return GetBridgeItemIntersect(Vector3i(x, y, z), itemNumber, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<int> TrapDoorCeiling(short itemNumber, int x, int y, int z)
|
std::optional<int> TrapDoorCeiling(short itemNumber, int x, int y, int z)
|
||||||
|
@ -242,5 +242,5 @@ std::optional<int> TrapDoorCeiling(short itemNumber, int x, int y, int z)
|
||||||
if (!trapDoorItem->MeshBits.TestAny() || trapDoorItem->ItemFlags[2] == 0)
|
if (!trapDoorItem->MeshBits.TestAny() || trapDoorItem->ItemFlags[2] == 0)
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
|
||||||
return GetBridgeItemIntersect(itemNumber, x, y, z, true);
|
return GetBridgeItemIntersect(Vector3i(x, y, z), itemNumber, true);
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,78 +77,82 @@ namespace TEN::Entities::Traps
|
||||||
|
|
||||||
switch (item.Animation.ActiveState)
|
switch (item.Animation.ActiveState)
|
||||||
{
|
{
|
||||||
case CRUMBLING_PLATFORM_STATE_SHAKE:
|
case CRUMBLING_PLATFORM_STATE_IDLE:
|
||||||
{
|
|
||||||
if (item.ItemFlags[0] > 0)
|
|
||||||
{
|
|
||||||
item.ItemFlags[0]--;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
item.Animation.TargetState = CRUMBLING_PLATFORM_STATE_FALL;
|
|
||||||
item.ItemFlags[1] = CRUMBLING_PLATFORM_VELOCITY_MIN;
|
|
||||||
|
|
||||||
auto pointColl = GetCollision(item);
|
|
||||||
pointColl.Block->RemoveBridge(itemNumber);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CRUMBLING_PLATFORM_STATE_FALL:
|
case CRUMBLING_PLATFORM_STATE_SHAKE:
|
||||||
|
{
|
||||||
|
if (item.ItemFlags[0] > 0)
|
||||||
{
|
{
|
||||||
short& fallVel = item.ItemFlags[1];
|
item.ItemFlags[0]--;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
item.Animation.TargetState = CRUMBLING_PLATFORM_STATE_FALL;
|
||||||
|
item.ItemFlags[1] = CRUMBLING_PLATFORM_VELOCITY_MIN;
|
||||||
|
|
||||||
// Get point collision.
|
|
||||||
auto box = GameBoundingBox(&item);
|
|
||||||
auto pointColl = GetCollision(item);
|
auto pointColl = GetCollision(item);
|
||||||
int relFloorHeight = (item.Pose.Position.y - pointColl.Position.Floor) - box.Y1 ;
|
pointColl.Block->RemoveBridge(itemNumber);
|
||||||
|
|
||||||
// Airborne.
|
|
||||||
if (relFloorHeight <= fallVel)
|
|
||||||
{
|
|
||||||
fallVel += CRUMBLING_PLATFORM_VELOCITY_ACCEL;
|
|
||||||
if (fallVel > CRUMBLING_PLATFORM_VELOCITY_MAX)
|
|
||||||
fallVel = CRUMBLING_PLATFORM_VELOCITY_MAX;
|
|
||||||
|
|
||||||
item.Pose.Position.y += fallVel;
|
|
||||||
}
|
|
||||||
// Grounded.
|
|
||||||
else
|
|
||||||
{
|
|
||||||
item.Animation.TargetState = CRUMBLING_PLATFORM_STATE_LAND;
|
|
||||||
item.Pose.Position.y = pointColl.Position.Floor;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update room number.
|
|
||||||
int probedRoomNumber = pointColl.RoomNumber;
|
|
||||||
if (item.RoomNumber != probedRoomNumber)
|
|
||||||
ItemNewRoom(itemNumber, probedRoomNumber);
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CRUMBLING_PLATFORM_STATE_LAND:
|
case CRUMBLING_PLATFORM_STATE_FALL:
|
||||||
|
{
|
||||||
|
short& fallVel = item.ItemFlags[1];
|
||||||
|
|
||||||
|
// Get point collision.
|
||||||
|
auto box = GameBoundingBox(&item);
|
||||||
|
auto pointColl = GetCollision(item);
|
||||||
|
int relFloorHeight = (item.Pose.Position.y - pointColl.Position.Floor) - box.Y1 ;
|
||||||
|
|
||||||
|
// Airborne.
|
||||||
|
if (relFloorHeight <= fallVel)
|
||||||
{
|
{
|
||||||
// Align to surface.
|
fallVel += CRUMBLING_PLATFORM_VELOCITY_ACCEL;
|
||||||
auto radius = Vector2(Objects[item.ObjectNumber].radius);
|
if (fallVel > CRUMBLING_PLATFORM_VELOCITY_MAX)
|
||||||
AlignEntityToSurface(&item, radius);
|
fallVel = CRUMBLING_PLATFORM_VELOCITY_MAX;
|
||||||
|
|
||||||
// Deactivate.
|
item.Pose.Position.y += fallVel;
|
||||||
if (TestLastFrame(&item))
|
}
|
||||||
{
|
// Grounded.
|
||||||
RemoveActiveItem(itemNumber);
|
else
|
||||||
item.Status = ITEM_NOT_ACTIVE;
|
{
|
||||||
}
|
item.Animation.TargetState = CRUMBLING_PLATFORM_STATE_LAND;
|
||||||
|
item.Pose.Position.y = pointColl.Position.Floor;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
// Update room number.
|
||||||
|
int probedRoomNumber = pointColl.RoomNumber;
|
||||||
|
if (item.RoomNumber != probedRoomNumber)
|
||||||
|
ItemNewRoom(itemNumber, probedRoomNumber);
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
break;
|
||||||
TENLog(
|
|
||||||
"Error with crumbling platform with entity ID" + std::to_string(itemNumber) + ". animation state not recognized.",
|
case CRUMBLING_PLATFORM_STATE_LAND:
|
||||||
LogLevel::Error, LogConfig::All);
|
{
|
||||||
break;
|
// Align to surface.
|
||||||
|
auto radius = Vector2(Objects[item.ObjectNumber].radius);
|
||||||
|
AlignEntityToSurface(&item, radius);
|
||||||
|
|
||||||
|
// Deactivate.
|
||||||
|
if (TestLastFrame(&item))
|
||||||
|
{
|
||||||
|
RemoveActiveItem(itemNumber);
|
||||||
|
item.Status = ITEM_NOT_ACTIVE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
TENLog(
|
||||||
|
"Error with crumbling platform item " + std::to_string(itemNumber) +
|
||||||
|
": attempted to handle missing state " + std::to_string(item.Animation.ActiveState),
|
||||||
|
LogLevel::Error, LogConfig::All);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
AnimateItem(&item);
|
AnimateItem(&item);
|
||||||
|
@ -181,7 +185,7 @@ namespace TEN::Entities::Traps
|
||||||
if (item.Animation.ActiveState == CRUMBLING_PLATFORM_STATE_IDLE ||
|
if (item.Animation.ActiveState == CRUMBLING_PLATFORM_STATE_IDLE ||
|
||||||
item.Animation.ActiveState == CRUMBLING_PLATFORM_STATE_SHAKE)
|
item.Animation.ActiveState == CRUMBLING_PLATFORM_STATE_SHAKE)
|
||||||
{
|
{
|
||||||
auto boxHeight = GetBridgeItemIntersect(itemNumber, x, y, z, false);
|
auto boxHeight = GetBridgeItemIntersect(Vector3i(x, y, z), itemNumber, false);
|
||||||
if (boxHeight.has_value())
|
if (boxHeight.has_value())
|
||||||
return *boxHeight;
|
return *boxHeight;
|
||||||
}
|
}
|
||||||
|
@ -196,7 +200,7 @@ namespace TEN::Entities::Traps
|
||||||
if (item.Animation.ActiveState == CRUMBLING_PLATFORM_STATE_IDLE ||
|
if (item.Animation.ActiveState == CRUMBLING_PLATFORM_STATE_IDLE ||
|
||||||
item.Animation.ActiveState == CRUMBLING_PLATFORM_STATE_SHAKE)
|
item.Animation.ActiveState == CRUMBLING_PLATFORM_STATE_SHAKE)
|
||||||
{
|
{
|
||||||
auto boxHeight = GetBridgeItemIntersect(itemNumber, x, y, z, true);
|
auto boxHeight = GetBridgeItemIntersect(Vector3i(x, y, z), itemNumber, true);
|
||||||
if (boxHeight.has_value())
|
if (boxHeight.has_value())
|
||||||
return *boxHeight;
|
return *boxHeight;
|
||||||
}
|
}
|
||||||
|
|
|
@ -145,7 +145,7 @@ std::optional<int> FallingBlockFloor(short itemNumber, int x, int y, int z)
|
||||||
if (!item->MeshBits.TestAny() || item->ItemFlags[0] >= FALLINGBLOCK_DELAY)
|
if (!item->MeshBits.TestAny() || item->ItemFlags[0] >= FALLINGBLOCK_DELAY)
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
|
||||||
return GetBridgeItemIntersect(itemNumber, x, y, z, false);
|
return GetBridgeItemIntersect(Vector3i(x, y, z), itemNumber, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<int> FallingBlockCeiling(short itemNumber, int x, int y, int z)
|
std::optional<int> FallingBlockCeiling(short itemNumber, int x, int y, int z)
|
||||||
|
@ -155,7 +155,7 @@ std::optional<int> FallingBlockCeiling(short itemNumber, int x, int y, int z)
|
||||||
if (!item->MeshBits.TestAny() || item->ItemFlags[0] >= FALLINGBLOCK_DELAY)
|
if (!item->MeshBits.TestAny() || item->ItemFlags[0] >= FALLINGBLOCK_DELAY)
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
|
||||||
return GetBridgeItemIntersect(itemNumber, x, y, z, true);
|
return GetBridgeItemIntersect(Vector3i(x, y, z), itemNumber, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
int FallingBlockFloorBorder(short itemNumber)
|
int FallingBlockFloorBorder(short itemNumber)
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#include "Objects/Generic/Object/generic_bridge.h"
|
#include "Objects/Generic/Object/generic_bridge.h"
|
||||||
#include "Objects/Generic/Object/burning_torch.h"
|
#include "Objects/Generic/Object/burning_torch.h"
|
||||||
#include "Objects/Generic/Object/polerope.h"
|
#include "Objects/Generic/Object/polerope.h"
|
||||||
|
#include "Objects/Generic/Object/Pushable/PushableObject.h"
|
||||||
#include "Objects/Generic/Object/rope.h"
|
#include "Objects/Generic/Object/rope.h"
|
||||||
|
|
||||||
// Switches
|
// Switches
|
||||||
|
@ -61,7 +62,7 @@ static void StartObject(ObjectInfo* object)
|
||||||
object->ceilingBorder = TrapDoorCeilingBorder;
|
object->ceilingBorder = TrapDoorCeilingBorder;
|
||||||
object->floor = TrapDoorFloor;
|
object->floor = TrapDoorFloor;
|
||||||
object->ceiling = TrapDoorCeiling;
|
object->ceiling = TrapDoorCeiling;
|
||||||
object->SetupHitEffect(true);
|
object->SetHitEffect(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,7 +78,7 @@ static void StartObject(ObjectInfo* object)
|
||||||
object->ceilingBorder = TrapDoorCeilingBorder;
|
object->ceilingBorder = TrapDoorCeilingBorder;
|
||||||
object->floor = TrapDoorFloor;
|
object->floor = TrapDoorFloor;
|
||||||
object->ceiling = TrapDoorCeiling;
|
object->ceiling = TrapDoorCeiling;
|
||||||
object->SetupHitEffect(true);
|
object->SetHitEffect(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,10 +94,16 @@ static void StartObject(ObjectInfo* object)
|
||||||
object->ceilingBorder = TrapDoorCeilingBorder;
|
object->ceilingBorder = TrapDoorCeilingBorder;
|
||||||
object->floor = TrapDoorFloor;
|
object->floor = TrapDoorFloor;
|
||||||
object->ceiling = TrapDoorCeiling;
|
object->ceiling = TrapDoorCeiling;
|
||||||
object->SetupHitEffect(true);
|
object->SetHitEffect(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (int objectNumber = ID_PUSHABLE_OBJECT1; objectNumber <= ID_PUSHABLE_OBJECT10; objectNumber++)
|
||||||
|
InitPushableObject(object, objectNumber);
|
||||||
|
|
||||||
|
for (int objectNumber = ID_PUSHABLE_OBJECT_CLIMBABLE1; objectNumber <= ID_PUSHABLE_OBJECT_CLIMBABLE10; objectNumber++)
|
||||||
|
InitPushableObject(object, objectNumber);
|
||||||
|
|
||||||
object = &Objects[ID_BRIDGE_FLAT];
|
object = &Objects[ID_BRIDGE_FLAT];
|
||||||
if (object->loaded)
|
if (object->loaded)
|
||||||
{
|
{
|
||||||
|
@ -155,7 +162,7 @@ void StartSwitches(ObjectInfo* object)
|
||||||
{
|
{
|
||||||
object->collision = CogSwitchCollision;
|
object->collision = CogSwitchCollision;
|
||||||
object->control = CogSwitchControl;
|
object->control = CogSwitchControl;
|
||||||
object->SetupHitEffect(true);
|
object->SetHitEffect(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
object = &Objects[ID_LEVER_SWITCH];
|
object = &Objects[ID_LEVER_SWITCH];
|
||||||
|
@ -163,7 +170,7 @@ void StartSwitches(ObjectInfo* object)
|
||||||
{
|
{
|
||||||
object->collision = RailSwitchCollision;
|
object->collision = RailSwitchCollision;
|
||||||
object->control = SwitchControl;
|
object->control = SwitchControl;
|
||||||
object->SetupHitEffect(true);
|
object->SetHitEffect(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
object = &Objects[ID_JUMP_SWITCH];
|
object = &Objects[ID_JUMP_SWITCH];
|
||||||
|
@ -171,7 +178,7 @@ void StartSwitches(ObjectInfo* object)
|
||||||
{
|
{
|
||||||
object->collision = JumpSwitchCollision;
|
object->collision = JumpSwitchCollision;
|
||||||
object->control = SwitchControl;
|
object->control = SwitchControl;
|
||||||
object->SetupHitEffect(true);
|
object->SetHitEffect(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int objectNumber = ID_SWITCH_TYPE1; objectNumber <= ID_SWITCH_TYPE16; objectNumber++)
|
for (int objectNumber = ID_SWITCH_TYPE1; objectNumber <= ID_SWITCH_TYPE16; objectNumber++)
|
||||||
|
@ -181,7 +188,7 @@ void StartSwitches(ObjectInfo* object)
|
||||||
{
|
{
|
||||||
object->collision = SwitchCollision;
|
object->collision = SwitchCollision;
|
||||||
object->control = SwitchControl;
|
object->control = SwitchControl;
|
||||||
object->SetupHitEffect(true);
|
object->SetHitEffect(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -190,7 +197,7 @@ void StartSwitches(ObjectInfo* object)
|
||||||
{
|
{
|
||||||
object->collision = AirlockSwitchCollision;
|
object->collision = AirlockSwitchCollision;
|
||||||
object->control = SwitchControl;
|
object->control = SwitchControl;
|
||||||
object->SetupHitEffect(true);
|
object->SetHitEffect(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
object = &Objects[ID_CROWBAR_SWITCH];
|
object = &Objects[ID_CROWBAR_SWITCH];
|
||||||
|
@ -198,7 +205,7 @@ void StartSwitches(ObjectInfo* object)
|
||||||
{
|
{
|
||||||
object->collision = CrowbarSwitchCollision;
|
object->collision = CrowbarSwitchCollision;
|
||||||
object->control = SwitchControl;
|
object->control = SwitchControl;
|
||||||
object->SetupHitEffect(true);
|
object->SetHitEffect(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
object = &Objects[ID_MINECART_SWITCH];
|
object = &Objects[ID_MINECART_SWITCH];
|
||||||
|
@ -207,7 +214,7 @@ void StartSwitches(ObjectInfo* object)
|
||||||
object->Initialize = InitializeAnimating;
|
object->Initialize = InitializeAnimating;
|
||||||
object->control = AnimatingControl;
|
object->control = AnimatingControl;
|
||||||
object->collision = ObjectCollision;
|
object->collision = ObjectCollision;
|
||||||
object->SetupHitEffect(true);
|
object->SetHitEffect(true);
|
||||||
object->shadowType = ShadowMode::All;
|
object->shadowType = ShadowMode::All;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -234,7 +241,7 @@ void StartSwitches(ObjectInfo* object)
|
||||||
{
|
{
|
||||||
object->control = TurnSwitchControl;
|
object->control = TurnSwitchControl;
|
||||||
object->collision = TurnSwitchCollision;
|
object->collision = TurnSwitchCollision;
|
||||||
object->SetupHitEffect(true);
|
object->SetHitEffect(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
object = &Objects[ID_SEQUENCE_SWITCH1];
|
object = &Objects[ID_SEQUENCE_SWITCH1];
|
||||||
|
@ -242,7 +249,7 @@ void StartSwitches(ObjectInfo* object)
|
||||||
{
|
{
|
||||||
object->collision = FullBlockSwitchCollision;
|
object->collision = FullBlockSwitchCollision;
|
||||||
object->control = FullBlockSwitchControl;
|
object->control = FullBlockSwitchControl;
|
||||||
object->SetupHitEffect(true);
|
object->SetHitEffect(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
object = &Objects[ID_SEQUENCE_SWITCH2];
|
object = &Objects[ID_SEQUENCE_SWITCH2];
|
||||||
|
@ -250,7 +257,7 @@ void StartSwitches(ObjectInfo* object)
|
||||||
{
|
{
|
||||||
object->collision = FullBlockSwitchCollision;
|
object->collision = FullBlockSwitchCollision;
|
||||||
object->control = FullBlockSwitchControl;
|
object->control = FullBlockSwitchControl;
|
||||||
object->SetupHitEffect(true);
|
object->SetHitEffect(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
object = &Objects[ID_SEQUENCE_SWITCH3];
|
object = &Objects[ID_SEQUENCE_SWITCH3];
|
||||||
|
@ -258,7 +265,7 @@ void StartSwitches(ObjectInfo* object)
|
||||||
{
|
{
|
||||||
object->collision = FullBlockSwitchCollision;
|
object->collision = FullBlockSwitchCollision;
|
||||||
object->control = FullBlockSwitchControl;
|
object->control = FullBlockSwitchControl;
|
||||||
object->SetupHitEffect(true);
|
object->SetHitEffect(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int objectID = ID_SHOOT_SWITCH1; objectID <= ID_SHOOT_SWITCH4; objectID++)
|
for (int objectID = ID_SHOOT_SWITCH1; objectID <= ID_SHOOT_SWITCH4; objectID++)
|
||||||
|
@ -292,7 +299,7 @@ void StartDoors(ObjectInfo* object)
|
||||||
object->Initialize = InitializeDoor;
|
object->Initialize = InitializeDoor;
|
||||||
object->control = DoorControl;
|
object->control = DoorControl;
|
||||||
object->collision = DoorCollision;
|
object->collision = DoorCollision;
|
||||||
object->SetupHitEffect(true);
|
object->SetHitEffect(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -301,7 +308,7 @@ void StartDoors(ObjectInfo* object)
|
||||||
{
|
{
|
||||||
object->Initialize = InitializeDoor;
|
object->Initialize = InitializeDoor;
|
||||||
object->control = DoorControl;
|
object->control = DoorControl;
|
||||||
object->SetupHitEffect(true);
|
object->SetHitEffect(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
object = &Objects[ID_LIFT_DOORS2];
|
object = &Objects[ID_LIFT_DOORS2];
|
||||||
|
@ -309,7 +316,7 @@ void StartDoors(ObjectInfo* object)
|
||||||
{
|
{
|
||||||
object->Initialize = InitializeDoor;
|
object->Initialize = InitializeDoor;
|
||||||
object->control = DoorControl;
|
object->control = DoorControl;
|
||||||
object->SetupHitEffect(true);
|
object->SetHitEffect(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
object = &Objects[ID_SEQUENCE_DOOR1];
|
object = &Objects[ID_SEQUENCE_DOOR1];
|
||||||
|
@ -318,7 +325,7 @@ void StartDoors(ObjectInfo* object)
|
||||||
object->Initialize = InitializeDoor;
|
object->Initialize = InitializeDoor;
|
||||||
object->collision = DoorCollision;
|
object->collision = DoorCollision;
|
||||||
object->control = SequenceDoorControl;
|
object->control = SequenceDoorControl;
|
||||||
object->SetupHitEffect(true);
|
object->SetHitEffect(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = ID_DOUBLE_DOORS1; i <= ID_DOUBLE_DOORS4; i++)
|
for (int i = ID_DOUBLE_DOORS1; i <= ID_DOUBLE_DOORS4; i++)
|
||||||
|
@ -329,7 +336,7 @@ void StartDoors(ObjectInfo* object)
|
||||||
object->Initialize = InitializeDoor;
|
object->Initialize = InitializeDoor;
|
||||||
object->collision = DoubleDoorCollision;
|
object->collision = DoubleDoorCollision;
|
||||||
object->control = PushPullKickDoorControl;
|
object->control = PushPullKickDoorControl;
|
||||||
object->SetupHitEffect(true);
|
object->SetHitEffect(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -341,7 +348,7 @@ void StartDoors(ObjectInfo* object)
|
||||||
object->Initialize = InitializeDoor;
|
object->Initialize = InitializeDoor;
|
||||||
object->collision = UnderwaterDoorCollision;
|
object->collision = UnderwaterDoorCollision;
|
||||||
object->control = PushPullKickDoorControl;
|
object->control = PushPullKickDoorControl;
|
||||||
object->SetupHitEffect(true);
|
object->SetHitEffect(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -353,7 +360,7 @@ void StartDoors(ObjectInfo* object)
|
||||||
object->Initialize = InitializeDoor;
|
object->Initialize = InitializeDoor;
|
||||||
object->collision = PushPullKickDoorCollision;
|
object->collision = PushPullKickDoorCollision;
|
||||||
object->control = PushPullKickDoorControl;
|
object->control = PushPullKickDoorControl;
|
||||||
object->SetupHitEffect(true);
|
object->SetHitEffect(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -362,7 +369,7 @@ void StartDoors(ObjectInfo* object)
|
||||||
{
|
{
|
||||||
object->Initialize = InitializeSteelDoor;
|
object->Initialize = InitializeSteelDoor;
|
||||||
object->collision = SteelDoorCollision;
|
object->collision = SteelDoorCollision;
|
||||||
object->SetupHitEffect(true);
|
object->SetHitEffect(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -406,7 +413,7 @@ void StartTraps(ObjectInfo* object)
|
||||||
if (object->loaded)
|
if (object->loaded)
|
||||||
{
|
{
|
||||||
object->collision = PoleCollision;
|
object->collision = PoleCollision;
|
||||||
object->SetupHitEffect(true);
|
object->SetHitEffect(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
object = &Objects[ID_BURNING_TORCH_ITEM];
|
object = &Objects[ID_BURNING_TORCH_ITEM];
|
||||||
|
|
|
@ -46,7 +46,7 @@ static void StartEntity(ObjectInfo* obj)
|
||||||
obj->radius = 340;
|
obj->radius = 340;
|
||||||
obj->intelligent = true;
|
obj->intelligent = true;
|
||||||
obj->SetBoneRotationFlags(2, ROT_Y);
|
obj->SetBoneRotationFlags(2, ROT_Y);
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_BEAR];
|
obj = &Objects[ID_BEAR];
|
||||||
|
@ -61,7 +61,7 @@ static void StartEntity(ObjectInfo* obj)
|
||||||
obj->radius = 340;
|
obj->radius = 340;
|
||||||
obj->intelligent = true;
|
obj->intelligent = true;
|
||||||
obj->SetBoneRotationFlags(13, ROT_Y);
|
obj->SetBoneRotationFlags(13, ROT_Y);
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_APE];
|
obj = &Objects[ID_APE];
|
||||||
|
@ -76,7 +76,7 @@ static void StartEntity(ObjectInfo* obj)
|
||||||
obj->radius = 340;
|
obj->radius = 340;
|
||||||
obj->intelligent = true;
|
obj->intelligent = true;
|
||||||
obj->LotType = LotType::Ape;
|
obj->LotType = LotType::Ape;
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_BIG_RAT];
|
obj = &Objects[ID_BIG_RAT];
|
||||||
|
@ -93,7 +93,7 @@ static void StartEntity(ObjectInfo* obj)
|
||||||
obj->waterCreature = true;
|
obj->waterCreature = true;
|
||||||
obj->LotType = LotType::Water;
|
obj->LotType = LotType::Water;
|
||||||
obj->SetBoneRotationFlags(1, ROT_Y);
|
obj->SetBoneRotationFlags(1, ROT_Y);
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_NATLA];
|
obj = &Objects[ID_NATLA];
|
||||||
|
@ -109,7 +109,7 @@ static void StartEntity(ObjectInfo* obj)
|
||||||
obj->LotType = LotType::Flyer;
|
obj->LotType = LotType::Flyer;
|
||||||
obj->SetBoneRotationFlags(0, ROT_X | ROT_Y); // Torso
|
obj->SetBoneRotationFlags(0, ROT_X | ROT_Y); // Torso
|
||||||
obj->SetBoneRotationFlags(1, ROT_X | ROT_Y); // Head
|
obj->SetBoneRotationFlags(1, ROT_X | ROT_Y); // Head
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_GIANT_MUTANT];
|
obj = &Objects[ID_GIANT_MUTANT];
|
||||||
|
@ -124,7 +124,7 @@ static void StartEntity(ObjectInfo* obj)
|
||||||
obj->intelligent = true;
|
obj->intelligent = true;
|
||||||
obj->LotType = LotType::Blockable;
|
obj->LotType = LotType::Blockable;
|
||||||
obj->SetBoneRotationFlags(1, ROT_Y);
|
obj->SetBoneRotationFlags(1, ROT_Y);
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_DOPPELGANGER];
|
obj = &Objects[ID_DOPPELGANGER];
|
||||||
|
@ -142,7 +142,7 @@ static void StartEntity(ObjectInfo* obj)
|
||||||
obj->radius = 102;
|
obj->radius = 102;
|
||||||
obj->intelligent = true;
|
obj->intelligent = true;
|
||||||
obj->nonLot = true;
|
obj->nonLot = true;
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_CENTAUR_MUTANT];
|
obj = &Objects[ID_CENTAUR_MUTANT];
|
||||||
|
@ -158,7 +158,7 @@ static void StartEntity(ObjectInfo* obj)
|
||||||
obj->intelligent = true;
|
obj->intelligent = true;
|
||||||
obj->LotType = LotType::Blockable;
|
obj->LotType = LotType::Blockable;
|
||||||
obj->SetBoneRotationFlags(10, ROT_X | ROT_Y);
|
obj->SetBoneRotationFlags(10, ROT_X | ROT_Y);
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_WINGED_MUMMY];
|
obj = &Objects[ID_WINGED_MUMMY];
|
||||||
|
@ -175,7 +175,7 @@ static void StartEntity(ObjectInfo* obj)
|
||||||
obj->LotType = LotType::Flyer;
|
obj->LotType = LotType::Flyer;
|
||||||
obj->SetBoneRotationFlags(1, ROT_Y);
|
obj->SetBoneRotationFlags(1, ROT_Y);
|
||||||
obj->SetBoneRotationFlags(2, ROT_Y);
|
obj->SetBoneRotationFlags(2, ROT_Y);
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_COWBOY];
|
obj = &Objects[ID_COWBOY];
|
||||||
|
@ -191,7 +191,7 @@ static void StartEntity(ObjectInfo* obj)
|
||||||
obj->intelligent = true;
|
obj->intelligent = true;
|
||||||
obj->SetBoneRotationFlags(0, ROT_X | ROT_Y);
|
obj->SetBoneRotationFlags(0, ROT_X | ROT_Y);
|
||||||
obj->SetBoneRotationFlags(1, ROT_X | ROT_Y);
|
obj->SetBoneRotationFlags(1, ROT_X | ROT_Y);
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_KOLD];
|
obj = &Objects[ID_KOLD];
|
||||||
|
@ -207,7 +207,7 @@ static void StartEntity(ObjectInfo* obj)
|
||||||
obj->intelligent = true;
|
obj->intelligent = true;
|
||||||
obj->SetBoneRotationFlags(1, ROT_Y);
|
obj->SetBoneRotationFlags(1, ROT_Y);
|
||||||
obj->SetBoneRotationFlags(0, ROT_X | ROT_Y);
|
obj->SetBoneRotationFlags(0, ROT_X | ROT_Y);
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_SKATEBOARD_KID];
|
obj = &Objects[ID_SKATEBOARD_KID];
|
||||||
|
@ -223,7 +223,7 @@ static void StartEntity(ObjectInfo* obj)
|
||||||
obj->intelligent = true;
|
obj->intelligent = true;
|
||||||
obj->SetBoneRotationFlags(7, ROT_Y); // Head.
|
obj->SetBoneRotationFlags(7, ROT_Y); // Head.
|
||||||
obj->SetBoneRotationFlags(0, ROT_Y | ROT_X); // Torso.
|
obj->SetBoneRotationFlags(0, ROT_Y | ROT_X); // Torso.
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -246,7 +246,7 @@ static void StartTrap(ObjectInfo* obj)
|
||||||
obj->control = ControlDamoclesSword;
|
obj->control = ControlDamoclesSword;
|
||||||
obj->collision = CollideDamoclesSword;
|
obj->collision = CollideDamoclesSword;
|
||||||
obj->shadowType = ShadowMode::All;
|
obj->shadowType = ShadowMode::All;
|
||||||
obj->SetupHitEffect(true);
|
obj->SetHitEffect(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_SLAMMING_DOORS];
|
obj = &Objects[ID_SLAMMING_DOORS];
|
||||||
|
@ -256,7 +256,7 @@ static void StartTrap(ObjectInfo* obj)
|
||||||
obj->control = ControlSlammingDoors;
|
obj->control = ControlSlammingDoors;
|
||||||
obj->collision = GenericSphereBoxCollision;
|
obj->collision = GenericSphereBoxCollision;
|
||||||
obj->shadowType = ShadowMode::All;
|
obj->shadowType = ShadowMode::All;
|
||||||
obj->SetupHitEffect(true);
|
obj->SetHitEffect(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_SWINGING_BLADE];
|
obj = &Objects[ID_SWINGING_BLADE];
|
||||||
|
@ -266,7 +266,7 @@ static void StartTrap(ObjectInfo* obj)
|
||||||
obj->control = ControlSwingingBlade;
|
obj->control = ControlSwingingBlade;
|
||||||
obj->collision = GenericSphereBoxCollision;
|
obj->collision = GenericSphereBoxCollision;
|
||||||
obj->shadowType = ShadowMode::All;
|
obj->shadowType = ShadowMode::All;
|
||||||
obj->SetupHitEffect(true);
|
obj->SetHitEffect(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -57,7 +57,7 @@ static void StartEntity(ObjectInfo* obj)
|
||||||
obj->waterCreature = true;
|
obj->waterCreature = true;
|
||||||
obj->LotType = LotType::Water;
|
obj->LotType = LotType::Water;
|
||||||
obj->SetBoneRotationFlags(9, ROT_Y);
|
obj->SetBoneRotationFlags(9, ROT_Y);
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_BARRACUDA];
|
obj = &Objects[ID_BARRACUDA];
|
||||||
|
@ -74,7 +74,7 @@ static void StartEntity(ObjectInfo* obj)
|
||||||
obj->waterCreature = true;
|
obj->waterCreature = true;
|
||||||
obj->LotType = LotType::Water;
|
obj->LotType = LotType::Water;
|
||||||
obj->SetBoneRotationFlags(6, ROT_Y);
|
obj->SetBoneRotationFlags(6, ROT_Y);
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_EAGLE];
|
obj = &Objects[ID_EAGLE];
|
||||||
|
@ -89,7 +89,7 @@ static void StartEntity(ObjectInfo* obj)
|
||||||
obj->intelligent = true;
|
obj->intelligent = true;
|
||||||
obj->pivotLength = 0;
|
obj->pivotLength = 0;
|
||||||
obj->LotType = LotType::Flyer;
|
obj->LotType = LotType::Flyer;
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_CROW];
|
obj = &Objects[ID_CROW];
|
||||||
|
@ -104,7 +104,7 @@ static void StartEntity(ObjectInfo* obj)
|
||||||
obj->intelligent = true;
|
obj->intelligent = true;
|
||||||
obj->pivotLength = 0;
|
obj->pivotLength = 0;
|
||||||
obj->LotType = LotType::Flyer;
|
obj->LotType = LotType::Flyer;
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_RAT];
|
obj = &Objects[ID_RAT];
|
||||||
|
@ -118,7 +118,7 @@ static void StartEntity(ObjectInfo* obj)
|
||||||
obj->pivotLength = 50;
|
obj->pivotLength = 50;
|
||||||
obj->radius = 204;
|
obj->radius = 204;
|
||||||
obj->intelligent = true;
|
obj->intelligent = true;
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_YETI];
|
obj = &Objects[ID_YETI];
|
||||||
|
@ -135,7 +135,7 @@ static void StartEntity(ObjectInfo* obj)
|
||||||
obj->LotType = LotType::Human;
|
obj->LotType = LotType::Human;
|
||||||
obj->SetBoneRotationFlags(6, ROT_Y);
|
obj->SetBoneRotationFlags(6, ROT_Y);
|
||||||
obj->SetBoneRotationFlags(14, ROT_Y);
|
obj->SetBoneRotationFlags(14, ROT_Y);
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_GOON_SILENCER1];
|
obj = &Objects[ID_GOON_SILENCER1];
|
||||||
|
@ -151,7 +151,7 @@ static void StartEntity(ObjectInfo* obj)
|
||||||
obj->intelligent = true;
|
obj->intelligent = true;
|
||||||
obj->SetBoneRotationFlags(0, ROT_X | ROT_Y);
|
obj->SetBoneRotationFlags(0, ROT_X | ROT_Y);
|
||||||
obj->SetBoneRotationFlags(1, ROT_X | ROT_Y);
|
obj->SetBoneRotationFlags(1, ROT_X | ROT_Y);
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_GOON_SILENCER2];
|
obj = &Objects[ID_GOON_SILENCER2];
|
||||||
|
@ -168,7 +168,7 @@ static void StartEntity(ObjectInfo* obj)
|
||||||
obj->intelligent = true;
|
obj->intelligent = true;
|
||||||
obj->SetBoneRotationFlags(0, ROT_X | ROT_Y);
|
obj->SetBoneRotationFlags(0, ROT_X | ROT_Y);
|
||||||
obj->SetBoneRotationFlags(1, ROT_X | ROT_Y);
|
obj->SetBoneRotationFlags(1, ROT_X | ROT_Y);
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_GOON_SILENCER3];
|
obj = &Objects[ID_GOON_SILENCER3];
|
||||||
|
@ -185,7 +185,7 @@ static void StartEntity(ObjectInfo* obj)
|
||||||
obj->intelligent = true;
|
obj->intelligent = true;
|
||||||
obj->SetBoneRotationFlags(0, ROT_X | ROT_Y);
|
obj->SetBoneRotationFlags(0, ROT_X | ROT_Y);
|
||||||
obj->SetBoneRotationFlags(1, ROT_X | ROT_Y);
|
obj->SetBoneRotationFlags(1, ROT_X | ROT_Y);
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_WORKER_SHOTGUN];
|
obj = &Objects[ID_WORKER_SHOTGUN];
|
||||||
|
@ -201,7 +201,7 @@ static void StartEntity(ObjectInfo* obj)
|
||||||
obj->intelligent = true;
|
obj->intelligent = true;
|
||||||
obj->SetBoneRotationFlags(4, ROT_X | ROT_Y);
|
obj->SetBoneRotationFlags(4, ROT_X | ROT_Y);
|
||||||
obj->SetBoneRotationFlags(13, ROT_X | ROT_Y);
|
obj->SetBoneRotationFlags(13, ROT_X | ROT_Y);
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_WORKER_MACHINEGUN];
|
obj = &Objects[ID_WORKER_MACHINEGUN];
|
||||||
|
@ -217,7 +217,7 @@ static void StartEntity(ObjectInfo* obj)
|
||||||
obj->intelligent = true;
|
obj->intelligent = true;
|
||||||
obj->SetBoneRotationFlags(4, ROT_X | ROT_Y);
|
obj->SetBoneRotationFlags(4, ROT_X | ROT_Y);
|
||||||
obj->SetBoneRotationFlags(13, ROT_X | ROT_Y);
|
obj->SetBoneRotationFlags(13, ROT_X | ROT_Y);
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_SMALL_SPIDER];
|
obj = &Objects[ID_SMALL_SPIDER];
|
||||||
|
@ -232,7 +232,7 @@ static void StartEntity(ObjectInfo* obj)
|
||||||
obj->radius = 102;
|
obj->radius = 102;
|
||||||
obj->intelligent = true;
|
obj->intelligent = true;
|
||||||
obj->LotType = LotType::Spider;
|
obj->LotType = LotType::Spider;
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_BIG_SPIDER];
|
obj = &Objects[ID_BIG_SPIDER];
|
||||||
|
@ -246,7 +246,7 @@ static void StartEntity(ObjectInfo* obj)
|
||||||
obj->pivotLength = 0;
|
obj->pivotLength = 0;
|
||||||
obj->radius = 102;
|
obj->radius = 102;
|
||||||
obj->intelligent = true;
|
obj->intelligent = true;
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_WORKER_DUAL_REVOLVER];
|
obj = &Objects[ID_WORKER_DUAL_REVOLVER];
|
||||||
|
@ -262,7 +262,7 @@ static void StartEntity(ObjectInfo* obj)
|
||||||
obj->intelligent = true;
|
obj->intelligent = true;
|
||||||
obj->SetBoneRotationFlags(0, ROT_X | ROT_Y);
|
obj->SetBoneRotationFlags(0, ROT_X | ROT_Y);
|
||||||
obj->SetBoneRotationFlags(11, ROT_X | ROT_Y);
|
obj->SetBoneRotationFlags(11, ROT_X | ROT_Y);
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_BIRDMONSTER];
|
obj = &Objects[ID_BIRDMONSTER];
|
||||||
|
@ -277,7 +277,7 @@ static void StartEntity(ObjectInfo* obj)
|
||||||
obj->radius = 341;
|
obj->radius = 341;
|
||||||
obj->intelligent = true;
|
obj->intelligent = true;
|
||||||
obj->SetBoneRotationFlags(14, ROT_X | ROT_Y);
|
obj->SetBoneRotationFlags(14, ROT_X | ROT_Y);
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_WORKER_FLAMETHROWER];
|
obj = &Objects[ID_WORKER_FLAMETHROWER];
|
||||||
|
@ -293,7 +293,7 @@ static void StartEntity(ObjectInfo* obj)
|
||||||
obj->intelligent = true;
|
obj->intelligent = true;
|
||||||
obj->SetBoneRotationFlags(4, ROT_X | ROT_Y);
|
obj->SetBoneRotationFlags(4, ROT_X | ROT_Y);
|
||||||
obj->SetBoneRotationFlags(14, ROT_X | ROT_Y);
|
obj->SetBoneRotationFlags(14, ROT_X | ROT_Y);
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_KNIFETHROWER];
|
obj = &Objects[ID_KNIFETHROWER];
|
||||||
|
@ -309,7 +309,7 @@ static void StartEntity(ObjectInfo* obj)
|
||||||
obj->intelligent = true;
|
obj->intelligent = true;
|
||||||
obj->SetBoneRotationFlags(6, ROT_X | ROT_Y);
|
obj->SetBoneRotationFlags(6, ROT_X | ROT_Y);
|
||||||
obj->SetBoneRotationFlags(8, ROT_X | ROT_Y);
|
obj->SetBoneRotationFlags(8, ROT_X | ROT_Y);
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_MERCENARY_UZI];
|
obj = &Objects[ID_MERCENARY_UZI];
|
||||||
|
@ -325,7 +325,7 @@ static void StartEntity(ObjectInfo* obj)
|
||||||
obj->intelligent = true;
|
obj->intelligent = true;
|
||||||
obj->SetBoneRotationFlags(6, ROT_X | ROT_Y);
|
obj->SetBoneRotationFlags(6, ROT_X | ROT_Y);
|
||||||
obj->SetBoneRotationFlags(8, ROT_X | ROT_Y);
|
obj->SetBoneRotationFlags(8, ROT_X | ROT_Y);
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_MERCENARY_AUTOPISTOLS1];
|
obj = &Objects[ID_MERCENARY_AUTOPISTOLS1];
|
||||||
|
@ -341,7 +341,7 @@ static void StartEntity(ObjectInfo* obj)
|
||||||
obj->intelligent = true;
|
obj->intelligent = true;
|
||||||
obj->SetBoneRotationFlags(6, ROT_X | ROT_Y);
|
obj->SetBoneRotationFlags(6, ROT_X | ROT_Y);
|
||||||
obj->SetBoneRotationFlags(8, ROT_X | ROT_Y);
|
obj->SetBoneRotationFlags(8, ROT_X | ROT_Y);
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_MERCENARY_AUTOPISTOLS2];
|
obj = &Objects[ID_MERCENARY_AUTOPISTOLS2];
|
||||||
|
@ -358,7 +358,7 @@ static void StartEntity(ObjectInfo* obj)
|
||||||
obj->intelligent = true;
|
obj->intelligent = true;
|
||||||
obj->SetBoneRotationFlags(6, ROT_X | ROT_Y);
|
obj->SetBoneRotationFlags(6, ROT_X | ROT_Y);
|
||||||
obj->SetBoneRotationFlags(8, ROT_X | ROT_Y);
|
obj->SetBoneRotationFlags(8, ROT_X | ROT_Y);
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_MONK1];
|
obj = &Objects[ID_MONK1];
|
||||||
|
@ -373,7 +373,7 @@ static void StartEntity(ObjectInfo* obj)
|
||||||
obj->radius = 204;
|
obj->radius = 204;
|
||||||
obj->intelligent = true;
|
obj->intelligent = true;
|
||||||
obj->SetBoneRotationFlags(6, ROT_X | ROT_Y);
|
obj->SetBoneRotationFlags(6, ROT_X | ROT_Y);
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_MONK2];
|
obj = &Objects[ID_MONK2];
|
||||||
|
@ -388,7 +388,7 @@ static void StartEntity(ObjectInfo* obj)
|
||||||
obj->radius = 204;
|
obj->radius = 204;
|
||||||
obj->intelligent = true;
|
obj->intelligent = true;
|
||||||
obj->SetBoneRotationFlags(6, ROT_X | ROT_Y);
|
obj->SetBoneRotationFlags(6, ROT_X | ROT_Y);
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_SWORD_GUARDIAN];
|
obj = &Objects[ID_SWORD_GUARDIAN];
|
||||||
|
@ -406,7 +406,7 @@ static void StartEntity(ObjectInfo* obj)
|
||||||
obj->intelligent = true;
|
obj->intelligent = true;
|
||||||
obj->SetBoneRotationFlags(6, ROT_X | ROT_Y);
|
obj->SetBoneRotationFlags(6, ROT_X | ROT_Y);
|
||||||
obj->SetBoneRotationFlags(17, ROT_X | ROT_Y);
|
obj->SetBoneRotationFlags(17, ROT_X | ROT_Y);
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_SPEAR_GUARDIAN];
|
obj = &Objects[ID_SPEAR_GUARDIAN];
|
||||||
|
@ -424,7 +424,7 @@ static void StartEntity(ObjectInfo* obj)
|
||||||
obj->intelligent = true;
|
obj->intelligent = true;
|
||||||
obj->SetBoneRotationFlags(6, ROT_X | ROT_Y);
|
obj->SetBoneRotationFlags(6, ROT_X | ROT_Y);
|
||||||
obj->SetBoneRotationFlags(12, ROT_X | ROT_Y);
|
obj->SetBoneRotationFlags(12, ROT_X | ROT_Y);
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_DRAGON_FRONT];
|
obj = &Objects[ID_DRAGON_FRONT];
|
||||||
|
@ -439,7 +439,7 @@ static void StartEntity(ObjectInfo* obj)
|
||||||
obj->radius = 256;
|
obj->radius = 256;
|
||||||
obj->intelligent = true;
|
obj->intelligent = true;
|
||||||
obj->SetBoneRotationFlags(10, ROT_Z);
|
obj->SetBoneRotationFlags(10, ROT_Z);
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_DRAGON_BACK];
|
obj = &Objects[ID_DRAGON_BACK];
|
||||||
|
@ -450,7 +450,7 @@ static void StartEntity(ObjectInfo* obj)
|
||||||
obj->collision = DragonCollision;
|
obj->collision = DragonCollision;
|
||||||
obj->control = DragonControl;
|
obj->control = DragonControl;
|
||||||
obj->radius = 256;
|
obj->radius = 256;
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_MARCO_BARTOLI];
|
obj = &Objects[ID_MARCO_BARTOLI];
|
||||||
|
@ -472,7 +472,7 @@ static void StartEntity(ObjectInfo* obj)
|
||||||
obj->radius = 256;
|
obj->radius = 256;
|
||||||
obj->intelligent = true;
|
obj->intelligent = true;
|
||||||
obj->LotType = LotType::SnowmobileGun;
|
obj->LotType = LotType::SnowmobileGun;
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_SNOWMOBILE_DRIVER];
|
obj = &Objects[ID_SNOWMOBILE_DRIVER];
|
||||||
|
@ -481,7 +481,7 @@ static void StartEntity(ObjectInfo* obj)
|
||||||
CheckIfSlotExists(ID_SNOWMOBILE_GUN, GetObjectName(ID_SNOWMOBILE_DRIVER));
|
CheckIfSlotExists(ID_SNOWMOBILE_GUN, GetObjectName(ID_SNOWMOBILE_DRIVER));
|
||||||
obj->Initialize = InitializeSkidooMan;
|
obj->Initialize = InitializeSkidooMan;
|
||||||
obj->control = SkidooManControl;
|
obj->control = SkidooManControl;
|
||||||
obj->SetupHitEffect(true);
|
obj->SetHitEffect(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -512,7 +512,7 @@ static void StartTrap(ObjectInfo* obj)
|
||||||
obj->Initialize = InitializeKillerStatue;
|
obj->Initialize = InitializeKillerStatue;
|
||||||
obj->control = KillerStatueControl;
|
obj->control = KillerStatueControl;
|
||||||
obj->collision = ObjectCollision;
|
obj->collision = ObjectCollision;
|
||||||
obj->SetupHitEffect(true);
|
obj->SetHitEffect(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -527,7 +527,7 @@ static void StartVehicles(ObjectInfo* obj)
|
||||||
obj->collision = SpeedboatPlayerCollision;
|
obj->collision = SpeedboatPlayerCollision;
|
||||||
obj->control = SpeedboatControl;
|
obj->control = SpeedboatControl;
|
||||||
obj->shadowType = ShadowMode::Lara;
|
obj->shadowType = ShadowMode::Lara;
|
||||||
obj->SetupHitEffect(true);
|
obj->SetHitEffect(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Create a new renderer for the skidoo with animated track.
|
// TODO: Create a new renderer for the skidoo with animated track.
|
||||||
|
@ -537,7 +537,7 @@ static void StartVehicles(ObjectInfo* obj)
|
||||||
obj->Initialize = InitializeSkidoo;
|
obj->Initialize = InitializeSkidoo;
|
||||||
obj->collision = SkidooPlayerCollision;
|
obj->collision = SkidooPlayerCollision;
|
||||||
obj->shadowType = ShadowMode::Lara;
|
obj->shadowType = ShadowMode::Lara;
|
||||||
obj->SetupHitEffect(true);
|
obj->SetHitEffect(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1027,7 +1027,7 @@ namespace TEN::Entities::Vehicles
|
||||||
{
|
{
|
||||||
if (TestBoundsCollide(item, kayakItem, KAYAK_TO_ENTITY_RADIUS))
|
if (TestBoundsCollide(item, kayakItem, KAYAK_TO_ENTITY_RADIUS))
|
||||||
{
|
{
|
||||||
DoLotsOfBlood(laraItem->Pose.Position.x, laraItem->Pose.Position.y - STEP_SIZE, laraItem->Pose.Position.z, kayakItem->Animation.Velocity.z, kayakItem->Pose.Orientation.y, laraItem->RoomNumber, 3);
|
DoLotsOfBlood(laraItem->Pose.Position.x, laraItem->Pose.Position.y - CLICK(1), laraItem->Pose.Position.z, kayakItem->Animation.Velocity.z, kayakItem->Pose.Orientation.y, laraItem->RoomNumber, 3);
|
||||||
DoDamage(laraItem, 5);
|
DoDamage(laraItem, 5);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,7 +70,7 @@ static void StartEntity(ObjectInfo* obj)
|
||||||
obj->nonLot = true; // NOTE: Doesn't move to reach the player, only throws projectiles.
|
obj->nonLot = true; // NOTE: Doesn't move to reach the player, only throws projectiles.
|
||||||
obj->SetBoneRotationFlags(6, ROT_X | ROT_Y);
|
obj->SetBoneRotationFlags(6, ROT_X | ROT_Y);
|
||||||
obj->SetBoneRotationFlags(13, ROT_Y);
|
obj->SetBoneRotationFlags(13, ROT_Y);
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_TIGER];
|
obj = &Objects[ID_TIGER];
|
||||||
|
@ -85,7 +85,7 @@ static void StartEntity(ObjectInfo* obj)
|
||||||
obj->radius = 340;
|
obj->radius = 340;
|
||||||
obj->intelligent = true;
|
obj->intelligent = true;
|
||||||
obj->SetBoneRotationFlags(21, ROT_Y);
|
obj->SetBoneRotationFlags(21, ROT_Y);
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_COBRA];
|
obj = &Objects[ID_COBRA];
|
||||||
|
@ -101,7 +101,7 @@ static void StartEntity(ObjectInfo* obj)
|
||||||
obj->nonLot = true;
|
obj->nonLot = true;
|
||||||
obj->SetBoneRotationFlags(0, ROT_Y);
|
obj->SetBoneRotationFlags(0, ROT_Y);
|
||||||
obj->SetBoneRotationFlags(6, ROT_Y);
|
obj->SetBoneRotationFlags(6, ROT_Y);
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_RAPTOR];
|
obj = &Objects[ID_RAPTOR];
|
||||||
|
@ -119,7 +119,7 @@ static void StartEntity(ObjectInfo* obj)
|
||||||
obj->SetBoneRotationFlags(21, ROT_Y);
|
obj->SetBoneRotationFlags(21, ROT_Y);
|
||||||
obj->SetBoneRotationFlags(23, ROT_Y);
|
obj->SetBoneRotationFlags(23, ROT_Y);
|
||||||
obj->SetBoneRotationFlags(25, ROT_Y);
|
obj->SetBoneRotationFlags(25, ROT_Y);
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_TRIBESMAN_WITH_AX];
|
obj = &Objects[ID_TRIBESMAN_WITH_AX];
|
||||||
|
@ -135,7 +135,7 @@ static void StartEntity(ObjectInfo* obj)
|
||||||
obj->pivotLength = 0;
|
obj->pivotLength = 0;
|
||||||
obj->SetBoneRotationFlags(6, ROT_Y);
|
obj->SetBoneRotationFlags(6, ROT_Y);
|
||||||
obj->SetBoneRotationFlags(13, ROT_Y);
|
obj->SetBoneRotationFlags(13, ROT_Y);
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_TRIBESMAN_WITH_DARTS];
|
obj = &Objects[ID_TRIBESMAN_WITH_DARTS];
|
||||||
|
@ -151,7 +151,7 @@ static void StartEntity(ObjectInfo* obj)
|
||||||
obj->pivotLength = 0;
|
obj->pivotLength = 0;
|
||||||
obj->SetBoneRotationFlags(6, ROT_Y);
|
obj->SetBoneRotationFlags(6, ROT_Y);
|
||||||
obj->SetBoneRotationFlags(13, ROT_Y);
|
obj->SetBoneRotationFlags(13, ROT_Y);
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_TYRANNOSAUR];
|
obj = &Objects[ID_TYRANNOSAUR];
|
||||||
|
@ -168,7 +168,7 @@ static void StartEntity(ObjectInfo* obj)
|
||||||
obj->LotType = LotType::Blockable;
|
obj->LotType = LotType::Blockable;
|
||||||
obj->SetBoneRotationFlags(10, ROT_Y);
|
obj->SetBoneRotationFlags(10, ROT_Y);
|
||||||
obj->SetBoneRotationFlags(11, ROT_Y);
|
obj->SetBoneRotationFlags(11, ROT_Y);
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_SCUBA_DIVER];
|
obj = &Objects[ID_SCUBA_DIVER];
|
||||||
|
@ -186,7 +186,7 @@ static void StartEntity(ObjectInfo* obj)
|
||||||
obj->LotType = LotType::Water;
|
obj->LotType = LotType::Water;
|
||||||
obj->SetBoneRotationFlags(10, ROT_X | ROT_Y);
|
obj->SetBoneRotationFlags(10, ROT_X | ROT_Y);
|
||||||
obj->SetBoneRotationFlags(14, ROT_Y);
|
obj->SetBoneRotationFlags(14, ROT_Y);
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_SCUBA_HARPOON];
|
obj = &Objects[ID_SCUBA_HARPOON];
|
||||||
|
@ -209,7 +209,7 @@ static void StartEntity(ObjectInfo* obj)
|
||||||
obj->pivotLength = 0;
|
obj->pivotLength = 0;
|
||||||
obj->SetBoneRotationFlags(0, ROT_X | ROT_Y);
|
obj->SetBoneRotationFlags(0, ROT_X | ROT_Y);
|
||||||
obj->SetBoneRotationFlags(7, ROT_Y);
|
obj->SetBoneRotationFlags(7, ROT_Y);
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_MONKEY];
|
obj = &Objects[ID_MONKEY];
|
||||||
|
@ -227,7 +227,7 @@ static void StartEntity(ObjectInfo* obj)
|
||||||
obj->pivotLength = 0;
|
obj->pivotLength = 0;
|
||||||
obj->SetBoneRotationFlags(0, ROT_X | ROT_Y);
|
obj->SetBoneRotationFlags(0, ROT_X | ROT_Y);
|
||||||
obj->SetBoneRotationFlags(7, ROT_Y);
|
obj->SetBoneRotationFlags(7, ROT_Y);
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_MP_WITH_GUN];
|
obj = &Objects[ID_MP_WITH_GUN];
|
||||||
|
@ -243,7 +243,7 @@ static void StartEntity(ObjectInfo* obj)
|
||||||
obj->pivotLength = 0;
|
obj->pivotLength = 0;
|
||||||
obj->SetBoneRotationFlags(6, ROT_X | ROT_Y);
|
obj->SetBoneRotationFlags(6, ROT_X | ROT_Y);
|
||||||
obj->SetBoneRotationFlags(13, ROT_Y);
|
obj->SetBoneRotationFlags(13, ROT_Y);
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_MP_WITH_STICK];
|
obj = &Objects[ID_MP_WITH_STICK];
|
||||||
|
@ -260,7 +260,7 @@ static void StartEntity(ObjectInfo* obj)
|
||||||
obj->LotType = LotType::Human;
|
obj->LotType = LotType::Human;
|
||||||
obj->SetBoneRotationFlags(6, ROT_X | ROT_Y);
|
obj->SetBoneRotationFlags(6, ROT_X | ROT_Y);
|
||||||
obj->SetBoneRotationFlags(13, ROT_Y);
|
obj->SetBoneRotationFlags(13, ROT_Y);
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_SHIVA];
|
obj = &Objects[ID_SHIVA];
|
||||||
|
@ -279,7 +279,7 @@ static void StartEntity(ObjectInfo* obj)
|
||||||
obj->LotType = LotType::Blockable;
|
obj->LotType = LotType::Blockable;
|
||||||
obj->SetBoneRotationFlags(6, ROT_X | ROT_Y);
|
obj->SetBoneRotationFlags(6, ROT_X | ROT_Y);
|
||||||
obj->SetBoneRotationFlags(25, ROT_X | ROT_Y);
|
obj->SetBoneRotationFlags(25, ROT_X | ROT_Y);
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_SOPHIA_LEIGH_BOSS];
|
obj = &Objects[ID_SOPHIA_LEIGH_BOSS];
|
||||||
|
@ -296,7 +296,7 @@ static void StartEntity(ObjectInfo* obj)
|
||||||
obj->LotType = LotType::Human;
|
obj->LotType = LotType::Human;
|
||||||
obj->SetBoneRotationFlags(6, ROT_X | ROT_Y);
|
obj->SetBoneRotationFlags(6, ROT_X | ROT_Y);
|
||||||
obj->SetBoneRotationFlags(13, ROT_Y);
|
obj->SetBoneRotationFlags(13, ROT_Y);
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_CIVVY];
|
obj = &Objects[ID_CIVVY];
|
||||||
|
@ -313,7 +313,7 @@ static void StartEntity(ObjectInfo* obj)
|
||||||
obj->LotType = LotType::Human;
|
obj->LotType = LotType::Human;
|
||||||
obj->SetBoneRotationFlags(6, ROT_X | ROT_Y);
|
obj->SetBoneRotationFlags(6, ROT_X | ROT_Y);
|
||||||
obj->SetBoneRotationFlags(13, ROT_Y);
|
obj->SetBoneRotationFlags(13, ROT_Y);
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_LIZARD];
|
obj = &Objects[ID_LIZARD];
|
||||||
|
@ -329,7 +329,7 @@ static void StartEntity(ObjectInfo* obj)
|
||||||
obj->radius = 204;
|
obj->radius = 204;
|
||||||
obj->LotType = LotType::Human;
|
obj->LotType = LotType::Human;
|
||||||
obj->SetBoneRotationFlags(9, ROT_X | ROT_Z);
|
obj->SetBoneRotationFlags(9, ROT_X | ROT_Z);
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_PUNA_BOSS];
|
obj = &Objects[ID_PUNA_BOSS];
|
||||||
|
@ -347,7 +347,7 @@ static void StartEntity(ObjectInfo* obj)
|
||||||
obj->pivotLength = 50;
|
obj->pivotLength = 50;
|
||||||
obj->SetBoneRotationFlags(4, ROT_Y); // Puna quest object.
|
obj->SetBoneRotationFlags(4, ROT_Y); // Puna quest object.
|
||||||
obj->SetBoneRotationFlags(7, ROT_X | ROT_Y); // Head.
|
obj->SetBoneRotationFlags(7, ROT_X | ROT_Y); // Head.
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_WASP_MUTANT];
|
obj = &Objects[ID_WASP_MUTANT];
|
||||||
|
@ -362,7 +362,7 @@ static void StartEntity(ObjectInfo* obj)
|
||||||
obj->radius = 102;
|
obj->radius = 102;
|
||||||
obj->pivotLength = 0;
|
obj->pivotLength = 0;
|
||||||
obj->LotType = LotType::Flyer;
|
obj->LotType = LotType::Flyer;
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_COMPSOGNATHUS];
|
obj = &Objects[ID_COMPSOGNATHUS];
|
||||||
|
@ -377,7 +377,7 @@ static void StartEntity(ObjectInfo* obj)
|
||||||
obj->radius = 204;
|
obj->radius = 204;
|
||||||
obj->pivotLength = 0;
|
obj->pivotLength = 0;
|
||||||
obj->SetBoneRotationFlags(1, ROT_X | ROT_Z);
|
obj->SetBoneRotationFlags(1, ROT_X | ROT_Z);
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_CLAW_MUTANT];
|
obj = &Objects[ID_CLAW_MUTANT];
|
||||||
|
@ -393,7 +393,7 @@ static void StartEntity(ObjectInfo* obj)
|
||||||
obj->pivotLength = 0;
|
obj->pivotLength = 0;
|
||||||
obj->SetBoneRotationFlags(0, ROT_X | ROT_Z);
|
obj->SetBoneRotationFlags(0, ROT_X | ROT_Z);
|
||||||
obj->SetBoneRotationFlags(7, ROT_Y);
|
obj->SetBoneRotationFlags(7, ROT_Y);
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -436,7 +436,7 @@ static void StartObject(ObjectInfo* obj)
|
||||||
obj->HitRoutine = CorpseHit;
|
obj->HitRoutine = CorpseHit;
|
||||||
obj->HitPoints = NOT_TARGETABLE;
|
obj->HitPoints = NOT_TARGETABLE;
|
||||||
obj->shadowType = ShadowMode::None;
|
obj->shadowType = ShadowMode::None;
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -447,7 +447,7 @@ static void StartTrap(ObjectInfo* obj)
|
||||||
{
|
{
|
||||||
obj->control = TrainControl;
|
obj->control = TrainControl;
|
||||||
obj->collision = TrainCollision;
|
obj->collision = TrainCollision;
|
||||||
obj->SetupHitEffect(true);
|
obj->SetHitEffect(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_ELECTRIC_CLEANER];
|
obj = &Objects[ID_ELECTRIC_CLEANER];
|
||||||
|
@ -479,7 +479,7 @@ static void StartVehicles(ObjectInfo* obj)
|
||||||
obj->Initialize = InitializeQuadBike;
|
obj->Initialize = InitializeQuadBike;
|
||||||
obj->collision = QuadBikePlayerCollision;
|
obj->collision = QuadBikePlayerCollision;
|
||||||
obj->shadowType = ShadowMode::Lara;
|
obj->shadowType = ShadowMode::Lara;
|
||||||
obj->SetupHitEffect(true);
|
obj->SetHitEffect(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_RUBBER_BOAT];
|
obj = &Objects[ID_RUBBER_BOAT];
|
||||||
|
@ -490,7 +490,7 @@ static void StartVehicles(ObjectInfo* obj)
|
||||||
obj->collision = RubberBoatPlayerCollision;
|
obj->collision = RubberBoatPlayerCollision;
|
||||||
obj->drawRoutine = DrawRubberBoat;
|
obj->drawRoutine = DrawRubberBoat;
|
||||||
obj->shadowType = ShadowMode::Lara;
|
obj->shadowType = ShadowMode::Lara;
|
||||||
obj->SetupHitEffect(true);
|
obj->SetHitEffect(true);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -500,7 +500,7 @@ static void StartVehicles(ObjectInfo* obj)
|
||||||
obj->Initialize = InitializeKayak;
|
obj->Initialize = InitializeKayak;
|
||||||
obj->collision = KayakPlayerCollision;
|
obj->collision = KayakPlayerCollision;
|
||||||
obj->shadowType = ShadowMode::Lara;
|
obj->shadowType = ShadowMode::Lara;
|
||||||
obj->SetupHitEffect(true);
|
obj->SetHitEffect(true);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -510,7 +510,7 @@ static void StartVehicles(ObjectInfo* obj)
|
||||||
obj->Initialize = InitializeMinecart;
|
obj->Initialize = InitializeMinecart;
|
||||||
obj->collision = MinecartPlayerCollision;
|
obj->collision = MinecartPlayerCollision;
|
||||||
obj->shadowType = ShadowMode::Lara;
|
obj->shadowType = ShadowMode::Lara;
|
||||||
obj->SetupHitEffect(true);
|
obj->SetHitEffect(true);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -520,7 +520,7 @@ static void StartVehicles(ObjectInfo* obj)
|
||||||
obj->Initialize = BigGunInitialize;
|
obj->Initialize = BigGunInitialize;
|
||||||
obj->collision = BigGunCollision;
|
obj->collision = BigGunCollision;
|
||||||
obj->shadowType = ShadowMode::Lara;
|
obj->shadowType = ShadowMode::Lara;
|
||||||
obj->SetupHitEffect(true);
|
obj->SetHitEffect(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_UPV];
|
obj = &Objects[ID_UPV];
|
||||||
|
@ -530,7 +530,7 @@ static void StartVehicles(ObjectInfo* obj)
|
||||||
obj->control = UPVEffects;
|
obj->control = UPVEffects;
|
||||||
obj->collision = UPVPlayerCollision;
|
obj->collision = UPVPlayerCollision;
|
||||||
obj->shadowType = ShadowMode::Lara;
|
obj->shadowType = ShadowMode::Lara;
|
||||||
obj->SetupHitEffect(true);
|
obj->SetHitEffect(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -194,7 +194,7 @@ namespace TEN::Entities::TR4
|
||||||
{
|
{
|
||||||
if (abs(pos.x - mesh.pos.Position.x) < BLOCK(1) &&
|
if (abs(pos.x - mesh.pos.Position.x) < BLOCK(1) &&
|
||||||
abs(pos.z - mesh.pos.Position.z) < BLOCK(1) &&
|
abs(pos.z - mesh.pos.Position.z) < BLOCK(1) &&
|
||||||
StaticObjects[mesh.staticNumber].shatterType == SHT_NONE)
|
StaticObjects[mesh.staticNumber].shatterType == ShatterType::None)
|
||||||
{
|
{
|
||||||
ShatterObject(nullptr, &mesh, -64, LaraItem->RoomNumber, 0);
|
ShatterObject(nullptr, &mesh, -64, LaraItem->RoomNumber, 0);
|
||||||
SoundEffect(SFX_TR4_SMASH_ROCK, &item->Pose);
|
SoundEffect(SFX_TR4_SMASH_ROCK, &item->Pose);
|
||||||
|
|
|
@ -650,7 +650,7 @@ namespace TEN::Entities::TR4
|
||||||
|
|
||||||
if (abs(pos.x - staticMesh->pos.Position.x) < BLOCK(1) &&
|
if (abs(pos.x - staticMesh->pos.Position.x) < BLOCK(1) &&
|
||||||
abs(pos.z - staticMesh->pos.Position.z) < BLOCK(1) &&
|
abs(pos.z - staticMesh->pos.Position.z) < BLOCK(1) &&
|
||||||
StaticObjects[staticMesh->staticNumber].shatterType != SHT_NONE)
|
StaticObjects[staticMesh->staticNumber].shatterType != ShatterType::None)
|
||||||
{
|
{
|
||||||
ShatterObject(0, staticMesh, -128, LaraItem->RoomNumber, 0);
|
ShatterObject(0, staticMesh, -128, LaraItem->RoomNumber, 0);
|
||||||
SoundEffect(SFX_TR4_SMASH_ROCK, &item->Pose);
|
SoundEffect(SFX_TR4_SMASH_ROCK, &item->Pose);
|
||||||
|
|
|
@ -92,7 +92,7 @@ namespace TEN::Entities::TR4
|
||||||
|
|
||||||
if (((mesh->pos.Position.z / BLOCK(1)) == (z / BLOCK(1))) &&
|
if (((mesh->pos.Position.z / BLOCK(1)) == (z / BLOCK(1))) &&
|
||||||
((mesh->pos.Position.x / BLOCK(1)) == (x / BLOCK(1))) &&
|
((mesh->pos.Position.x / BLOCK(1)) == (x / BLOCK(1))) &&
|
||||||
StaticObjects[mesh->staticNumber].shatterType != SHT_NONE)
|
StaticObjects[mesh->staticNumber].shatterType != ShatterType::None)
|
||||||
{
|
{
|
||||||
ShatterObject(nullptr, mesh, -64, item->RoomNumber, 0);
|
ShatterObject(nullptr, mesh, -64, item->RoomNumber, 0);
|
||||||
SoundEffect(SFX_TR4_SMASH_ROCK, &item->Pose);
|
SoundEffect(SFX_TR4_SMASH_ROCK, &item->Pose);
|
||||||
|
|
|
@ -133,7 +133,8 @@ namespace TEN::Entities::TR4
|
||||||
{
|
{
|
||||||
target = &g_Level.Items[targetItem];
|
target = &g_Level.Items[targetItem];
|
||||||
|
|
||||||
if (target->ObjectNumber >= ID_PUSHABLE_OBJECT1 && target->ObjectNumber <= ID_PUSHABLE_OBJECT10)
|
if ( (target->ObjectNumber >= ID_PUSHABLE_OBJECT1 && target->ObjectNumber <= ID_PUSHABLE_OBJECT10) ||
|
||||||
|
(target->ObjectNumber >= ID_PUSHABLE_OBJECT_CLIMBABLE1 && target->ObjectNumber <= ID_PUSHABLE_OBJECT_CLIMBABLE10))
|
||||||
{
|
{
|
||||||
if (item->Pose.Position.x == target->Pose.Position.x &&
|
if (item->Pose.Position.x == target->Pose.Position.x &&
|
||||||
item->Pose.Position.z == target->Pose.Position.z)
|
item->Pose.Position.z == target->Pose.Position.z)
|
||||||
|
|
|
@ -266,7 +266,7 @@ namespace TEN::Entities::Vehicles
|
||||||
FloorInfo* floor = GetFloor(old->x, pos->y, pos->z, &roomNumber);
|
FloorInfo* floor = GetFloor(old->x, pos->y, pos->z, &roomNumber);
|
||||||
int height = GetFloorHeight(floor, old->x, pos->y, pos->z);
|
int height = GetFloorHeight(floor, old->x, pos->y, pos->z);
|
||||||
|
|
||||||
if (height < old->y - STEP_SIZE)
|
if (height < old->y - CLICK(1))
|
||||||
{
|
{
|
||||||
if (pos->z > old->z)
|
if (pos->z > old->z)
|
||||||
z = -1 - shiftZ;
|
z = -1 - shiftZ;
|
||||||
|
@ -278,7 +278,7 @@ namespace TEN::Entities::Vehicles
|
||||||
floor = GetFloor(pos->x, pos->y, old->z, &roomNumber);
|
floor = GetFloor(pos->x, pos->y, old->z, &roomNumber);
|
||||||
height = GetFloorHeight(floor, pos->x, pos->y, old->z);
|
height = GetFloorHeight(floor, pos->x, pos->y, old->z);
|
||||||
|
|
||||||
if (height < old->y - STEP_SIZE)
|
if (height < old->y - CLICK(1))
|
||||||
{
|
{
|
||||||
if (pos->x > old->x)
|
if (pos->x > old->x)
|
||||||
x = -1 - shiftX;
|
x = -1 - shiftX;
|
||||||
|
@ -642,11 +642,11 @@ namespace TEN::Entities::Vehicles
|
||||||
int rot2 = 0;
|
int rot2 = 0;
|
||||||
|
|
||||||
int hf = GetVehicleHeight(jeepItem, JEEP_FRONT, -JEEP_SIDE, false, &f);
|
int hf = GetVehicleHeight(jeepItem, JEEP_FRONT, -JEEP_SIDE, false, &f);
|
||||||
if (hf < f_old.y - STEP_SIZE)
|
if (hf < f_old.y - CLICK(1))
|
||||||
rot1 = abs(4 * DoJeepShift(jeepItem, &f, &f_old));
|
rot1 = abs(4 * DoJeepShift(jeepItem, &f, &f_old));
|
||||||
|
|
||||||
int hmm = GetVehicleHeight(jeepItem, -(JEEP_FRONT + 50), -JEEP_SIDE, false, &mm);
|
int hmm = GetVehicleHeight(jeepItem, -(JEEP_FRONT + 50), -JEEP_SIDE, false, &mm);
|
||||||
if (hmm < mm_old.y - STEP_SIZE)
|
if (hmm < mm_old.y - CLICK(1))
|
||||||
{
|
{
|
||||||
if (rot)
|
if (rot)
|
||||||
rot1 += abs(4 * DoJeepShift(jeepItem, &mm, &mm_old));
|
rot1 += abs(4 * DoJeepShift(jeepItem, &mm, &mm_old));
|
||||||
|
@ -655,15 +655,15 @@ namespace TEN::Entities::Vehicles
|
||||||
}
|
}
|
||||||
|
|
||||||
int hb = GetVehicleHeight(jeepItem, JEEP_FRONT, JEEP_SIDE, false, &b);
|
int hb = GetVehicleHeight(jeepItem, JEEP_FRONT, JEEP_SIDE, false, &b);
|
||||||
if (hb < b_old.y - STEP_SIZE)
|
if (hb < b_old.y - CLICK(1))
|
||||||
rot2 = -abs(4 * DoJeepShift(jeepItem, &b, &b_old));
|
rot2 = -abs(4 * DoJeepShift(jeepItem, &b, &b_old));
|
||||||
|
|
||||||
int hmb = GetVehicleHeight(jeepItem, -(JEEP_FRONT + 50), 0, false, &mb);
|
int hmb = GetVehicleHeight(jeepItem, -(JEEP_FRONT + 50), 0, false, &mb);
|
||||||
if (hmb < mb_old.y - STEP_SIZE)
|
if (hmb < mb_old.y - CLICK(1))
|
||||||
DoJeepShift(jeepItem, &mb, &mb_old);
|
DoJeepShift(jeepItem, &mb, &mb_old);
|
||||||
|
|
||||||
int hmt = GetVehicleHeight(jeepItem, -(JEEP_FRONT + 50), JEEP_SIDE, false, &mt);
|
int hmt = GetVehicleHeight(jeepItem, -(JEEP_FRONT + 50), JEEP_SIDE, false, &mt);
|
||||||
if (hmt < mt_old.y - STEP_SIZE)
|
if (hmt < mt_old.y - CLICK(1))
|
||||||
{
|
{
|
||||||
if (rot2)
|
if (rot2)
|
||||||
rot2 -= abs(4 * DoJeepShift(jeepItem, &mt, &mt_old));
|
rot2 -= abs(4 * DoJeepShift(jeepItem, &mt, &mt_old));
|
||||||
|
@ -676,7 +676,7 @@ namespace TEN::Entities::Vehicles
|
||||||
|
|
||||||
roomNumber = jeepItem->RoomNumber;
|
roomNumber = jeepItem->RoomNumber;
|
||||||
floor = GetFloor(jeepItem->Pose.Position.x, jeepItem->Pose.Position.y, jeepItem->Pose.Position.z, &roomNumber);
|
floor = GetFloor(jeepItem->Pose.Position.x, jeepItem->Pose.Position.y, jeepItem->Pose.Position.z, &roomNumber);
|
||||||
if (GetFloorHeight(floor, jeepItem->Pose.Position.x, jeepItem->Pose.Position.y, jeepItem->Pose.Position.z) < jeepItem->Pose.Position.y - STEP_SIZE)
|
if (GetFloorHeight(floor, jeepItem->Pose.Position.x, jeepItem->Pose.Position.y, jeepItem->Pose.Position.z) < jeepItem->Pose.Position.y - CLICK(1))
|
||||||
DoJeepShift(jeepItem, (Vector3i*)&jeepItem->Pose, &oldPos);
|
DoJeepShift(jeepItem, (Vector3i*)&jeepItem->Pose, &oldPos);
|
||||||
|
|
||||||
if (!jeep->Velocity)
|
if (!jeep->Velocity)
|
||||||
|
@ -735,7 +735,7 @@ namespace TEN::Entities::Vehicles
|
||||||
int rot1 = 0;
|
int rot1 = 0;
|
||||||
int rot2 = 0;
|
int rot2 = 0;
|
||||||
|
|
||||||
if (jeepItem->Pose.Position.y >= height - STEP_SIZE)
|
if (jeepItem->Pose.Position.y >= height - CLICK(1))
|
||||||
{
|
{
|
||||||
lara->Control.Look.Mode = (jeepItem->Animation.Velocity.z == 0.0f) ? LookMode::Horizontal : LookMode::Free;
|
lara->Control.Look.Mode = (jeepItem->Animation.Velocity.z == 0.0f) ? LookMode::Horizontal : LookMode::Free;
|
||||||
|
|
||||||
|
|
|
@ -100,7 +100,7 @@ namespace TEN::Entities
|
||||||
obj->pivotLength = 20;
|
obj->pivotLength = 20;
|
||||||
obj->radius = 128;
|
obj->radius = 128;
|
||||||
obj->intelligent = true;
|
obj->intelligent = true;
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_BIG_SCORPION];
|
obj = &Objects[ID_BIG_SCORPION];
|
||||||
|
@ -114,7 +114,7 @@ namespace TEN::Entities
|
||||||
obj->pivotLength = 50;
|
obj->pivotLength = 50;
|
||||||
obj->radius = 512;
|
obj->radius = 512;
|
||||||
obj->intelligent = true;
|
obj->intelligent = true;
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_HAMMERHEAD];
|
obj = &Objects[ID_HAMMERHEAD];
|
||||||
|
@ -130,7 +130,7 @@ namespace TEN::Entities
|
||||||
obj->intelligent = true;
|
obj->intelligent = true;
|
||||||
obj->waterCreature = true;
|
obj->waterCreature = true;
|
||||||
obj->LotType = LotType::Water;
|
obj->LotType = LotType::Water;
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_WILD_BOAR];
|
obj = &Objects[ID_WILD_BOAR];
|
||||||
|
@ -146,7 +146,7 @@ namespace TEN::Entities
|
||||||
obj->intelligent = true;
|
obj->intelligent = true;
|
||||||
obj->SetBoneRotationFlags(12, ROT_Y | ROT_Z);
|
obj->SetBoneRotationFlags(12, ROT_Y | ROT_Z);
|
||||||
obj->SetBoneRotationFlags(13, ROT_Y | ROT_Z);
|
obj->SetBoneRotationFlags(13, ROT_Y | ROT_Z);
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_DOG];
|
obj = &Objects[ID_DOG];
|
||||||
|
@ -162,7 +162,7 @@ namespace TEN::Entities
|
||||||
obj->intelligent = true;
|
obj->intelligent = true;
|
||||||
obj->SetBoneRotationFlags(0, ROT_Y);
|
obj->SetBoneRotationFlags(0, ROT_Y);
|
||||||
obj->SetBoneRotationFlags(2, ROT_X | ROT_Y);
|
obj->SetBoneRotationFlags(2, ROT_X | ROT_Y);
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_BAT];
|
obj = &Objects[ID_BAT];
|
||||||
|
@ -177,7 +177,7 @@ namespace TEN::Entities
|
||||||
obj->radius = 102;
|
obj->radius = 102;
|
||||||
obj->intelligent = true;
|
obj->intelligent = true;
|
||||||
obj->LotType = LotType::Flyer;
|
obj->LotType = LotType::Flyer;
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_AHMET];
|
obj = &Objects[ID_AHMET];
|
||||||
|
@ -192,7 +192,7 @@ namespace TEN::Entities
|
||||||
obj->radius = 341;
|
obj->radius = 341;
|
||||||
obj->intelligent = true;
|
obj->intelligent = true;
|
||||||
obj->SetBoneRotationFlags(9, ROT_Y);
|
obj->SetBoneRotationFlags(9, ROT_Y);
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_BADDY1];
|
obj = &Objects[ID_BADDY1];
|
||||||
|
@ -211,7 +211,7 @@ namespace TEN::Entities
|
||||||
obj->LotType = LotType::HumanPlusJumpAndMonkey;
|
obj->LotType = LotType::HumanPlusJumpAndMonkey;
|
||||||
obj->SetBoneRotationFlags(7, ROT_X | ROT_Y);
|
obj->SetBoneRotationFlags(7, ROT_X | ROT_Y);
|
||||||
obj->SetBoneRotationFlags(22, ROT_X | ROT_Y);
|
obj->SetBoneRotationFlags(22, ROT_X | ROT_Y);
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_BADDY2];
|
obj = &Objects[ID_BADDY2];
|
||||||
|
@ -231,7 +231,7 @@ namespace TEN::Entities
|
||||||
obj->LotType = LotType::HumanPlusJumpAndMonkey;
|
obj->LotType = LotType::HumanPlusJumpAndMonkey;
|
||||||
obj->SetBoneRotationFlags(7, ROT_X | ROT_Y);
|
obj->SetBoneRotationFlags(7, ROT_X | ROT_Y);
|
||||||
obj->SetBoneRotationFlags(22, ROT_X | ROT_Y);
|
obj->SetBoneRotationFlags(22, ROT_X | ROT_Y);
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_SAS_CAIRO];
|
obj = &Objects[ID_SAS_CAIRO];
|
||||||
|
@ -247,7 +247,7 @@ namespace TEN::Entities
|
||||||
obj->intelligent = true;
|
obj->intelligent = true;
|
||||||
obj->SetBoneRotationFlags(0, ROT_X | ROT_Y);
|
obj->SetBoneRotationFlags(0, ROT_X | ROT_Y);
|
||||||
obj->SetBoneRotationFlags(7, ROT_X | ROT_Y);
|
obj->SetBoneRotationFlags(7, ROT_X | ROT_Y);
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_MUMMY];
|
obj = &Objects[ID_MUMMY];
|
||||||
|
@ -260,9 +260,9 @@ namespace TEN::Entities
|
||||||
obj->HitPoints = 15;
|
obj->HitPoints = 15;
|
||||||
obj->radius = 170;
|
obj->radius = 170;
|
||||||
obj->intelligent = true;
|
obj->intelligent = true;
|
||||||
obj->damageType = DamageMode::ExplosivesOnly;
|
obj->damageType = DamageMode::Explosion;
|
||||||
obj->SetBoneRotationFlags(7, ROT_X | ROT_Y);
|
obj->SetBoneRotationFlags(7, ROT_X | ROT_Y);
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_SKELETON];
|
obj = &Objects[ID_SKELETON];
|
||||||
|
@ -277,9 +277,9 @@ namespace TEN::Entities
|
||||||
obj->radius = 128;
|
obj->radius = 128;
|
||||||
obj->explodableMeshbits = 0xA00;
|
obj->explodableMeshbits = 0xA00;
|
||||||
obj->intelligent = true;
|
obj->intelligent = true;
|
||||||
obj->damageType = DamageMode::ExplosivesOnly;
|
obj->damageType = DamageMode::Explosion;
|
||||||
obj->LotType = LotType::Skeleton;
|
obj->LotType = LotType::Skeleton;
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_KNIGHT_TEMPLAR];
|
obj = &Objects[ID_KNIGHT_TEMPLAR];
|
||||||
|
@ -296,7 +296,7 @@ namespace TEN::Entities
|
||||||
obj->damageType = DamageMode::None;
|
obj->damageType = DamageMode::None;
|
||||||
obj->SetBoneRotationFlags(6, ROT_X | ROT_Y);
|
obj->SetBoneRotationFlags(6, ROT_X | ROT_Y);
|
||||||
obj->SetBoneRotationFlags(7, ROT_Y);
|
obj->SetBoneRotationFlags(7, ROT_Y);
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_BIG_BEETLE];
|
obj = &Objects[ID_BIG_BEETLE];
|
||||||
|
@ -311,7 +311,7 @@ namespace TEN::Entities
|
||||||
obj->radius = 204;
|
obj->radius = 204;
|
||||||
obj->intelligent = true;
|
obj->intelligent = true;
|
||||||
obj->LotType = LotType::Flyer;
|
obj->LotType = LotType::Flyer;
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_SETHA];
|
obj = &Objects[ID_SETHA];
|
||||||
|
@ -327,7 +327,7 @@ namespace TEN::Entities
|
||||||
obj->intelligent = true;
|
obj->intelligent = true;
|
||||||
obj->damageType = DamageMode::None;
|
obj->damageType = DamageMode::None;
|
||||||
obj->LotType = LotType::Skeleton;
|
obj->LotType = LotType::Skeleton;
|
||||||
obj->SetupHitEffect(true);
|
obj->SetHitEffect(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_DEMIGOD1];
|
obj = &Objects[ID_DEMIGOD1];
|
||||||
|
@ -344,7 +344,7 @@ namespace TEN::Entities
|
||||||
obj->damageType = DamageMode::None;
|
obj->damageType = DamageMode::None;
|
||||||
obj->SetBoneRotationFlags(8, ROT_X | ROT_Y | ROT_Z);
|
obj->SetBoneRotationFlags(8, ROT_X | ROT_Y | ROT_Z);
|
||||||
obj->SetBoneRotationFlags(20, ROT_Y);
|
obj->SetBoneRotationFlags(20, ROT_Y);
|
||||||
obj->SetupHitEffect(true);
|
obj->SetHitEffect(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_DEMIGOD2];
|
obj = &Objects[ID_DEMIGOD2];
|
||||||
|
@ -360,7 +360,7 @@ namespace TEN::Entities
|
||||||
obj->intelligent = true;
|
obj->intelligent = true;
|
||||||
obj->SetBoneRotationFlags(8, ROT_X | ROT_Y | ROT_Z);
|
obj->SetBoneRotationFlags(8, ROT_X | ROT_Y | ROT_Z);
|
||||||
obj->SetBoneRotationFlags(20, ROT_Y);
|
obj->SetBoneRotationFlags(20, ROT_Y);
|
||||||
obj->SetupHitEffect(true);
|
obj->SetHitEffect(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_DEMIGOD3];
|
obj = &Objects[ID_DEMIGOD3];
|
||||||
|
@ -376,7 +376,7 @@ namespace TEN::Entities
|
||||||
obj->intelligent = true;
|
obj->intelligent = true;
|
||||||
obj->SetBoneRotationFlags(8, ROT_X | ROT_Y | ROT_Z);
|
obj->SetBoneRotationFlags(8, ROT_X | ROT_Y | ROT_Z);
|
||||||
obj->SetBoneRotationFlags(20, ROT_Y);
|
obj->SetBoneRotationFlags(20, ROT_Y);
|
||||||
obj->SetupHitEffect(true);
|
obj->SetHitEffect(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_JEAN_YVES];
|
obj = &Objects[ID_JEAN_YVES];
|
||||||
|
@ -386,7 +386,7 @@ namespace TEN::Entities
|
||||||
obj->control = JeanYvesControl;
|
obj->control = JeanYvesControl;
|
||||||
obj->collision = ObjectCollision;
|
obj->collision = ObjectCollision;
|
||||||
obj->nonLot = true;
|
obj->nonLot = true;
|
||||||
obj->SetupHitEffect(true);
|
obj->SetHitEffect(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_TROOPS];
|
obj = &Objects[ID_TROOPS];
|
||||||
|
@ -402,7 +402,7 @@ namespace TEN::Entities
|
||||||
obj->intelligent = true;
|
obj->intelligent = true;
|
||||||
obj->SetBoneRotationFlags(0, ROT_X | ROT_Y);
|
obj->SetBoneRotationFlags(0, ROT_X | ROT_Y);
|
||||||
obj->SetBoneRotationFlags(7, ROT_X | ROT_Y);
|
obj->SetBoneRotationFlags(7, ROT_X | ROT_Y);
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_SENTRY_GUN];
|
obj = &Objects[ID_SENTRY_GUN];
|
||||||
|
@ -422,7 +422,7 @@ namespace TEN::Entities
|
||||||
obj->SetBoneRotationFlags(1, ROT_X | ROT_X);
|
obj->SetBoneRotationFlags(1, ROT_X | ROT_X);
|
||||||
obj->SetBoneRotationFlags(2, ROT_X | ROT_Z);
|
obj->SetBoneRotationFlags(2, ROT_X | ROT_Z);
|
||||||
obj->SetBoneRotationFlags(3, ROT_X | ROT_Z);
|
obj->SetBoneRotationFlags(3, ROT_X | ROT_Z);
|
||||||
obj->SetupHitEffect(true);
|
obj->SetHitEffect(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_HARPY];
|
obj = &Objects[ID_HARPY];
|
||||||
|
@ -437,7 +437,7 @@ namespace TEN::Entities
|
||||||
obj->radius = 409;
|
obj->radius = 409;
|
||||||
obj->intelligent = true;
|
obj->intelligent = true;
|
||||||
obj->LotType = LotType::Flyer;
|
obj->LotType = LotType::Flyer;
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_GUIDE];
|
obj = &Objects[ID_GUIDE];
|
||||||
|
@ -454,7 +454,7 @@ namespace TEN::Entities
|
||||||
obj->meshSwapSlot = ID_MESHSWAP2;
|
obj->meshSwapSlot = ID_MESHSWAP2;
|
||||||
obj->SetBoneRotationFlags(6, ROT_X | ROT_Y);
|
obj->SetBoneRotationFlags(6, ROT_X | ROT_Y);
|
||||||
obj->SetBoneRotationFlags(20, ROT_X | ROT_Y);
|
obj->SetBoneRotationFlags(20, ROT_X | ROT_Y);
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_CROCODILE];
|
obj = &Objects[ID_CROCODILE];
|
||||||
|
@ -474,7 +474,7 @@ namespace TEN::Entities
|
||||||
obj->SetBoneRotationFlags(7, ROT_Y);
|
obj->SetBoneRotationFlags(7, ROT_Y);
|
||||||
obj->SetBoneRotationFlags(9, ROT_Y);
|
obj->SetBoneRotationFlags(9, ROT_Y);
|
||||||
obj->SetBoneRotationFlags(10, ROT_Y);
|
obj->SetBoneRotationFlags(10, ROT_Y);
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_SPHINX];
|
obj = &Objects[ID_SPHINX];
|
||||||
|
@ -489,7 +489,7 @@ namespace TEN::Entities
|
||||||
obj->radius = 512;
|
obj->radius = 512;
|
||||||
obj->intelligent = true;
|
obj->intelligent = true;
|
||||||
obj->damageType = DamageMode::None;
|
obj->damageType = DamageMode::None;
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_HORSE];
|
obj = &Objects[ID_HORSE];
|
||||||
|
@ -498,7 +498,7 @@ namespace TEN::Entities
|
||||||
obj->Initialize = InitializeHorse;
|
obj->Initialize = InitializeHorse;
|
||||||
obj->collision = ObjectCollision;
|
obj->collision = ObjectCollision;
|
||||||
obj->control = nullptr;
|
obj->control = nullptr;
|
||||||
obj->SetupHitEffect(true);
|
obj->SetHitEffect(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_HORSEMAN];
|
obj = &Objects[ID_HORSEMAN];
|
||||||
|
@ -512,7 +512,7 @@ namespace TEN::Entities
|
||||||
obj->pivotLength = 500;
|
obj->pivotLength = 500;
|
||||||
obj->radius = 409;
|
obj->radius = 409;
|
||||||
obj->intelligent = true;
|
obj->intelligent = true;
|
||||||
obj->SetupHitEffect(true);
|
obj->SetHitEffect(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_BABOON_NORMAL];
|
obj = &Objects[ID_BABOON_NORMAL];
|
||||||
|
@ -526,7 +526,7 @@ namespace TEN::Entities
|
||||||
obj->pivotLength = 200;
|
obj->pivotLength = 200;
|
||||||
obj->radius = 256;
|
obj->radius = 256;
|
||||||
obj->intelligent = true;
|
obj->intelligent = true;
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_BABOON_INV];
|
obj = &Objects[ID_BABOON_INV];
|
||||||
|
@ -541,7 +541,7 @@ namespace TEN::Entities
|
||||||
obj->pivotLength = 200;
|
obj->pivotLength = 200;
|
||||||
obj->radius = 256;
|
obj->radius = 256;
|
||||||
obj->intelligent = true;
|
obj->intelligent = true;
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_BABOON_SILENT];
|
obj = &Objects[ID_BABOON_SILENT];
|
||||||
|
@ -556,7 +556,7 @@ namespace TEN::Entities
|
||||||
obj->pivotLength = 200;
|
obj->pivotLength = 200;
|
||||||
obj->radius = 256;
|
obj->radius = 256;
|
||||||
obj->intelligent = true;
|
obj->intelligent = true;
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_CROCODILE_GOD];
|
obj = &Objects[ID_CROCODILE_GOD];
|
||||||
|
@ -573,7 +573,7 @@ namespace TEN::Entities
|
||||||
obj->damageType = DamageMode::None;
|
obj->damageType = DamageMode::None;
|
||||||
obj->SetBoneRotationFlags(6, ROT_X | ROT_Y);
|
obj->SetBoneRotationFlags(6, ROT_X | ROT_Y);
|
||||||
obj->SetBoneRotationFlags(7, ROT_X | ROT_Y);
|
obj->SetBoneRotationFlags(7, ROT_X | ROT_Y);
|
||||||
obj->SetupHitEffect();
|
obj->SetHitEffect();
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_LOCUSTS_EMITTER];
|
obj = &Objects[ID_LOCUSTS_EMITTER];
|
||||||
|
@ -619,7 +619,7 @@ namespace TEN::Entities
|
||||||
obj->Initialize = InitializeInjuredSas;
|
obj->Initialize = InitializeInjuredSas;
|
||||||
obj->control = InjuredSasControl;
|
obj->control = InjuredSasControl;
|
||||||
obj->collision = ObjectCollision;
|
obj->collision = ObjectCollision;
|
||||||
obj->SetupHitEffect(false, true);
|
obj->SetHitEffect(false, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_ENEMY_JEEP];
|
obj = &Objects[ID_ENEMY_JEEP];
|
||||||
|
@ -639,7 +639,7 @@ namespace TEN::Entities
|
||||||
obj->SetBoneRotationFlags(9, ROT_X);
|
obj->SetBoneRotationFlags(9, ROT_X);
|
||||||
obj->SetBoneRotationFlags(11, ROT_X);
|
obj->SetBoneRotationFlags(11, ROT_X);
|
||||||
obj->SetBoneRotationFlags(12, ROT_X);
|
obj->SetBoneRotationFlags(12, ROT_X);
|
||||||
obj->SetupHitEffect(true);
|
obj->SetHitEffect(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_VON_CROY];
|
obj = &Objects[ID_VON_CROY];
|
||||||
|
@ -666,7 +666,7 @@ namespace TEN::Entities
|
||||||
{
|
{
|
||||||
obj->control = AnimatingControl;
|
obj->control = AnimatingControl;
|
||||||
obj->collision = SasDragBlokeCollision;
|
obj->collision = SasDragBlokeCollision;
|
||||||
obj->SetupHitEffect(false, true);
|
obj->SetHitEffect(false, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_SARCOPHAGUS];
|
obj = &Objects[ID_SARCOPHAGUS];
|
||||||
|
@ -674,7 +674,7 @@ namespace TEN::Entities
|
||||||
{
|
{
|
||||||
obj->control = AnimatingControl;
|
obj->control = AnimatingControl;
|
||||||
obj->collision = SarcophagusCollision;
|
obj->collision = SarcophagusCollision;
|
||||||
obj->SetupHitEffect(true);
|
obj->SetHitEffect(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_MAPPER];
|
obj = &Objects[ID_MAPPER];
|
||||||
|
@ -683,7 +683,7 @@ namespace TEN::Entities
|
||||||
obj->Initialize = InitializeMapper;
|
obj->Initialize = InitializeMapper;
|
||||||
obj->control = MapperControl;
|
obj->control = MapperControl;
|
||||||
obj->drawRoutine = nullptr;
|
obj->drawRoutine = nullptr;
|
||||||
obj->SetupHitEffect(true);
|
obj->SetHitEffect(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_ELEMENT_PUZZLE];
|
obj = &Objects[ID_ELEMENT_PUZZLE];
|
||||||
|
@ -692,7 +692,7 @@ namespace TEN::Entities
|
||||||
obj->Initialize = InitializeElementPuzzle;
|
obj->Initialize = InitializeElementPuzzle;
|
||||||
obj->control = ElementPuzzleControl;
|
obj->control = ElementPuzzleControl;
|
||||||
obj->collision = ElementPuzzleCollision;
|
obj->collision = ElementPuzzleCollision;
|
||||||
obj->SetupHitEffect(true);
|
obj->SetHitEffect(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_WHEEL_OF_FORTUNE];
|
obj = &Objects[ID_WHEEL_OF_FORTUNE];
|
||||||
|
@ -706,14 +706,14 @@ namespace TEN::Entities
|
||||||
obj->SetBoneRotationFlags(1, ROT_Z);
|
obj->SetBoneRotationFlags(1, ROT_Z);
|
||||||
obj->SetBoneRotationFlags(2, ROT_Z);
|
obj->SetBoneRotationFlags(2, ROT_Z);
|
||||||
obj->SetBoneRotationFlags(3, ROT_Z);
|
obj->SetBoneRotationFlags(3, ROT_Z);
|
||||||
obj->SetupHitEffect(true);
|
obj->SetHitEffect(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_ENEMY_PIECE];
|
obj = &Objects[ID_ENEMY_PIECE];
|
||||||
if (obj->loaded)
|
if (obj->loaded)
|
||||||
{
|
{
|
||||||
obj->collision = ObjectCollision;
|
obj->collision = ObjectCollision;
|
||||||
obj->SetupHitEffect(true);
|
obj->SetHitEffect(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_GOD_HEAD];
|
obj = &Objects[ID_GOD_HEAD];
|
||||||
|
@ -728,7 +728,7 @@ namespace TEN::Entities
|
||||||
obj->Initialize = InitializeGamePiece;
|
obj->Initialize = InitializeGamePiece;
|
||||||
obj->control = SenetControl;
|
obj->control = SenetControl;
|
||||||
obj->collision = ObjectCollision;
|
obj->collision = ObjectCollision;
|
||||||
obj->SetupHitEffect(true);
|
obj->SetHitEffect(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -742,7 +742,7 @@ namespace TEN::Entities
|
||||||
obj->Initialize = InitializeObelisk;
|
obj->Initialize = InitializeObelisk;
|
||||||
obj->control = ObeliskControl;
|
obj->control = ObeliskControl;
|
||||||
obj->collision = ObjectCollision;
|
obj->collision = ObjectCollision;
|
||||||
obj->SetupHitEffect(true);
|
obj->SetHitEffect(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_WRAITH_TRAP];
|
obj = &Objects[ID_WRAITH_TRAP];
|
||||||
|
@ -751,7 +751,7 @@ namespace TEN::Entities
|
||||||
obj->Initialize = InitializeWraithTrap;
|
obj->Initialize = InitializeWraithTrap;
|
||||||
obj->control = WraithTrapControl;
|
obj->control = WraithTrapControl;
|
||||||
obj->collision = ObjectCollision;
|
obj->collision = ObjectCollision;
|
||||||
obj->SetupHitEffect(true);
|
obj->SetHitEffect(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int objectID = ID_WATERFALL1; objectID <= ID_WATERFALL6; objectID++)
|
for (int objectID = ID_WATERFALL1; objectID <= ID_WATERFALL6; objectID++)
|
||||||
|
@ -769,7 +769,7 @@ namespace TEN::Entities
|
||||||
{
|
{
|
||||||
obj->control = ChainControl;
|
obj->control = ChainControl;
|
||||||
obj->collision = GenericSphereBoxCollision;
|
obj->collision = GenericSphereBoxCollision;
|
||||||
obj->SetupHitEffect(true);
|
obj->SetHitEffect(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_PLOUGH];
|
obj = &Objects[ID_PLOUGH];
|
||||||
|
@ -777,7 +777,7 @@ namespace TEN::Entities
|
||||||
{
|
{
|
||||||
obj->control = PloughControl;
|
obj->control = PloughControl;
|
||||||
obj->collision = GenericSphereBoxCollision;
|
obj->collision = GenericSphereBoxCollision;
|
||||||
obj->SetupHitEffect(true);
|
obj->SetHitEffect(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_CATWALK_BLADE];
|
obj = &Objects[ID_CATWALK_BLADE];
|
||||||
|
@ -785,7 +785,7 @@ namespace TEN::Entities
|
||||||
{
|
{
|
||||||
obj->control = CatwalkBladeControl;
|
obj->control = CatwalkBladeControl;
|
||||||
obj->collision = BladeCollision;
|
obj->collision = BladeCollision;
|
||||||
obj->SetupHitEffect(true);
|
obj->SetHitEffect(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_SETH_BLADE];
|
obj = &Objects[ID_SETH_BLADE];
|
||||||
|
@ -794,7 +794,7 @@ namespace TEN::Entities
|
||||||
obj->Initialize = InitializeSethBlade;
|
obj->Initialize = InitializeSethBlade;
|
||||||
obj->control = SethBladeControl;
|
obj->control = SethBladeControl;
|
||||||
obj->collision = GenericSphereBoxCollision;
|
obj->collision = GenericSphereBoxCollision;
|
||||||
obj->SetupHitEffect(true);
|
obj->SetHitEffect(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_PLINTH_BLADE];
|
obj = &Objects[ID_PLINTH_BLADE];
|
||||||
|
@ -802,7 +802,7 @@ namespace TEN::Entities
|
||||||
{
|
{
|
||||||
obj->control = PlinthBladeControl;
|
obj->control = PlinthBladeControl;
|
||||||
obj->collision = BladeCollision;
|
obj->collision = BladeCollision;
|
||||||
obj->SetupHitEffect(true);
|
obj->SetHitEffect(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_BIRD_BLADE];
|
obj = &Objects[ID_BIRD_BLADE];
|
||||||
|
@ -810,7 +810,7 @@ namespace TEN::Entities
|
||||||
{
|
{
|
||||||
obj->control = BirdBladeControl;
|
obj->control = BirdBladeControl;
|
||||||
obj->collision = GenericSphereBoxCollision;
|
obj->collision = GenericSphereBoxCollision;
|
||||||
obj->SetupHitEffect(true);
|
obj->SetHitEffect(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_JOBY_SPIKES];
|
obj = &Objects[ID_JOBY_SPIKES];
|
||||||
|
@ -819,7 +819,7 @@ namespace TEN::Entities
|
||||||
obj->Initialize = InitializeJobySpikes;
|
obj->Initialize = InitializeJobySpikes;
|
||||||
obj->control = JobySpikesControl;
|
obj->control = JobySpikesControl;
|
||||||
obj->collision = GenericSphereBoxCollision;
|
obj->collision = GenericSphereBoxCollision;
|
||||||
obj->SetupHitEffect(true);
|
obj->SetHitEffect(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_MOVING_BLADE];
|
obj = &Objects[ID_MOVING_BLADE];
|
||||||
|
@ -827,7 +827,7 @@ namespace TEN::Entities
|
||||||
{
|
{
|
||||||
obj->control = MovingBladeControl;
|
obj->control = MovingBladeControl;
|
||||||
obj->collision = BladeCollision;
|
obj->collision = BladeCollision;
|
||||||
obj->SetupHitEffect(true);
|
obj->SetHitEffect(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_SPIKEBALL];
|
obj = &Objects[ID_SPIKEBALL];
|
||||||
|
@ -835,7 +835,7 @@ namespace TEN::Entities
|
||||||
{
|
{
|
||||||
obj->control = SpikeballControl;
|
obj->control = SpikeballControl;
|
||||||
obj->collision = GenericSphereBoxCollision;
|
obj->collision = GenericSphereBoxCollision;
|
||||||
obj->SetupHitEffect(true);
|
obj->SetHitEffect(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_CHAIN];
|
obj = &Objects[ID_CHAIN];
|
||||||
|
@ -843,7 +843,7 @@ namespace TEN::Entities
|
||||||
{
|
{
|
||||||
obj->control = ChainControl;
|
obj->control = ChainControl;
|
||||||
obj->collision = GenericSphereBoxCollision;
|
obj->collision = GenericSphereBoxCollision;
|
||||||
obj->SetupHitEffect(true);
|
obj->SetHitEffect(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_PLOUGH];
|
obj = &Objects[ID_PLOUGH];
|
||||||
|
@ -851,7 +851,7 @@ namespace TEN::Entities
|
||||||
{
|
{
|
||||||
obj->control = PloughControl;
|
obj->control = PloughControl;
|
||||||
obj->collision = GenericSphereBoxCollision;
|
obj->collision = GenericSphereBoxCollision;
|
||||||
obj->SetupHitEffect(true);
|
obj->SetHitEffect(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_FLOOR_4BLADES];
|
obj = &Objects[ID_FLOOR_4BLADES];
|
||||||
|
@ -859,7 +859,7 @@ namespace TEN::Entities
|
||||||
{
|
{
|
||||||
obj->control = FourBladesControl;
|
obj->control = FourBladesControl;
|
||||||
obj->collision = GenericSphereBoxCollision;
|
obj->collision = GenericSphereBoxCollision;
|
||||||
obj->SetupHitEffect(true);
|
obj->SetHitEffect(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_CEILING_4BLADES];
|
obj = &Objects[ID_CEILING_4BLADES];
|
||||||
|
@ -867,7 +867,7 @@ namespace TEN::Entities
|
||||||
{
|
{
|
||||||
obj->control = FourBladesControl;
|
obj->control = FourBladesControl;
|
||||||
obj->collision = GenericSphereBoxCollision;
|
obj->collision = GenericSphereBoxCollision;
|
||||||
obj->SetupHitEffect(true);
|
obj->SetHitEffect(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_STARGATE];
|
obj = &Objects[ID_STARGATE];
|
||||||
|
@ -875,7 +875,7 @@ namespace TEN::Entities
|
||||||
{
|
{
|
||||||
obj->control = StargateControl;
|
obj->control = StargateControl;
|
||||||
obj->collision = StargateCollision;
|
obj->collision = StargateCollision;
|
||||||
obj->SetupHitEffect(true);
|
obj->SetHitEffect(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_SLICER_DICER];
|
obj = &Objects[ID_SLICER_DICER];
|
||||||
|
@ -884,7 +884,7 @@ namespace TEN::Entities
|
||||||
obj->Initialize = InitializeSlicerDicer;
|
obj->Initialize = InitializeSlicerDicer;
|
||||||
obj->control = SlicerDicerControl;
|
obj->control = SlicerDicerControl;
|
||||||
obj->collision = BladeCollision;
|
obj->collision = BladeCollision;
|
||||||
obj->SetupHitEffect(true);
|
obj->SetHitEffect(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_MINE];
|
obj = &Objects[ID_MINE];
|
||||||
|
@ -901,7 +901,7 @@ namespace TEN::Entities
|
||||||
obj->Initialize = InitializeSpikyWall;
|
obj->Initialize = InitializeSpikyWall;
|
||||||
obj->control = ControlSpikyWall;
|
obj->control = ControlSpikyWall;
|
||||||
obj->collision = CollideSpikyWall;
|
obj->collision = CollideSpikyWall;
|
||||||
obj->SetupHitEffect(true);
|
obj->SetHitEffect(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_SPIKY_CEILING];
|
obj = &Objects[ID_SPIKY_CEILING];
|
||||||
|
@ -910,7 +910,7 @@ namespace TEN::Entities
|
||||||
obj->Initialize = InitializeSpikyCeiling;
|
obj->Initialize = InitializeSpikyCeiling;
|
||||||
obj->control = ControlSpikyCeiling;
|
obj->control = ControlSpikyCeiling;
|
||||||
obj->collision = CollideSpikyCeiling;
|
obj->collision = CollideSpikyCeiling;
|
||||||
obj->SetupHitEffect(true);
|
obj->SetHitEffect(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_COG];
|
obj = &Objects[ID_COG];
|
||||||
|
@ -918,7 +918,7 @@ namespace TEN::Entities
|
||||||
{
|
{
|
||||||
obj->control = CogControl;
|
obj->control = CogControl;
|
||||||
obj->collision = CogCollision;
|
obj->collision = CogCollision;
|
||||||
obj->SetupHitEffect(true);
|
obj->SetHitEffect(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_LARA_DOUBLE];
|
obj = &Objects[ID_LARA_DOUBLE];
|
||||||
|
@ -932,7 +932,7 @@ namespace TEN::Entities
|
||||||
obj->pivotLength = 50;
|
obj->pivotLength = 50;
|
||||||
obj->radius = 128;
|
obj->radius = 128;
|
||||||
obj->intelligent = true;
|
obj->intelligent = true;
|
||||||
obj->SetupHitEffect(true);
|
obj->SetHitEffect(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_TEETH_SPIKES];
|
obj = &Objects[ID_TEETH_SPIKES];
|
||||||
|
@ -947,7 +947,7 @@ namespace TEN::Entities
|
||||||
{
|
{
|
||||||
obj->control = HammerControl;
|
obj->control = HammerControl;
|
||||||
obj->collision = GenericSphereBoxCollision;
|
obj->collision = GenericSphereBoxCollision;
|
||||||
obj->SetupHitEffect(true);
|
obj->SetHitEffect(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -959,7 +959,7 @@ namespace TEN::Entities
|
||||||
obj->Initialize = InitializeJeep;
|
obj->Initialize = InitializeJeep;
|
||||||
obj->collision = JeepPlayerCollision;
|
obj->collision = JeepPlayerCollision;
|
||||||
obj->shadowType = ShadowMode::Lara;
|
obj->shadowType = ShadowMode::Lara;
|
||||||
obj->SetupHitEffect(true);
|
obj->SetHitEffect(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_MOTORBIKE];
|
obj = &Objects[ID_MOTORBIKE];
|
||||||
|
@ -968,7 +968,7 @@ namespace TEN::Entities
|
||||||
obj->Initialize = InitializeMotorbike;
|
obj->Initialize = InitializeMotorbike;
|
||||||
obj->collision = MotorbikePlayerCollision;
|
obj->collision = MotorbikePlayerCollision;
|
||||||
obj->shadowType = ShadowMode::Lara;
|
obj->shadowType = ShadowMode::Lara;
|
||||||
obj->SetupHitEffect(true);
|
obj->SetHitEffect(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -347,7 +347,7 @@ namespace TEN::Entities::Creatures::TR5
|
||||||
{
|
{
|
||||||
if (!((pos.x ^ mesh->pos.Position.x) & 0xFFFFFC00))
|
if (!((pos.x ^ mesh->pos.Position.x) & 0xFFFFFC00))
|
||||||
{
|
{
|
||||||
if (StaticObjects[mesh->staticNumber].shatterType != SHT_NONE)
|
if (StaticObjects[mesh->staticNumber].shatterType != ShatterType::None)
|
||||||
{
|
{
|
||||||
ShatterObject(0, mesh, -64, LaraItem->RoomNumber, 0);
|
ShatterObject(0, mesh, -64, LaraItem->RoomNumber, 0);
|
||||||
//SoundEffect(ShatterSounds[gfCurrentLevel - 5][*(v28 + 18)], v28);
|
//SoundEffect(ShatterSounds[gfCurrentLevel - 5][*(v28 + 18)], v28);
|
||||||
|
|
|
@ -110,7 +110,7 @@ namespace TEN::Entities::Creatures::TR5
|
||||||
|
|
||||||
if (objOnLos < 0 && GetRandomControl() & 1)
|
if (objOnLos < 0 && GetRandomControl() & 1)
|
||||||
{
|
{
|
||||||
if (StaticObjects[hitMesh->staticNumber].shatterType != SHT_NONE)
|
if (StaticObjects[hitMesh->staticNumber].shatterType != ShatterType::None)
|
||||||
{
|
{
|
||||||
ShatterObject(0, hitMesh, 64, target.RoomNumber, 0);
|
ShatterObject(0, hitMesh, 64, target.RoomNumber, 0);
|
||||||
TestTriggers(hitMesh->pos.Position.x, hitMesh->pos.Position.y, hitMesh->pos.Position.z, target.RoomNumber, true);
|
TestTriggers(hitMesh->pos.Position.x, hitMesh->pos.Position.y, hitMesh->pos.Position.z, target.RoomNumber, true);
|
||||||
|
|
|
@ -553,7 +553,7 @@ namespace TEN::Entities::Creatures::TR5
|
||||||
|
|
||||||
if (!((mesh->pos.Position.z ^ pos.z) & 0xFFFFFC00) && !((mesh->pos.Position.x ^ pos.x) & 0xFFFFFC00))
|
if (!((mesh->pos.Position.z ^ pos.z) & 0xFFFFFC00) && !((mesh->pos.Position.x ^ pos.x) & 0xFFFFFC00))
|
||||||
{
|
{
|
||||||
if (StaticObjects[mesh->staticNumber].shatterType != SHT_NONE)
|
if (StaticObjects[mesh->staticNumber].shatterType != ShatterType::None)
|
||||||
{
|
{
|
||||||
ShatterObject(0, mesh, -64, LaraItem->RoomNumber, 0);
|
ShatterObject(0, mesh, -64, LaraItem->RoomNumber, 0);
|
||||||
SoundEffect(GetShatterSound(mesh->staticNumber), (Pose*)mesh);
|
SoundEffect(GetShatterSound(mesh->staticNumber), (Pose*)mesh);
|
||||||
|
|
|
@ -76,7 +76,7 @@ bool IsOnExpandingPlatform(int itemNumber, int x, int z)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return GetBridgeItemIntersect(itemNumber, x, item->Pose.Position.y, z, false).has_value();
|
return GetBridgeItemIntersect(Vector3i(x, item->Pose.Position.y, z), itemNumber, false).has_value();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsInFrontOfExpandingPlatform(int itemNumber, int x, int y, int z, int margin)
|
bool IsInFrontOfExpandingPlatform(int itemNumber, int x, int y, int z, int margin)
|
||||||
|
@ -129,7 +129,7 @@ bool IsInFrontOfExpandingPlatform(int itemNumber, int x, int y, int z, int margi
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return GetBridgeItemIntersect(itemNumber, x, item->Pose.Position.y, z, false).has_value();
|
return GetBridgeItemIntersect(Vector3i(x, item->Pose.Position.y, z), itemNumber, false).has_value();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShiftLaraOnPlatform(short itemNumber, bool isExpanding)
|
void ShiftLaraOnPlatform(short itemNumber, bool isExpanding)
|
||||||
|
@ -254,7 +254,7 @@ std::optional<int> ExpandingPlatformFloor(short itemNumber, int x, int y, int z)
|
||||||
if (!IsOnExpandingPlatform(itemNumber, x, z))
|
if (!IsOnExpandingPlatform(itemNumber, x, z))
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
|
||||||
return GetBridgeItemIntersect(itemNumber, x, y, z, false);
|
return GetBridgeItemIntersect(Vector3i(x, y, z), itemNumber, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<int> ExpandingPlatformCeiling(short itemNumber, int x, int y, int z)
|
std::optional<int> ExpandingPlatformCeiling(short itemNumber, int x, int y, int z)
|
||||||
|
@ -262,7 +262,7 @@ std::optional<int> ExpandingPlatformCeiling(short itemNumber, int x, int y, int
|
||||||
if (!IsOnExpandingPlatform(itemNumber, x, z))
|
if (!IsOnExpandingPlatform(itemNumber, x, z))
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
|
||||||
return GetBridgeItemIntersect(itemNumber, x, y, z, true);
|
return GetBridgeItemIntersect(Vector3i(x, y, z), itemNumber, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
int ExpandingPlatformFloorBorder(short itemNumber)
|
int ExpandingPlatformFloorBorder(short itemNumber)
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,38 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
class Vector3i;
|
|
||||||
struct CollisionInfo;
|
|
||||||
struct ItemInfo;
|
|
||||||
|
|
||||||
namespace TEN::Entities::Generic
|
|
||||||
{
|
|
||||||
struct PushableInfo;
|
|
||||||
|
|
||||||
PushableInfo* GetPushableInfo(ItemInfo* item);
|
|
||||||
void InitializePushableBlock(short itemNumber);
|
|
||||||
|
|
||||||
void ClearMovableBlockSplitters(const Vector3i& pos, short roomNumber);
|
|
||||||
void PushableBlockControl(short itemNumber);
|
|
||||||
void PushableBlockCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll);
|
|
||||||
bool TestBlockMovable(ItemInfo* item, int blockHeight);
|
|
||||||
bool TestBlockPush(ItemInfo* item, int blockHeight, int quadrant);
|
|
||||||
bool TestBlockPull(ItemInfo* item, int blockHeight, int quadrant);
|
|
||||||
|
|
||||||
// Pushable stack utilities
|
|
||||||
void MoveStackXZ(short itemNumber);
|
|
||||||
void MoveStackY(short itemNumber, int y);
|
|
||||||
void RemoveBridgeStack(short itemNumber);
|
|
||||||
void AddBridgeStack(short itemNumber);
|
|
||||||
void RemoveFromStack(short itemNumber);
|
|
||||||
int FindStack(short itemNumber);
|
|
||||||
int GetStackHeight(ItemInfo* item);
|
|
||||||
bool CheckStackLimit(ItemInfo* item);
|
|
||||||
|
|
||||||
void PushLoop(ItemInfo* item);
|
|
||||||
void PushEnd(ItemInfo* item);
|
|
||||||
|
|
||||||
std::optional<int> PushableBlockFloor(short itemNumber, int x, int y, int z);
|
|
||||||
std::optional<int> PushableBlockCeiling(short itemNumber, int x, int y, int z);
|
|
||||||
int PushableBlockFloorBorder(short itemNumber);
|
|
||||||
int PushableBlockCeilingBorder(short itemNumber);
|
|
||||||
}
|
|
|
@ -1,36 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
namespace TEN::Entities::Generic
|
|
||||||
{
|
|
||||||
enum class PushableMovementState
|
|
||||||
{
|
|
||||||
None,
|
|
||||||
Moving,
|
|
||||||
Stopping
|
|
||||||
};
|
|
||||||
|
|
||||||
struct PushableInfo
|
|
||||||
{
|
|
||||||
PushableMovementState MovementState = PushableMovementState::None;
|
|
||||||
|
|
||||||
int height; // height for collision, also in floor procedure
|
|
||||||
int weight;
|
|
||||||
int stackLimit;
|
|
||||||
int moveX; // used for pushable movement code
|
|
||||||
int moveZ; // used for pushable movement code
|
|
||||||
int linkedIndex; // using itemFlags[1] for now
|
|
||||||
int gravity; // fall acceleration
|
|
||||||
int loopSound; // looped sound index for movement
|
|
||||||
int stopSound; // ending sound index
|
|
||||||
int fallSound; // sound on hitting floor (if dropped)
|
|
||||||
int climb; // not used for now
|
|
||||||
bool canFall; // OCB 32
|
|
||||||
bool hasFloorCeiling; // has floor and ceiling procedures (OCB 64)
|
|
||||||
bool disablePull; // OCB 128
|
|
||||||
bool disablePush; // OCB 256
|
|
||||||
bool disableW; // OCB 512 (W+E)
|
|
||||||
bool disableE; // OCB 512 (W+E)
|
|
||||||
bool disableN; // OCB 1024 (N+S)
|
|
||||||
bool disableS; // OCB 1024 (N+S)
|
|
||||||
};
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue