diff --git a/CREDITS.md b/CREDITS.md index c19a52a688..1f4054da67 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -290,6 +290,7 @@ This page lists all the individual contributions to the project by their author. - TechnoType conversion placeholder - EIP 00529A14 crash fix on Linux - Teleport timer reset after load game fix + - Teleport and Tunnel loco visual tilt fix - Skip units' turret rotation and jumpjets' wobbling under EMP - Misc code refactor & maintenance, CN doc fixes, bugfixes - **FlyStar** diff --git a/YRpp b/YRpp index 68adf8b140..205c36c561 160000 --- a/YRpp +++ b/YRpp @@ -1 +1 @@ -Subproject commit 68adf8b1400f9036e7a1b2bb3c02af4d2b5e36d0 +Subproject commit 205c36c56194c2094d0d4914b2b1cbfb52d5bc5a diff --git a/docs/Fixed-or-Improved-Logics.md b/docs/Fixed-or-Improved-Logics.md index ec015331a7..7738d13fc4 100644 --- a/docs/Fixed-or-Improved-Logics.md +++ b/docs/Fixed-or-Improved-Logics.md @@ -145,6 +145,8 @@ This page describes all ingame logics that are fixed or improved in Phobos witho - Fixed `AmbientDamage` when used with `IsRailgun=yes` being cut off by elevation changes. - Fixed railgun and fire particles being cut off by elevation changes. - Fixed teleport units' (for example CLEG) frozen-still timer being cleared after load game. +- Fixed teleport units being unable to visually tilt on slopes. +- Fixed units with Teleport or Tunnel locomotor being unable to be visually flipped like other locomotors do. - Aircraft docking on buildings now respect `[AudioVisual]`->`PoseDir` as the default setting and do not always land facing north or in case of pre-placed buildings, the building's direction. - Spawned aircraft now align with the spawner's facing when landing. diff --git a/docs/Whats-New.md b/docs/Whats-New.md index ec2ca4b4e4..a6e9c4fa86 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -411,9 +411,11 @@ Vanilla fixes: - Fixed `AmbientDamage` when used with `IsRailgun=yes` being cut off by elevation changes (by Starkku) - Fixed railgun and fire particles being cut off by elevation changes (by Starkku) - Fixed teleport units' frozen-still timer being reset after load game (by Trsdy) +- Fixed teleport units being unable to visually tilt on slopes (by Trsdy) +- Fixed teleport and drill units being unable to be visually flipped (by Trsdy) - Aircraft docking on buildings now respect `[AudioVisual]`->`PoseDir` as the default setting and do not always land facing north or in case of pre-placed buildings, the building's direction (by Starkku) - Spawned aircraft now align with the spawner's facing when landing (by Starkku) -- + Phobos fixes: - Fixed a few errors of calling for superweapon launch by `LaunchSW` or building infiltration (by Trsdy) - Add `ImmuneToCrit` for shields (by Trsdy) diff --git a/src/Ext/TechnoType/Hooks.cpp b/src/Ext/TechnoType/Hooks.cpp index 7ff4262782..21f5c84f0b 100644 --- a/src/Ext/TechnoType/Hooks.cpp +++ b/src/Ext/TechnoType/Hooks.cpp @@ -15,6 +15,7 @@ #include #include +#include DEFINE_HOOK(0x6F64A9, TechnoClass_DrawHealthBar_Hide, 0x5) { @@ -26,7 +27,7 @@ DEFINE_HOOK(0x6F64A9, TechnoClass_DrawHealthBar_Hide, 0x5) return 0; } -DEFINE_HOOK(0x6F3C56, TechnoClass_Transform_6F3AD0_TurretMultiOffset, 0x0) +DEFINE_HOOK(0x6F3C56, TechnoClass_GetFLH_TurretMultiOffset, 0x0) { LEA_STACK(Matrix3D*, mtx, STACK_OFFSET(0xD8, -0x90)); GET(TechnoTypeClass*, technoType, EDX); @@ -58,14 +59,14 @@ DEFINE_HOOK(0x73B780, UnitClass_DrawVXL_TurretMultiOffset, 0x0) return 0x73B790; } +constexpr reference const Pixel_Per_Lepton {}; + DEFINE_HOOK(0x73BA4C, UnitClass_DrawVXL_TurretMultiOffset1, 0x0) { LEA_STACK(Matrix3D*, mtx, STACK_OFFSET(0x1D0, -0x13C)); GET(TechnoTypeClass*, technoType, EBX); - double& factor = *reinterpret_cast(0xB1D008); - - TechnoTypeExt::ApplyTurretOffset(technoType, mtx, factor); + TechnoTypeExt::ApplyTurretOffset(technoType, mtx, Pixel_Per_Lepton); return 0x73BA68; } @@ -105,7 +106,7 @@ DEFINE_HOOK(0x73CCE1, UnitClass_DrawSHP_TurretOffest, 0x6) float angle = static_cast(turretRad - bodyRad); mtx.RotateZ(angle); - auto res = Matrix3D::MatrixMultiply(mtx, Vector3D::Empty); + auto res = mtx.GetTranslation(); auto location = CoordStruct { static_cast(res.X), static_cast(-res.Y), static_cast(res.Z) }; pos += TacticalClass::CoordsToScreen(location); @@ -338,49 +339,88 @@ DEFINE_HOOK(0x4DB157, FootClass_DrawVoxelShadow_TurretShadow, 0x8) GET(FootClass*, pThis, ESI); GET_STACK(Point2D, pos, STACK_OFFSET(0x18, 0x28)); GET_STACK(Surface*, pSurface, STACK_OFFSET(0x18, 0x24)); - GET_STACK(bool, a9, STACK_OFFSET(0x18, 0x20)); // unknown usage + GET_STACK(bool, a9, STACK_OFFSET(0x18, 0x20)); GET_STACK(Matrix3D*, pMatrix, STACK_OFFSET(0x18, 0x1C)); - GET_STACK(Point2D*, a4, STACK_OFFSET(0x18, 0x14)); // unknown usage - GET_STACK(Point2D, a3, STACK_OFFSET(0x18, -0x10)); // unknown usage - GET_STACK(int*, a5, STACK_OFFSET(0x18, 0x10)); // unknown usage - GET_STACK(int, angle, STACK_OFFSET(0x18, 0xC)); - GET_STACK(int, idx, STACK_OFFSET(0x18, 0x8)); - GET_STACK(VoxelStruct*, pVXL, STACK_OFFSET(0x18, 0x4)); + GET_STACK(RectangleStruct*, bound, STACK_OFFSET(0x18, 0x14)); + GET_STACK(Point2D, a3, STACK_OFFSET(0x18, -0x10)); + GET_STACK(decltype(ObjectTypeClass::VoxelShadowCache)*, shadow_cache, STACK_OFFSET(0x18, 0x10)); + GET_STACK(VoxelIndexKey, index_key, STACK_OFFSET(0x18, 0xC)); + GET_STACK(int, shadow_index, STACK_OFFSET(0x18, 0x8)); + GET_STACK(VoxelStruct*, main_vxl, STACK_OFFSET(0x18, 0x4)); auto pType = pThis->GetTechnoType(); auto const pTypeExt = TechnoTypeExt::ExtMap.Find(pType); - auto tur = pType->Gunner || pType->IsChargeTurret - ? &pType->ChargerTurrets[pThis->CurrentTurretNumber] - : &pType->TurretVoxel; - if (pTypeExt->TurretShadow.Get(RulesExt::Global()->DrawTurretShadow) && tur->VXL && tur->HVA) + + // We need to handle Ares turrets/barrels + struct DummyExtHere { - auto mtx = pThis->Locomotor->Shadow_Matrix(0); - pTypeExt->ApplyTurretOffset(&mtx, *reinterpret_cast(0xB1D008)); + char before[0xA4]; + std::vector ChargerTurrets; + std::vector ChargerBarrels; + }; + + auto GetTurretVoxel = [pType](int idx) ->VoxelStruct* + { + if (pType->TurretCount == 0 || pType->IsGattling || idx < 0) + return &pType->TurretVoxel; + + if (idx < 18) + return &pType->ChargerTurrets[idx]; + + if (CAN_USE_ARES && AresHelper::CanUseAres) + { + auto* aresTypeExt = reinterpret_cast(pType->align_2FC); + return &aresTypeExt->ChargerTurrets[idx - 18]; + } + + return nullptr; + }; + + auto GetBarrelVoxel = [pType](int idx)->VoxelStruct* + { + if (pType->TurretCount == 0 || pType->IsGattling || idx < 0) + return &pType->BarrelVoxel; + + if (idx < 18) + return &pType->ChargerBarrels[idx]; + + if (CAN_USE_ARES && AresHelper::CanUseAres) + { + auto* aresTypeExt = reinterpret_cast(pType->align_2FC); + return &aresTypeExt->ChargerBarrels[idx - 18]; + } + + return nullptr; + }; + + auto tur = GetTurretVoxel(pThis->CurrentTurretNumber); + + if (tur && pTypeExt->TurretShadow.Get(RulesExt::Global()->DrawTurretShadow) && tur->VXL && tur->HVA) + { + auto mtx = Matrix3D::GetIdentity(); + pTypeExt->ApplyTurretOffset(&mtx, Pixel_Per_Lepton); mtx.TranslateZ(-tur->HVA->Matrixes[0].GetZVal()); mtx.RotateZ(static_cast(pThis->SecondaryFacing.Current().GetRadian<32>() - pThis->PrimaryFacing.Current().GetRadian<32>())); - Matrix3D::MatrixMultiply(&mtx, &Matrix3D::VoxelDefaultMatrix, &mtx); + mtx = *pMatrix * mtx; + + pThis->DrawVoxelShadow(tur, 0, index_key, nullptr, bound, &a3, &mtx, a9, pSurface, pos); - pThis->DrawVoxelShadow(tur, 0, angle, 0, a4, &a3, &mtx, a9, pSurface, pos); - auto bar = &pType->BarrelVoxel; + auto bar = GetBarrelVoxel(pThis->CurrentTurretNumber); - if (bar->VXL && bar->HVA) - pThis->DrawVoxelShadow(bar, 0, angle, 0, a4, &a3, &mtx, a9, pSurface, pos); + if (bar && bar->VXL && bar->HVA) + pThis->DrawVoxelShadow(bar, 0, index_key, nullptr, bound, &a3, &mtx, a9, pSurface, pos); } if (!pTypeExt->ShadowIndices.size()) { - pThis->DrawVoxelShadow(pVXL, idx, angle, a5, a4, &a3, pMatrix, a9, pSurface, pos); + pThis->DrawVoxelShadow(main_vxl, shadow_index, index_key, shadow_cache, bound, &a3, pMatrix, a9, pSurface, pos); } else { for (auto index : pTypeExt->ShadowIndices) { - auto hva = pVXL->HVA; - Matrix3D idxmtx = *pMatrix; - idxmtx.TranslateZ(-hva->Matrixes[index].GetZVal()); - Matrix3D::MatrixMultiply(&idxmtx, &Matrix3D::VoxelDefaultMatrix, &idxmtx); - pThis->DrawVoxelShadow(pVXL, index, angle, a5, a4, &a3, pMatrix, a9, pSurface, pos); + pThis->DrawVoxelShadow(main_vxl, index, index_key, shadow_cache, bound, &a3, pMatrix, a9, pSurface, pos); } } diff --git a/src/Ext/Unit/Hooks.Jumpjet.cpp b/src/Ext/Unit/Hooks.Jumpjet.cpp index 5cba39018a..7ccca680a0 100644 --- a/src/Ext/Unit/Hooks.Jumpjet.cpp +++ b/src/Ext/Unit/Hooks.Jumpjet.cpp @@ -208,3 +208,58 @@ void __stdcall JumpjetLocomotionClass_Unlimbo(ILocomotion* pThis) } DEFINE_JUMP(VTABLE, 0x7ECDB8, GET_OFFSET(JumpjetLocomotionClass_Unlimbo)) + + +// Visual bugfix : Teleport loco vxls could not tilt +Matrix3D* __stdcall TeleportLocomotionClass_Draw_Matrix(ILocomotion* iloco, Matrix3D* ret, VoxelIndexKey* pIndex) +{ + __assume(iloco != nullptr); + auto const pThis = static_cast(iloco); + auto linked = pThis->LinkedTo; + auto slope_idx = MapClass::Instance->GetCellAt(linked->Location)->SlopeIndex; + + if (pIndex && pIndex->Is_Valid_Key()) + *(int*)(pIndex) = slope_idx + (*(int*)(pIndex) << 6); + + if (slope_idx && pIndex && pIndex->Is_Valid_Key()) + *ret = Matrix3D::VoxelRampMatrix[slope_idx] * pThis->LocomotionClass::Draw_Matrix(pIndex); + else + *ret = pThis->LocomotionClass::Draw_Matrix(pIndex); + + float arf = linked->AngleRotatedForwards; + float ars = linked->AngleRotatedSideways; + + if (std::abs(ars) >= 0.005 || std::abs(arf) >= 0.005) + { + if (pIndex)// just forget it bro, no one cares + *(int*)pIndex = -1; + + double scalex = linked->GetTechnoType()->VoxelScaleX; + double scaley = linked->GetTechnoType()->VoxelScaleY; + + Matrix3D pre = Matrix3D::GetIdentity(); + pre.TranslateZ(float(std::abs(Math::sin(ars)) * scalex + std::abs(Math::sin(arf)) * scaley)); + ret->TranslateX(float(Math::sgn(arf) * (scaley * (1 - Math::cos(arf))))); + ret->TranslateY(float(Math::sgn(-ars) * (scalex * (1 - Math::cos(ars))))); + ret->RotateX(ars); + ret->RotateY(arf); + + *ret = pre * *ret; + } + return ret; +} + +DEFINE_JUMP(VTABLE, 0x7F5024, GET_OFFSET(TeleportLocomotionClass_Draw_Matrix)); +DEFINE_JUMP(VTABLE, 0x7F5028, 0x5142A0);//TeleportLocomotionClass_Shadow_Matrix : just use hover's to save my time + +// Visual bugfix: Tunnel loco could not tilt when being flipped +DEFINE_HOOK(0x729B5D, TunnelLocomotionClass_DrawMatrix_Tilt, 0x8) +{ + GET(ILocomotion*, iloco, ESI); + GET_BASE(VoxelIndexKey*, pIndex, 0x10); + GET_BASE(Matrix3D*, ret, 0xC); + R->EAX(TeleportLocomotionClass_Draw_Matrix(iloco, ret, pIndex)); + return 0x729C09; +} + +DEFINE_JUMP(VTABLE, 0x7F5A4C, 0x5142A0);//TunnelLocomotionClass_Shadow_Matrix : just use hover's to save my time diff --git a/src/Misc/Hooks.BugFixes.cpp b/src/Misc/Hooks.BugFixes.cpp index 0a4e4e9af1..c0c42e0600 100644 --- a/src/Misc/Hooks.BugFixes.cpp +++ b/src/Misc/Hooks.BugFixes.cpp @@ -794,4 +794,8 @@ DEFINE_HOOK(0x689EB0, ScenarioClass_ReadMap_SkipHeaderInCampaign, 0x6) return SessionClass::IsCampaign() ? 0x689FC0 : 0; } -DEFINE_JUMP(LJMP, 0x719CBC, 0x719CD8);//Skip incorrect load ctor call in TeleportLocomotionClass_Load +//Skip incorrect load ctor call in various LocomotionClass_Load +DEFINE_JUMP(LJMP, 0x719CBC, 0x719CD8);//Teleport, notorious CLEG frozen state removal on loading game +DEFINE_JUMP(LJMP, 0x72A16A, 0x72A186);//Tunnel, not a big deal +DEFINE_JUMP(LJMP, 0x663428, 0x663445);//Rocket, not a big deal +DEFINE_JUMP(LJMP, 0x5170CE, 0x5170E0);//Hover, not a big deal