|
1 | 1 | #include <JumpjetLocomotionClass.h> |
2 | 2 | #include <UnitClass.h> |
| 3 | +#include <BuildingClass.h> |
| 4 | + |
3 | 5 | #include <Utilities/Macro.h> |
4 | 6 | #include <Ext/Techno/Body.h> |
5 | | -#include <Ext/TechnoType/Body.h> |
6 | 7 | #include <Ext/WeaponType/Body.h> |
7 | | -#include <Ext/Techno/Body.h> |
8 | 8 |
|
9 | 9 | // Misc jumpjet facing, turning, drawing fix -- Author: Trsdy |
10 | 10 | // Jumpjets stuck at FireError::FACING because Jumpjet has its own facing just for JumpjetTurnRate |
@@ -222,3 +222,159 @@ void __stdcall JumpjetLocomotionClass_Unlimbo(ILocomotion* pThis) |
222 | 222 | } |
223 | 223 |
|
224 | 224 | DEFINE_FUNCTION_JUMP(VTABLE, 0x7ECDB8, JumpjetLocomotionClass_Unlimbo) |
| 225 | + |
| 226 | +// Let the jumpjet increase their height earlier or simply skip the stop check |
| 227 | +namespace JumpjetRushHelpers |
| 228 | +{ |
| 229 | + bool Skip = false; |
| 230 | + int GetJumpjetHeightWithOccupyTechno(const CellClass* pCell); // Replace sub_485080 |
| 231 | + int JumpjetLocomotionPredictHeight(JumpjetLocomotionClass* pThis); // Replace sub_54D820 |
| 232 | +} |
| 233 | + |
| 234 | +int JumpjetRushHelpers::GetJumpjetHeightWithOccupyTechno(const CellClass* pCell) |
| 235 | +{ |
| 236 | + if (const auto pBuilding = pCell->GetBuilding()) |
| 237 | + { |
| 238 | + auto dim2 = CoordStruct::Empty; |
| 239 | + pBuilding->Type->Dimension2(&dim2); |
| 240 | + return dim2.Z; |
| 241 | + } |
| 242 | + |
| 243 | + int height = 0; |
| 244 | + |
| 245 | + if (pCell->FindTechnoNearestTo(Point2D::Empty, false)) |
| 246 | + height += 85; // Vanilla |
| 247 | + |
| 248 | + if (pCell->ContainsBridge()) |
| 249 | + height += CellClass::BridgeHeight; |
| 250 | + |
| 251 | + return height; |
| 252 | +} |
| 253 | + |
| 254 | +int JumpjetRushHelpers::JumpjetLocomotionPredictHeight(JumpjetLocomotionClass* pThis) |
| 255 | +{ |
| 256 | + const auto pFoot = pThis->LinkedTo; |
| 257 | + const auto pLocation = &pFoot->Location; |
| 258 | + |
| 259 | + constexpr int shift = 8; // >> shift -> / Unsorted::LeptonsPerCell |
| 260 | + constexpr auto point2Cell = [](const Point2D& point) -> CellStruct |
| 261 | + { |
| 262 | + return CellStruct { static_cast<short>(point.X >> shift), static_cast<short>(point.Y >> shift) }; |
| 263 | + }; |
| 264 | + auto getJumpjetHeight = [](const CellClass* const pCell, const Point2D& point) -> int |
| 265 | + { |
| 266 | + return pCell->GetFloorHeight(Point2D { point.X, point.Y }) + JumpjetRushHelpers::GetJumpjetHeightWithOccupyTechno(pCell); |
| 267 | + }; |
| 268 | + |
| 269 | + // Initialize |
| 270 | + auto curCoord = Point2D { pLocation->X, pLocation->Y }; |
| 271 | + const CellClass* pCurCell = MapClass::Instance.GetCellAt(point2Cell(curCoord)); |
| 272 | + int maxHeight = getJumpjetHeight(pCurCell, curCoord); |
| 273 | + |
| 274 | + // If is moving |
| 275 | + if (pThis->CurrentSpeed > 0.0) |
| 276 | + { |
| 277 | + // Prepare for prediction |
| 278 | + auto lastCoord = Point2D::Empty; |
| 279 | + const int checkLength = (pThis->LocomotionFacing.IsRotating() || !pFoot->Destination) |
| 280 | + ? Unsorted::LeptonsPerCell |
| 281 | + : Math::min((Unsorted::LeptonsPerCell * 5), pFoot->DistanceFrom(pFoot->Destination)); // Predict the distance of 5 cells ahead |
| 282 | + const double angle = -pThis->LocomotionFacing.Current().GetRadian<65536>(); |
| 283 | + const auto checkCoord = Point2D { static_cast<int>(checkLength * Math::cos(angle) + 0.5), static_cast<int>(checkLength * Math::sin(angle) + 0.5) }; |
| 284 | + const int largeStep = Math::max(std::abs(checkCoord.X), std::abs(checkCoord.Y)); |
| 285 | + const int checkSteps = (largeStep > Unsorted::LeptonsPerCell) ? (largeStep / Unsorted::LeptonsPerCell + 1) : 1; |
| 286 | + const auto stepCoord = Point2D { (checkCoord.X / checkSteps), (checkCoord.Y / checkSteps) }; |
| 287 | + |
| 288 | + auto getSideHeight = [](const CellClass* const pCell) -> int |
| 289 | + { |
| 290 | + return (pCell->Level * Unsorted::LevelHeight) + JumpjetRushHelpers::GetJumpjetHeightWithOccupyTechno(pCell); |
| 291 | + }; |
| 292 | + auto getAntiAliasingCell = [&stepCoord, &checkCoord](const Point2D& curCoord, const Point2D& lastCoord) -> CellClass* |
| 293 | + { |
| 294 | + // Check if it is a diagonal relationship |
| 295 | + if ((curCoord.X >> shift) == (lastCoord.X >> shift) || (curCoord.Y >> shift) == (lastCoord.Y >> shift)) |
| 296 | + return nullptr; |
| 297 | + |
| 298 | + constexpr int mask = 0xFF; // & mask -> % Unsorted::LeptonsPerCell |
| 299 | + bool lastX = false; |
| 300 | + |
| 301 | + // Calculate the bias of the previous cell |
| 302 | + if (std::abs(stepCoord.X) > std::abs(stepCoord.Y)) |
| 303 | + { |
| 304 | + const int offsetX = curCoord.X & mask; |
| 305 | + const int deltaX = (stepCoord.X > 0) ? offsetX : (offsetX - Unsorted::LeptonsPerCell); |
| 306 | + const int projectedY = curCoord.Y - deltaX * checkCoord.Y / checkCoord.X; |
| 307 | + lastX = (projectedY ^ curCoord.Y) >> shift == 0; |
| 308 | + } |
| 309 | + else |
| 310 | + { |
| 311 | + const int offsetY = curCoord.Y & mask; |
| 312 | + const int deltaY = (stepCoord.Y > 0) ? offsetY : (offsetY - Unsorted::LeptonsPerCell); |
| 313 | + const int projectedX = curCoord.X - deltaY * checkCoord.X / checkCoord.Y; |
| 314 | + lastX = (projectedX ^ curCoord.X) >> shift != 0; |
| 315 | + } |
| 316 | + |
| 317 | + // Get cell |
| 318 | + return MapClass::Instance.TryGetCellAt(lastX |
| 319 | + ? CellStruct { static_cast<short>(lastCoord.X >> shift), static_cast<short>(curCoord.Y >> shift) } |
| 320 | + : CellStruct { static_cast<short>(curCoord.X >> shift), static_cast<short>(lastCoord.Y >> shift) }); |
| 321 | + }; |
| 322 | + auto checkStepHeight = [&maxHeight, &curCoord, &lastCoord, &pCurCell, &stepCoord, |
| 323 | + &getJumpjetHeight, &getAntiAliasingCell, &getSideHeight]() -> bool |
| 324 | + { |
| 325 | + // Check forward |
| 326 | + lastCoord = curCoord; |
| 327 | + curCoord += stepCoord; |
| 328 | + pCurCell = MapClass::Instance.TryGetCellAt(point2Cell(curCoord)); |
| 329 | + |
| 330 | + if (!pCurCell) |
| 331 | + return false; |
| 332 | + |
| 333 | + maxHeight = Math::max(maxHeight, getJumpjetHeight(pCurCell, curCoord)); |
| 334 | + |
| 335 | + // "Anti-Aliasing" |
| 336 | + if (const auto pCheckCell = getAntiAliasingCell(curCoord, lastCoord)) |
| 337 | + maxHeight = Math::max(maxHeight, getSideHeight(pCheckCell)); |
| 338 | + |
| 339 | + return true; |
| 340 | + }; |
| 341 | + |
| 342 | + // Predict height |
| 343 | + if (checkStepHeight()) |
| 344 | + { |
| 345 | + // The forward cell is not so high, keep moving |
| 346 | + if ((pLocation->Z - maxHeight) >= pFoot->GetTechnoType()->JumpjetHeight) |
| 347 | + JumpjetRushHelpers::Skip = true; |
| 348 | + |
| 349 | + // Check further |
| 350 | + for (int i = 1; i < checkSteps && checkStepHeight(); ++i); |
| 351 | + } |
| 352 | + } |
| 353 | + |
| 354 | + return maxHeight; |
| 355 | +} |
| 356 | + |
| 357 | +DEFINE_HOOK(0x54D827, JumpjetLocomotionClass_sub_54D820_PredictHeight, 0x8) |
| 358 | +{ |
| 359 | + enum { SkipVanillaCalculate = 0x54D928 }; |
| 360 | + |
| 361 | + GET(JumpjetLocomotionClass*, pThis, ESI); |
| 362 | + |
| 363 | + if (!RulesExt::Global()->JumpjetClimbPredictHeight) |
| 364 | + return 0; |
| 365 | + |
| 366 | + R->EAX(JumpjetRushHelpers::JumpjetLocomotionPredictHeight(pThis)); |
| 367 | + return SkipVanillaCalculate; |
| 368 | +} |
| 369 | + |
| 370 | +DEFINE_HOOK(0x54D4C0, JumpjetLocomotionClass_sub_54D0F0_NoStuck, 0x6) |
| 371 | +{ |
| 372 | + enum { SkipCheckStop = 0x54D52F }; |
| 373 | + |
| 374 | + if (JumpjetRushHelpers::Skip) |
| 375 | + JumpjetRushHelpers::Skip = false; |
| 376 | + else if (!RulesExt::Global()->JumpjetClimbWithoutCutOut) |
| 377 | + return 0; |
| 378 | + |
| 379 | + return SkipCheckStop; |
| 380 | +} |
0 commit comments