Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Burst projectile retargeting #1073

Open
wants to merge 27 commits into
base: develop
Choose a base branch
from

Conversation

FS-21
Copy link
Contributor

@FS-21 FS-21 commented Jun 1, 2023

  • The firer will pick targets randomly.
  • Works with missiles (no splits, airbusts, etc), cannons, lasers & spawners.
  • A valid techno is required for trigger the logic.
  • OmniFire=yes will make selectable any targets around the firer, limited by the weapon range.
  • OmniFire=no will force the firer to pick targets in an area composed by the firer's weapon range around the original target intersected with the firer's weapon range around the firer.
  • RandomTarget.Spawners.MultipleTargets=true gives each spawner it's own target.
  • This logic should be used only in one weapon of the object.

[SOMEWEAPON] ; WeaponType
RandomTarget=0.0 ; double or percentage
RandomTarget.Spawners.MultipleTargets=false ; boolean

Summary by CodeRabbit

  • New Features

    • Introduced Burst.Retarget allowing projectiles to retarget after each burst.
    • Added new projectile behavior for randomly selecting targets within range.
    • Customizable trajectory detonation and spawn distance adjustments for airstrike & spy plane.
    • Enabled application of Verses and PercentAtMax for negative damage.
  • Enhancements

    • Improved targeting logic with the ability to refresh and update random targets for units.
    • Enhanced AI logic to handle new targeting and mission behaviors.
  • Documentation

    • Updated New-or-Enhanced-Logics.md with examples and conditions for new projectile features.
    • Added summary of new features in Whats-New.md.
  • Refactor

    • Centralized unit availability checks to TechnoExt class.
    • Removed redundant IsUnitAvailable function from ScriptExt class.
  • Bug Fixes

    • Fixed issues with random target selection and assignment for various unit types.

@FS-21
Copy link
Contributor Author

FS-21 commented Jun 1, 2023

Regarding how affects the Omnifire tag.
imagen

@mevitar
Copy link

mevitar commented Jun 1, 2023

Appears to work fine (even tried it for Trajectory=Straight projectiles), except for a case where a jumpjet without a turret and with OmniFire=no retargeted its burst fire into a landed AircraftType that was on a straight line behind. Same thing happened multuple times.

The jumpjet also had a secondary weapon that it switches to after running out of ammo, and that one has OmniFire=yes but it doesn't retarget.

@FS-21
Copy link
Contributor Author

FS-21 commented Jun 2, 2023

Appears to work fine (even tried it for Trajectory=Straight projectiles), except for a case where a jumpjet without a turret and with OmniFire=no retargeted its burst fire into a landed AircraftType that was on a straight line behind. Same thing happened multuple times.

The jumpjet also had a secondary weapon that it switches to after running out of ammo, and that one has OmniFire=yes but it doesn't retarget.

What kind of weapons were used in the jumpjet case? I specified they should be of the type missile (but I noticed that invisible bullets works too).

@mevitar
Copy link

mevitar commented Jun 2, 2023

I know you said that it was designed for regular missiles, but it also appears to works fine for Trajectory=Straight (also tried with Arcing=yes, and it doesn't work there at all).
I've encountered this problem by accident while testing Trajectory=Straight, but apparently it's not the projectile, because there's an issue with some jumpjets that they might attack an incorrect target, regardless of what weapon it has. Probably a combination of firing angle + the jumpjet trying to reposition itself + face the target is what caused such behavior, but i'm unable to reproduce it anymore.

@Starkku Starkku force-pushed the develop branch 2 times, most recently from aae92fd to a32076e Compare June 3, 2023 10:14
Copy link
Contributor

@MortonPL MortonPL left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few minor things and one question.

src/Ext/Techno/Hooks.Firing.cpp Outdated Show resolved Hide resolved
src/Ext/Techno/Hooks.Firing.cpp Outdated Show resolved Hide resolved
src/Ext/Techno/Hooks.Firing.cpp Outdated Show resolved Hide resolved
@MortonPL MortonPL added the Feature / Enhancement Anything new not present in vanilla/Ares label Jun 6, 2023
Now isn't required to have Burst > 1 in the weapon.
@FS-21
Copy link
Contributor Author

FS-21 commented Sep 4, 2023

I updated with the rewrite of the feature.
Now isn't required a Burst > 1 to work and it supports lasers and spawners (I don't know if there are more supported cases).

Now the tag is called "RandomTarget" (docs updated and the first post has been updated)
With the new tag RandomTarget.Spawners.MultipleTargets=true the firer gives each spawner it's own random target.

@FS-21 FS-21 requested a review from MortonPL September 4, 2023 14:35
@github-actions
Copy link

github-actions bot commented Sep 22, 2023

Nightly build for this pull request:

This comment is automatic and is meant to allow guests to get latest nightly builds for this pull request without registering. It is updated on every successful build.

- Buidings retained target.
- Units retained targets when they were ordered to move.
- Small turret fix for non-laser weapons.
@mevitar
Copy link

mevitar commented Nov 9, 2023

I found a potential issue with spawners on the Sept 22nd build, but can't test it if it still happens later because the newer build didn't build properly.

Copy link

coderabbitai bot commented Nov 9, 2023

Walkthrough

The recent updates introduce a new targeting feature for projectiles, allowing them to randomly select targets within range. This includes the ability to retarget each projectile burst and enhancements to AI logic for target selection. The changes span across various classes, focusing on extending functionality for Techno, WeaponType, and Script classes, with a particular emphasis on random target handling and unit availability checks.

Changes

File(s) Summary
CREDITS.md, docs/New-or-Enhanced-Logics.md, docs/Whats-New.md Added documentation for the new Burst.Retarget feature and other projectile targeting enhancements.
YRpp Updated subproject commit reference.
src/Ext/Techno/... Added functions and member variables for handling random target selection and updating in Techno classes.
src/Ext/Techno/Hooks.cpp, src/Ext/Techno/Hooks.Firing.cpp Introduced new hooks and updated existing ones for random target management in various unit classes.
src/Ext/WeaponType/... Added new fields to WeaponTypeExt class for random target functionality.
src/Ext/Script/... Refactored unit availability checks from ScriptExt to TechnoExt class across multiple script-related files.

Poem

In the code where logic weaves,
A rabbit hops with autumn leaves. 🍂
Targets shift with random grace,
In the AI's endless chase. 🎯


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

Share
Tips

Chat

There are 3 ways to chat with CodeRabbit:

Note: Auto-reply has been disabled for this repository by the repository owner. The CodeRabbit bot will not respond to your replies unless it is explicitly tagged.

  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai generate interesting stats about this repository and render them as a table.
    • @coderabbitai show all the console.log statements in this repository.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (invoked as PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger a review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai help to get help.

Additionally, you can add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.

CodeRabbit Configration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Status

Actionable comments generated: 7

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between a5b0edd and ea7d0e3.
Files selected for processing (11)
  • CREDITS.md (1 hunks)
  • YRpp (1 hunks)
  • docs/New-or-Enhanced-Logics.md (1 hunks)
  • docs/Whats-New.md (1 hunks)
  • src/Ext/Techno/Body.Update.cpp (2 hunks)
  • src/Ext/Techno/Body.cpp (3 hunks)
  • src/Ext/Techno/Body.h (5 hunks)
  • src/Ext/Techno/Hooks.Firing.cpp (2 hunks)
  • src/Ext/Techno/Hooks.cpp (2 hunks)
  • src/Ext/WeaponType/Body.cpp (2 hunks)
  • src/Ext/WeaponType/Body.h (2 hunks)
Files skipped from review due to trivial changes (1)
  • src/Ext/WeaponType/Body.cpp
Additional comments: 23
CREDITS.md (1)
  • 128-131: The contribution of Burst.Retarget for projectile retargeting is correctly credited.
YRpp (1)
  • 1-1: The hunk provided does not contain any code to review. Please provide the relevant code hunks for review.
docs/Whats-New.md (1)
  • 351-351: The changes to the documentation are clear and concise, providing a good summary of the new feature.
src/Ext/WeaponType/Body.h (2)
  • 39-43: The new properties RandomTarget and RandomTarget_Spawners_MultipleTargets are added to the ExtData class. Ensure that these properties are used correctly in the rest of the codebase.

  • 65-66: The new properties are initialized in the constructor of ExtData. The initialization seems correct.

src/Ext/Techno/Hooks.cpp (2)
  • 5-9: New include statements for "Ext/BuildingType/Body.h" and "Ext/Script/Body.h" have been added. Ensure that these files exist and are accessible from this location.

  • 27-33: The RefreshRandomTargets function is called within the TechnoClass_AI function. This suggests that the AI logic has been updated to handle the refreshing of random targets. Ensure that this function is implemented correctly and that it doesn't introduce any performance issues.

src/Ext/Techno/Body.h (6)
  • 9-9: The inclusion of Utilities/EnumFunctions.h is new. Ensure that this header file is available and that it contains the necessary functions or definitions used in this file.

  • 42-43: New member variables OriginalTarget and CurrentRandomTarget are added to the ExtData struct. Ensure that these variables are used appropriately in the code and that their values are properly managed.

  • 67-68: The constructor of ExtData is updated to initialize the new member variables OriginalTarget and CurrentRandomTarget to nullptr. This is a good practice to avoid undefined behavior.

  • 82-82: The new member function RefreshRandomTargets is added. Ensure that this function is implemented correctly and that it is called at appropriate places in the code.

  • 89-90: The InvalidatePointer function is updated to handle the new member variables OriginalTarget and CurrentRandomTarget. This is a good practice to ensure that these pointers are invalidated when the objects they point to are removed.

  • 154-155: The new static member functions UpdateRandomTarget and GetRandomTarget are added. Ensure that these functions are implemented correctly and that they are called at appropriate places in the code.

src/Ext/Techno/Hooks.Firing.cpp (9)
  • 876-882: Ensure that the UpdateRandomTarget function handles null pointers and other edge cases correctly.

  • 885-891: Same as above, ensure that the UpdateRandomTarget function handles null pointers and other edge cases correctly.

  • 894-912: This hook seems to be changing the target of the TechnoClass object. Ensure that this change is intended and that it doesn't interfere with other parts of the code that might be using the Target property.

  • 914-941: This hook seems to be iterating over SpawnedNodes and updating the CurrentRandomTarget for each node. Ensure that this operation is thread-safe if SpawnedNodes can be accessed or modified from multiple threads.

  • 943-962: This hook seems to be updating the CurrentRandomTarget for a AircraftClass object. Ensure that this operation is thread-safe if CurrentRandomTarget can be accessed or modified from multiple threads.

  • 964-983: Same as above, ensure that the operation is thread-safe if CurrentRandomTarget can be accessed or modified from multiple threads.

  • 985-1008: This hook seems to be iterating over CurrentObjects and updating the CurrentRandomTarget and OriginalTarget for each object. Ensure that this operation is thread-safe if CurrentObjects, CurrentRandomTarget, or OriginalTarget can be accessed or modified from multiple threads.

  • 1010-1032: This hook seems to be updating the CurrentRandomTarget and OriginalTarget for a FootClass object. Ensure that this operation is thread-safe if CurrentRandomTarget or OriginalTarget can be accessed or modified from multiple threads.

  • 1034-1052: This hook seems to be updating the Target for a BulletClass object. Ensure that this operation is thread-safe if Target can be accessed or modified from multiple threads.

src/Ext/Techno/Body.cpp (1)
  • 868-873: The Serialize function has been updated to include the OriginalTarget and CurrentRandomTarget variables. Ensure that the deserialization function is also updated to include these variables to maintain consistency.

docs/New-or-Enhanced-Logics.md Show resolved Hide resolved
src/Ext/Techno/Body.Update.cpp Show resolved Hide resolved
src/Ext/Techno/Body.Update.cpp Outdated Show resolved Hide resolved
src/Ext/Techno/Body.Update.cpp Outdated Show resolved Hide resolved
src/Ext/Techno/Body.Update.cpp Outdated Show resolved Hide resolved
src/Ext/Techno/Body.cpp Outdated Show resolved Hide resolved
Comment on lines 759 to 834
TechnoClass* TechnoExt::GetRandomTarget(TechnoClass* pThis)
{
TechnoClass* selection = nullptr;

if (!pThis && !pThis->Target)
return selection;

int weaponIndex = pThis->SelectWeapon(pThis->Target);
auto pWeapon = pThis->GetWeapon(weaponIndex)->WeaponType;
if (!pWeapon)
return selection;

const auto pWeaponExt = WeaponTypeExt::ExtMap.Find(pWeapon);
if (!pWeaponExt || pWeaponExt->RandomTarget <= 0.0)
return selection;

const auto pExt = TechnoExt::ExtMap.Find(pThis);
if (!pExt)
return selection;

int retargetProbability = std::min((int)round(pWeaponExt->RandomTarget * 100), 100);
int dice = ScenarioClass::Instance->Random.RandomRanged(1, 100);

if (retargetProbability < dice)
return selection;

auto pThisType = pThis->GetTechnoType();
int minimumRange = pWeapon->MinimumRange;
int range = pWeapon->Range;
int airRange = pWeapon->Range + pThisType->AirRangeBonus;
bool omniFire = pWeapon->OmniFire;
std::vector<TechnoClass*> candidates;
auto originalTarget = abstract_cast<TechnoClass*>(!pExt->OriginalTarget ? pThis->Target : pExt->OriginalTarget);
bool friendlyFire = pThis->Owner->IsAlliedWith(originalTarget);

// Looking for all valid targeting candidates
for (auto pCandidate : *TechnoClass::Array)
{
if (pCandidate == pThis
|| !ScriptExt::IsUnitAvailable(pCandidate, true)
|| pThisType->Immune
|| !EnumFunctions::IsTechnoEligible(pCandidate, pWeaponExt->CanTarget, true)
|| (!pWeapon->Projectile->AA && pCandidate->IsInAir())
|| (!pWeapon->Projectile->AG && !pCandidate->IsInAir())
|| (!friendlyFire && (pThis->Owner->IsAlliedWith(pCandidate) || ScriptExt::IsUnitMindControlledFriendly(pThis->Owner, pCandidate)))
|| pCandidate->TemporalTargetingMe
|| pCandidate->BeingWarpedOut
|| (pCandidate->GetTechnoType()->Underwater && pCandidate->GetTechnoType()->NavalTargeting == NavalTargetingType::Underwater_Never)
|| (pCandidate->GetTechnoType()->Naval && pCandidate->GetTechnoType()->NavalTargeting == NavalTargetingType::Naval_None)
|| (pCandidate->CloakState == CloakState::Cloaked && !pThisType->Naval)
|| (pCandidate->InWhichLayer() == Layer::Underground))
{
continue;
}

int distanceFromAttacker = pThis->DistanceFrom(pCandidate);
if (distanceFromAttacker < minimumRange)
continue;

if (omniFire)
{
if (pCandidate->IsInAir())
range = airRange;

if (distanceFromAttacker <= range)
candidates.push_back(pCandidate);
}
else
{
int distanceFromOriginalTargetToCandidate = pCandidate->DistanceFrom(pThis->Target);
int distanceFromOriginalTarget = pThis->DistanceFrom(pThis->Target);

if (pCandidate->IsInAir())
range = airRange;

if (distanceFromAttacker <= range && distanceFromOriginalTargetToCandidate <= distanceFromOriginalTarget)
Copy link

@coderabbitai coderabbitai bot Nov 9, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The GetRandomTarget function is also quite complex. Similar to the UpdateRandomTarget function, consider breaking it down into smaller functions for better readability and maintainability. For example, the part where it checks if the candidate is valid could be a separate function. This would make the code easier to understand and maintain.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am also not sure what does this function do and why is it in ScriptExt: ScriptExt::IsUnitMindControlledFriendly(pThis->Owner, pCandidate)

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Metadorius The function ScriptExt::IsUnitMindControlledFriendly(pThis->Owner, pCandidate) is likely used to check if a unit (pCandidate) is mind-controlled and is friendly to the owner (pThis->Owner).

The reason it's in ScriptExt might be because it's a utility function that can be used in various scripts, not just in this context. It's common to place such utility functions in a separate class or namespace (like ScriptExt) to avoid code duplication and improve code organization.

However, without more context or access to the ScriptExt class, this is just an educated guess. You might want to check the implementation of ScriptExt::IsUnitMindControlledFriendly for a more accurate understanding.

Copy link
Contributor

@Starkku Starkku Nov 9, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Function ScriptExt::IsUnitMindControlledFriendly() was originally placed in ScriptExt because that is the only place where it was called. If it is now being called elsewhere it should likely be relocated to TechnoExt since that would be more appropriate for function that checks TechnoClass state. Same for ScriptExt::IsUnitAvailable().

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like humans are talking. I'll be quiet for now. If you need me again, just tag @coderabbitai in a new comment.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We usually place static extension functions as if they were the non-static functions of the class that is being extended (think of C# extension methods), so if it accepts the TechnoClass, it should be in TechnoExt. I am not sure if the description you gave is accurate, I would say that it needs a clarification from @FS-21.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean ScriptExt::IsUnitMindControlledFriendly()? I don't touch what other people do, like that ScriptExt::IsUnitAvailable().
I'll move them to TechnoExt then.

@mevitar
Copy link

mevitar commented Nov 10, 2023

Something with weapon firing restrictions is terribly wrong, because GI-like infantry is be able to fire while their deploy sequence is playing. This is not the appropriate behavior, nor does it happen in the dev build or without Phobos - they should wait with firing their weapon until their deploy sequence finishes playing.

Retargetting doesn't work properly for Aircraft Carrier type spawners. Spawned aircrafts can get bugged, and, instead of following the proper target, they will try to fly outside of the map.
retargetspawn2x
This is with RandomTarget=100% and RandomTarget.Spawners.MultipleTargets=yes.

@FS-21
Copy link
Contributor Author

FS-21 commented Nov 11, 2023

Something with weapon firing restrictions is terribly wrong, because GI-like infantry is be able to fire while their deploy sequence is playing. This is not the appropriate behavior, nor does it happen in the dev build or without Phobos - they should wait with firing their weapon until their deploy sequence finishes playing.

Then I need to add additional checks in that case.

Retargetting doesn't work properly for Aircraft Carrier type spawners. Spawned aircrafts can get bugged, and, instead of following the proper target, they will try to fly outside of the map. retargetspawn2x retargetspawn2x This is with RandomTarget=100% and RandomTarget.Spawners.MultipleTargets=yes.

How is that Carrier weapon? The original Allied Carrier works fine with this:

[HornetLauncher]
Damage=1
ROF=150
Range=25
Spawner=yes
Projectile=Invisible
Speed=10
Warhead=Super
OmniFire=yes
RandomTarget=1.0 ; double or percentage
RandomTarget.Spawners.MultipleTargets=true ; boolean

@mevitar
Copy link

mevitar commented Nov 20, 2023

It has nothing to do with the weapon.

The retargeting apparently ignores whether an object is actually targetable by the spawner weapon, which means it will still consider cloaked (and invisible) objects, objects with Immune=yes, and objects with 0% verses armor as legal targets. Thus, this behavior, with spawned aircraft flying away because it is unable to attack the thing it was ordered to target.

Don't know if this happens to non-spawner weapons.

@FS-21
Copy link
Contributor Author

FS-21 commented Nov 21, 2023

Yes, currently the method for picking the target doesn't ask for the spawned weapons capabilities so I'll address the deploy & this issue in the weekend for covering these examples you mentioned.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Status

Actionable comments generated: 6

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between ea7d0e3 and 54f327e.
Files selected for processing (9)
  • src/Ext/Script/Body.cpp (5 hunks)
  • src/Ext/Script/Body.h (1 hunks)
  • src/Ext/Script/Mission.Attack.cpp (7 hunks)
  • src/Ext/Script/Mission.Move.cpp (4 hunks)
  • src/Ext/Techno/Body.Update.cpp (2 hunks)
  • src/Ext/Techno/Body.cpp (3 hunks)
  • src/Ext/Techno/Body.h (6 hunks)
  • src/Ext/Techno/Hooks.Firing.cpp (2 hunks)
  • src/Ext/Techno/Hooks.cpp (2 hunks)
Files skipped from review due to trivial changes (3)
  • src/Ext/Script/Body.cpp
  • src/Ext/Script/Body.h
  • src/Ext/Script/Mission.Move.cpp
Additional comments: 15
src/Ext/Techno/Hooks.Firing.cpp (1)
  • 872-1052:

Ensure that the TechnoExt::IsUnitAvailable function correctly checks for unit availability, as it's used to determine if the current random target is still valid. This is crucial for the correct functioning of the Burst.Retarget feature.

src/Ext/Techno/Hooks.cpp (1)
  • 27-33: The addition of pExt->UpdateRandomTargets(); within the TechnoClass_AI function is correct and aligns with the summary provided. However, ensure that the UpdateRandomTargets method is thread-safe if TechnoClass_AI can be called from multiple threads, as this could lead to race conditions.
src/Ext/Techno/Body.Update.cpp (2)
  • 6-12:
    No action needed regarding the headers as they have been verified to be in use.

  • 734-767:
    The implementation of UpdateRandomTargets seems correct and aligns with the feature's intent. However, ensure that the IsUnitAvailable function correctly handles all edge cases, especially since it has been moved from ScriptExt to TechnoExt.

src/Ext/Techno/Body.h (5)
  • 6-12: The inclusion of multiple Utilities headers seems redundant, as Macro.h is included twice (lines 6 and 8). Verify if this is intentional or if one of the includes can be removed to avoid potential confusion or conflicts.

  • 39-43: The addition of new member variables OriginalTarget and CurrentRandomTarget is consistent with the summary provided. Ensure that these new variables are properly initialized, used, and maintained throughout the codebase.

  • 82-82: The UpdateRandomTargets function is added as per the summary. However, ensure that this function is implemented and called appropriately within the codebase to maintain the new feature's functionality.

  • 126-130: The IsUnitAvailable function has been moved from ScriptExt to TechnoExt as mentioned in the summary. Verify that all references to this function have been updated to reflect its new location.

  • 155-156: The addition of UpdateRandomTarget and FindRandomTarget functions aligns with the summary. Ensure that these functions are implemented and that their logic correctly supports the new retargeting feature.

src/Ext/Script/Mission.Attack.cpp (6)
  • 115-120: The check for TechnoExt::IsUnitAvailable(pFoot, true) is correct and the subsequent code is logically sound. The loop iterates through each unit in the team and checks if it is available before proceeding with further logic.

  • 142-148: The check for TechnoExt::IsUnitAvailable(pLeaderUnit, true) is correct, and the logic to find a new team leader if the current one is not available is sound. The code correctly updates the team leader in the TeamExt data.

  • 298-304: The logic to determine if the current focus is a valid target based on its air/ground status and whether the leader has the appropriate weapons is correct. The additional checks for immunity and mind control status are also appropriate.

  • 309-315: The check for TechnoExt::IsUnitAvailable(pFoot, true) is correct, and the subsequent logic handles various cases for different unit types, such as aircraft and naval units, correctly.

  • 485-491: The loop in GreatestThreat correctly filters out objects that are not valid targets based on various conditions, including availability, immunity, and mind control status. The use of TechnoExt::IsUnitAvailable is consistent with the rest of the codebase.

  • 1167-1173: The logic in Mission_Attack_List1Random correctly filters the list of potential targets based on their availability and whether they are allied or mind-controlled. The use of TechnoExt::IsUnitAvailable is correct, and the loop correctly builds a list of valid indexes for random selection.

Comment on lines 5 to 11
#include <Ext/House/Body.h>
#include <Ext/WarheadType/Body.h>
#include <Ext/WeaponType/Body.h>
#include <Ext/BuildingType/Body.h>
#include <Ext/Script/Body.h>
#include <Utilities/EnumFunctions.h>

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The inclusion of <Ext/BuildingType/Body.h> is redundant as it is already included on line 8. This could lead to potential compilation issues or confusion.

- #include <Ext/BuildingType/Body.h>

Commitable suggestion

IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
#include <Ext/House/Body.h>
#include <Ext/WarheadType/Body.h>
#include <Ext/WeaponType/Body.h>
#include <Ext/BuildingType/Body.h>
#include <Ext/Script/Body.h>
#include <Utilities/EnumFunctions.h>
#include <Ext/House/Body.h>
#include <Ext/WarheadType/Body.h>
#include <Ext/WeaponType/Body.h>
#include <Ext/Script/Body.h>
#include <Utilities/EnumFunctions.h>

Comment on lines 655 to 770
return false;

if (pThis->GetCurrentMission() == Mission::Move)
{
pExt->CurrentRandomTarget = nullptr;
pExt->OriginalTarget = nullptr;

return false;
}

if (pExt->CurrentRandomTarget && IsUnitAvailable(pExt->CurrentRandomTarget, false) && pThis->SpawnManager)
return false;

if (!pThis->Target && !IsUnitAvailable(abstract_cast<TechnoClass*>(pExt->OriginalTarget), false))
{
pExt->OriginalTarget = nullptr;
return false;
}

if (pExt->OriginalTarget && !IsUnitAvailable(abstract_cast<TechnoClass*>(pExt->OriginalTarget), false))
{
pExt->CurrentRandomTarget = nullptr;
pExt->OriginalTarget = nullptr;
}

if (pThis->GetCurrentMission() != Mission::Attack)
{
pExt->OriginalTarget = nullptr;
return false;
}

if (!pThis->Target)
return false;

if (pThis->DistanceFrom(pExt->OriginalTarget) > pWeapon->Range)
{
if (pThis->WhatAmI() == AbstractType::Building)
{
pThis->SetTarget(nullptr);
pExt->CurrentRandomTarget = nullptr;
pExt->OriginalTarget = nullptr;

return false;
}

pThis->SetTarget(pExt->OriginalTarget);
}

if (pThis->DistanceFrom(pThis->Target) > pWeapon->Range)
{
pThis->SetTarget(pExt->OriginalTarget);
return false;
}

auto pRandomTarget = FindRandomTarget(pThis);

if (!pRandomTarget)
return false;

pExt->OriginalTarget = !pExt->OriginalTarget ? pThis->Target : pExt->OriginalTarget;
pExt->CurrentRandomTarget = pRandomTarget;
pThis->Target = pRandomTarget;

if (pThis->SpawnManager)
{
bool isFirstSpawn = true;

for (auto pSpawn : pThis->SpawnManager->SpawnedNodes)
{
if (!pSpawn->Unit)
continue;

TechnoClass* pSpawnTarget = nullptr;

auto pSpawnExt = TechnoExt::ExtMap.Find(pSpawn->Unit);
if (!pSpawnExt)
continue;

if (isFirstSpawn)
{
pSpawnTarget = pExt->CurrentRandomTarget;

if (pWeaponExt->RandomTarget_Spawners_MultipleTargets)
isFirstSpawn = false;
}
else
{
pSpawnTarget = FindRandomTarget(pThis);

if (!pSpawnTarget)
pSpawnTarget = abstract_cast<TechnoClass*>(pExt->OriginalTarget);
}

pSpawnExt->CurrentRandomTarget = pSpawnTarget;
pSpawnExt->OriginalTarget = pExt->OriginalTarget;
}
}

return true;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The UpdateRandomTarget function has several issues:

  • The function assumes that pThis->GetWeapon(weaponIndex) will always return a valid pointer before dereferencing it to access WeaponType. This could lead to a null pointer dereference if SelectWeapon returns an invalid index. Add a null check after getting the weapon.
  • The function uses pThis->DistanceFrom(pExt->OriginalTarget) without checking if pExt->OriginalTarget is a valid pointer, which could lead to a crash. Add a null check before using pExt->OriginalTarget.
  • The function does not check if pThis->SpawnManager is a valid pointer before using it in a loop, which could lead to a crash. Add a null check before the loop that iterates over pThis->SpawnManager->SpawnedNodes.

Comment on lines 773 to 861
TechnoClass* TechnoExt::FindRandomTarget(TechnoClass* pThis)
{
TechnoClass* selection = nullptr;

if (!pThis && !pThis->Target)
return selection;

int weaponIndex = pThis->SelectWeapon(pThis->Target);
auto pWeapon = pThis->GetWeapon(weaponIndex)->WeaponType;
if (!pWeapon)
return selection;

const auto pWeaponExt = WeaponTypeExt::ExtMap.Find(pWeapon);
if (!pWeaponExt || pWeaponExt->RandomTarget <= 0.0)
return selection;

const auto pExt = TechnoExt::ExtMap.Find(pThis);
if (!pExt)
return selection;

int retargetProbability = std::min((int)round(pWeaponExt->RandomTarget * 100), 100);
int dice = ScenarioClass::Instance->Random.RandomRanged(1, 100);

if (retargetProbability < dice)
return selection;

auto pThisType = pThis->GetTechnoType();
int minimumRange = pWeapon->MinimumRange;
int range = pWeapon->Range;
int airRange = pWeapon->Range + pThisType->AirRangeBonus;
bool omniFire = pWeapon->OmniFire;
std::vector<TechnoClass*> candidates;
auto originalTarget = abstract_cast<TechnoClass*>(!pExt->OriginalTarget ? pThis->Target : pExt->OriginalTarget);
bool friendlyFire = pThis->Owner->IsAlliedWith(originalTarget);

// Looking for all valid targeting candidates
for (auto pCandidate : *TechnoClass::Array)
{
if (pCandidate == pThis
|| !IsUnitAvailable(pCandidate, true)
|| pThisType->Immune
|| !EnumFunctions::IsTechnoEligible(pCandidate, pWeaponExt->CanTarget, true)
|| (!pWeapon->Projectile->AA && pCandidate->IsInAir())
|| (!pWeapon->Projectile->AG && !pCandidate->IsInAir())
|| (!friendlyFire && (pThis->Owner->IsAlliedWith(pCandidate) || ScriptExt::IsUnitMindControlledFriendly(pThis->Owner, pCandidate)))
|| pCandidate->TemporalTargetingMe
|| pCandidate->BeingWarpedOut
|| (pCandidate->GetTechnoType()->Underwater && pCandidate->GetTechnoType()->NavalTargeting == NavalTargetingType::Underwater_Never)
|| (pCandidate->GetTechnoType()->Naval && pCandidate->GetTechnoType()->NavalTargeting == NavalTargetingType::Naval_None)
|| (pCandidate->CloakState == CloakState::Cloaked && !pThisType->Naval)
|| (pCandidate->InWhichLayer() == Layer::Underground))
{
continue;
}

int distanceFromAttacker = pThis->DistanceFrom(pCandidate);
if (distanceFromAttacker < minimumRange)
continue;

if (omniFire)
{
if (pCandidate->IsInAir())
range = airRange;

if (distanceFromAttacker <= range)
candidates.push_back(pCandidate);
}
else
{
int distanceFromOriginalTargetToCandidate = pCandidate->DistanceFrom(pThis->Target);
int distanceFromOriginalTarget = pThis->DistanceFrom(pThis->Target);

if (pCandidate->IsInAir())
range = airRange;

if (distanceFromAttacker <= range && distanceFromOriginalTargetToCandidate <= distanceFromOriginalTarget)
candidates.push_back(pCandidate);
}
}

if (candidates.size() == 0)
return selection;

// Pick one new target from the list of targets inside the weapon range
dice = ScenarioClass::Instance->Random.RandomRanged(0, candidates.size() - 1);
selection = candidates.at(dice);

return selection;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The FindRandomTarget function has a potential issue:

  • The function does not check if pThis->Target is a valid pointer before using it in calculations for distanceFromOriginalTargetToCandidate and distanceFromOriginalTarget. This could lead to a crash if pThis->Target is null. Add a null check before using pThis->Target.

Comment on lines 882 to 887
.Process(this->DeployFireTimer)
.Process(this->ForceFullRearmDelay)
.Process(this->WHAnimRemainingCreationInterval)
.Process(this->OriginalTarget)
.Process(this->CurrentRandomTarget)
;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The serialization of OriginalTarget and CurrentRandomTarget is added, but there is no version check to ensure backward compatibility with save files that do not have these fields. This could lead to issues when loading older save files. Implement a version check to handle the absence of these fields in older save files.

Comment on lines 60 to 66

pFocus = abstract_cast<TechnoClass*>(pTeam->Focus);

if (!IsUnitAvailable(pFocus, true))
if (!TechnoExt::IsUnitAvailable(pFocus, true))
{
pTeam->Focus = nullptr;
pFocus = nullptr;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The check for TechnoExt::IsUnitAvailable(pFocus, true) is correct, but the subsequent nulling of pTeam->Focus and pFocus should be inside the if block. The current placement outside the block will null the focus regardless of whether pFocus is available or not, which is likely unintended.

	if (!TechnoExt::IsUnitAvailable(pFocus, true))
	{
-		pTeam->Focus = nullptr;
-		pFocus = nullptr;
+		pTeam->Focus = nullptr;
+		pFocus = nullptr;
	}

Commitable suggestion

IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
pFocus = abstract_cast<TechnoClass*>(pTeam->Focus);
if (!IsUnitAvailable(pFocus, true))
if (!TechnoExt::IsUnitAvailable(pFocus, true))
{
pTeam->Focus = nullptr;
pFocus = nullptr;
pFocus = abstract_cast<TechnoClass*>(pTeam->Focus);
if (!TechnoExt::IsUnitAvailable(pFocus, true))
{
pTeam->Focus = nullptr;
pFocus = nullptr;
}

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Status

Actionable comments generated: 5

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between 54f327e and 1692452.
Files selected for processing (11)
  • CREDITS.md (1 hunks)
  • docs/New-or-Enhanced-Logics.md (1 hunks)
  • docs/Whats-New.md (1 hunks)
  • src/Ext/Script/Body.cpp (5 hunks)
  • src/Ext/Techno/Body.Update.cpp (3 hunks)
  • src/Ext/Techno/Body.cpp (3 hunks)
  • src/Ext/Techno/Body.h (6 hunks)
  • src/Ext/Techno/Hooks.Firing.cpp (2 hunks)
  • src/Ext/Techno/Hooks.cpp (1 hunks)
  • src/Ext/WeaponType/Body.cpp (2 hunks)
  • src/Ext/WeaponType/Body.h (2 hunks)
Additional comments: 26
src/Ext/WeaponType/Body.h (3)
  • 42-42: The addition of RandomTarget as a double and its initialization to 0.0 is appropriate for representing a percentage or range value.
  • 43-43: The addition of RandomTarget_DistributeBurst as a boolean and its initialization to false is logical for a default behavior.
  • 44-44: The addition of RandomTarget_Spawners_MultipleTargets as a boolean and its initialization to false makes sense to require explicit enabling for this behavior.
src/Ext/Techno/Body.h (5)
  • 9-9: The inclusion of EnumFunctions.h is appropriate for the functionality introduced in this file.
  • 42-44: The addition of OriginalTarget, ResetRandomTarget, and CurrentRandomTarget members, along with their initializations, is logical for managing the state of random targeting.
  • 86-86: The addition of the UpdateRandomTargets function is appropriate for implementing the random target feature.
  • 131-131: The addition of the IsUnitAvailable static function is appropriate for checking unit availability in the context of random targeting.
  • 158-160: The addition of UpdateRandomTarget, FindRandomTarget, and IsValidTechno static functions is logical for managing random target selection and validation.
src/Ext/WeaponType/Body.cpp (2)
  • 62-64: The modifications to LoadFromINIFile to support reading the new random target properties from INI files are appropriate and necessary for external configuration.
  • 92-94: The modifications to Serialize to support serializing and deserializing the new random target properties are crucial for saving and loading these properties.
src/Ext/Techno/Hooks.cpp (1)
  • 9-9: The inclusion of Script/Body.h is appropriate for the functionality introduced in this file.
CREDITS.md (1)
  • 133-133: The addition of the Burst.Retarget feature by Starkku is correctly formatted and consistent with the rest of the document.
src/Ext/Techno/Body.cpp (3)
  • 8-9: Adding new includes for WeaponType and Script extensions is necessary for the new functionalities introduced in this PR. Ensure that these headers are properly guarded against multiple inclusions and that they do not introduce circular dependencies.
  • 503-595: The FindRandomTarget function is responsible for finding a random target within the attacker's area. It's a critical part of the retargeting feature and seems to be well-implemented with checks for various conditions. However, ensure that the function correctly handles edge cases, such as when no valid targets are found or when specific targeting restrictions apply.
  • Verify that the function's logic correctly accounts for all game rules and scenarios.
  • Consider adding comments to complex conditional checks to improve readability.
  • 597-611: The IsValidTechno function checks if a techno is valid for targeting or other actions. It's a good practice to centralize such checks in a single function to avoid code duplication and ensure consistency. However, consider adding more detailed comments explaining each condition for future maintainability.
src/Ext/Techno/Body.Update.cpp (2)
  • 10-10: The inclusion of <Ext/Script/Body.h> seems necessary for the new functionality introduced. Ensure that this header is indeed used within the file to avoid unnecessary compilation dependencies.
  • 751-809: The UpdateRandomTargets function introduces logic for handling random target updates for units. Here are a few observations and suggestions:
  1. Correctness and Logic: The function checks for the validity of the current random target and updates or resets it based on various conditions. Ensure that all conditions accurately reflect the intended logic, especially the checks for IsUnitAvailable and the handling of SpawnManager's spawned nodes.

  2. Performance: The function iterates over SpawnManager's spawned nodes within a loop (lines 780-799). Consider the potential performance impact if there are a large number of spawned nodes. If possible, look for optimizations or ensure that this operation does not become a bottleneck.

  3. Maintainability: The function is relatively large and handles multiple responsibilities. Consider breaking it down into smaller, more focused functions if the logic becomes too complex or if there are distinct sections that could be modularized. This will improve readability and maintainability.

  4. Error Handling: Ensure that there is adequate error handling, especially for external calls that might fail or return unexpected results.

Overall, the function seems to align with the objectives of the PR, enhancing gameplay dynamics by allowing weapons to select targets randomly within defined parameters. Just ensure that the implementation details are thoroughly tested, especially the interactions with different unit types and the SpawnManager.

src/Ext/Techno/Hooks.Firing.cpp (1)
  • 868-1110: The implementation of the Burst.Retarget feature and handling of random target selection for units introduces several hooks and logic to update targets under specific conditions. While the overall approach aligns with the PR objectives, there are a few areas that could benefit from further refinement:
  1. Consistency in Target Validation: The checks for valid targets are repeated across multiple hooks (SpawnerManagerClassAI_SwitchCase3, SpawnerManagerClassAI_SwitchCase4, SpawnerManagerClassAI_SpawnControlStatus3, TechnoClass_FireAt_BulletNewTarget). Consider abstracting these checks into a utility function to reduce code duplication and improve maintainability.

  2. Error Handling: Ensure that all external calls, especially those interacting with game objects and their properties, are safely handled. For instance, checks for null pointers and the existence of required extensions should be thorough to prevent crashes.

  3. Performance Considerations: The loops iterating over all current objects in AIMissionClassUAEXXZ_StopSelected_ClearRetargets and similar hooks could potentially impact performance, especially in large-scale battles. Evaluate if there are more efficient ways to achieve the desired functionality, such as maintaining a list of units with random targets that need to be cleared.

  4. Documentation and Comments: While the code changes are significant, there's a noticeable lack of comments explaining the logic and decisions behind certain implementations. Adding detailed comments would greatly aid in understanding the code, especially for complex logic related to targeting and weapon selection.

  5. Testing and Validation: Given the complexity of the changes and their potential impact on gameplay, thorough testing is recommended. This includes testing with various unit types, weapon configurations, and game scenarios to ensure that the Burst.Retarget feature behaves as expected without introducing bugs or balance issues.

Consider the above points to refine the implementation, focusing on code maintainability, performance, and robustness. If you need assistance with specific improvements or testing strategies, feel free to ask.

src/Ext/Script/Body.cpp (6)
  • 420-420: The call to TechnoExt::IsUnitAvailable(pLeaderUnit, true) correctly reflects the function's new location. However, ensure that the TechnoExt class and the IsUnitAvailable function are properly implemented to handle the logic previously managed by ScriptExt.
  • 455-455: The repeated calls to TechnoExt::IsUnitAvailable(pUnit, true) within the loop correctly use the updated function location. It's important to verify that the TechnoExt implementation of IsUnitAvailable maintains the same logic and error handling as its predecessor in ScriptExt.
  • 753-753: The usage of TechnoExt::IsUnitAvailable in the MoveMissionEndStatus function correctly reflects the function's relocation. As with other instances, ensure that the TechnoExt version of IsUnitAvailable is fully compatible with the expected behavior in this context.
  • 1085-1085: The call to TechnoExt::IsUnitAvailable in FindTheTeamLeader is correctly updated to match the function's new location. This is a critical part of determining the team leader, so it's essential to ensure that the TechnoExt implementation of IsUnitAvailable performs as expected.
  • 417-423: > 📝 NOTE

This review was outside the diff hunks, and no overlapping diff hunk was found. Original lines [1-1]

The file includes necessary headers and maintains a clean structure, which is good for maintainability. Ensure that all included headers are used within this file to avoid unnecessary dependencies.

  • 417-423: > 📝 NOTE

This review was outside the diff hunks, and no overlapping diff hunk was found. Original lines [1-1]

Given the extensive use of TechnoExt::IsUnitAvailable following its move from ScriptExt, consider adding a brief comment at each call site briefly explaining the criteria for unit availability. This would enhance readability and maintainability by providing context directly within the ScriptExt file, especially for developers unfamiliar with the TechnoExt implementation.

docs/Whats-New.md (1)
  • 351-351: The addition of the Burst.Retarget feature as highlighted in the summary aligns well with the detailed changelog provided. This feature, along with the other listed changes, showcases significant enhancements to the project.
docs/New-or-Enhanced-Logics.md (1)
  • 1277-1292: The documentation provides a clear explanation of the RandomTarget feature for projectiles, including its behavior with missiles, cannons, lasers, and spawners. It also outlines how OmniFire affects target selection and the special handling for spawners with RandomTarget.Spawners.MultipleTargets=true. This feature enhances gameplay by allowing for more dynamic interactions between units and their targets. The provided code snippet correctly demonstrates how to configure this feature in rulesmd.ini.

Comment on lines +367 to +378
bool TechnoExt::IsUnitAvailable(TechnoClass* pTechno, bool checkIfInTransportOrAbsorbed)
{
if (!pTechno)
return false;

bool isAvailable = pTechno->IsAlive && pTechno->Health > 0 && !pTechno->InLimbo && pTechno->IsOnMap;

if (checkIfInTransportOrAbsorbed)
isAvailable &= !pTechno->Absorbed && !pTechno->Transporter;

return isAvailable;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The IsUnitAvailable function checks basic conditions to determine if a unit is available for action. Consider adding checks for more specific conditions that could affect a unit's availability, such as being targeted by temporal weapons or being warped out, as mentioned in previous comments.

bool TechnoExt::IsUnitAvailable(TechnoClass* pTechno, bool checkIfInTransportOrAbsorbed)
{
  if (!pTechno)
    return false;

  bool isAvailable = pTechno->IsAlive && pTechno->Health > 0 && !pTechno->InLimbo && pTechno->IsOnMap;

+ // Consider adding checks for temporal targeting and warp out status here
+ isAvailable &= !pTechno->TemporalTargetingMe && !pTechno->BeingWarpedOut;

  if (checkIfInTransportOrAbsorbed)
    isAvailable &= !pTechno->Absorbed && !pTechno->Transporter;

  return isAvailable;
}

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
bool TechnoExt::IsUnitAvailable(TechnoClass* pTechno, bool checkIfInTransportOrAbsorbed)
{
if (!pTechno)
return false;
bool isAvailable = pTechno->IsAlive && pTechno->Health > 0 && !pTechno->InLimbo && pTechno->IsOnMap;
if (checkIfInTransportOrAbsorbed)
isAvailable &= !pTechno->Absorbed && !pTechno->Transporter;
return isAvailable;
}
bool TechnoExt::IsUnitAvailable(TechnoClass* pTechno, bool checkIfInTransportOrAbsorbed)
{
if (!pTechno)
return false;
bool isAvailable = pTechno->IsAlive && pTechno->Health > 0 && !pTechno->InLimbo && pTechno->IsOnMap;
// Consider adding checks for temporal targeting and warp out status here
isAvailable &= !pTechno->TemporalTargetingMe && !pTechno->BeingWarpedOut;
if (checkIfInTransportOrAbsorbed)
isAvailable &= !pTechno->Absorbed && !pTechno->Transporter;
return isAvailable;
}

Comment on lines +380 to +501

return false;
}

if (!IsValidTechno(pExt->CurrentRandomTarget))
pExt->CurrentRandomTarget = nullptr;

if (pExt->OriginalTarget && !IsValidTechno(static_cast<TechnoClass*>(pExt->OriginalTarget)))
pExt->OriginalTarget = nullptr;

if (pThis->Target && !IsValidTechno(static_cast<TechnoClass*>(pThis->Target)))
pThis->SetTarget(nullptr);

if (pThis->Target && pExt->CurrentRandomTarget && pThis->CurrentMission == Mission::Guard)
pThis->ForceMission(Mission::Attack);

if (pExt->CurrentRandomTarget && pThis->SpawnManager)
return false;

if (!pThis->Target && !pExt->OriginalTarget)
return false;

if (!pExt->OriginalTarget)
{
pExt->CurrentRandomTarget = nullptr;
pExt->OriginalTarget = nullptr;
}

if (pExt->CurrentRandomTarget && pThis->GetCurrentMission() != Mission::Attack)
{
pExt->CurrentRandomTarget = nullptr;
pThis->SetTarget(pExt->OriginalTarget ? pExt->OriginalTarget : nullptr);
pExt->OriginalTarget = nullptr;

return false;
}

if (!pThis->Target)
return false;

if (pThis->DistanceFrom(pExt->OriginalTarget) > pWeapon->Range || pThis->DistanceFrom(pThis->Target) > pWeapon->Range)
{
if (pThis->WhatAmI() == AbstractType::Building)
{
pThis->SetTarget(nullptr);
pExt->CurrentRandomTarget = nullptr;
pExt->OriginalTarget = nullptr;

return false;
}

pThis->SetTarget(pExt->OriginalTarget);
}

auto pRandomTarget = FindRandomTarget(pThis);

if (!pRandomTarget)
return false;

pExt->OriginalTarget = !pExt->OriginalTarget ? pThis->Target : pExt->OriginalTarget;
pExt->CurrentRandomTarget = pRandomTarget;
pThis->Target = pRandomTarget;

if (pThis->SpawnManager)
{
bool isFirstSpawn = true;

for (auto pSpawn : pThis->SpawnManager->SpawnedNodes)
{
if (!pSpawn->Unit)
continue;

TechnoClass* pSpawnTarget = nullptr;

auto pSpawnExt = TechnoExt::ExtMap.Find(pSpawn->Unit);
if (!pSpawnExt)
continue;

if (isFirstSpawn)
{
pSpawnTarget = pExt->CurrentRandomTarget;

if (pWeaponExt->RandomTarget_Spawners_MultipleTargets)
isFirstSpawn = false;
}
else
{
pSpawnTarget = FindRandomTarget(pThis);

if (!pSpawnTarget)
pSpawnTarget = static_cast<TechnoClass*>(pExt->OriginalTarget);
}

pSpawnExt->CurrentRandomTarget = pSpawnTarget;
pSpawnExt->OriginalTarget = pExt->OriginalTarget;
}
}

return true;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The UpdateRandomTarget function is complex and handles multiple scenarios. It's well-structured but could benefit from breaking down into smaller functions for better readability and maintainability. Additionally, ensure that all dereferenced pointers are checked for null before use to prevent potential null pointer dereferences.

  • Consider refactoring to split the function into smaller, more focused functions.
  • Add null checks for pointers like pWeapon and pThis->GetWeapon(weaponIndex) before dereferencing.

Comment on lines +635 to +637
.Process(this->OriginalTarget)
.Process(this->ResetRandomTarget)
.Process(this->CurrentRandomTarget)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Serialization of new data members OriginalTarget, ResetRandomTarget, and CurrentRandomTarget is crucial for saving and loading game states. Ensure that version checks are implemented to maintain backward compatibility with older save files, as mentioned in previous comments.

void TechnoExt::ExtData::Serialize(T& Stm)
{
  Stm
    .Process(this->TypeExtData)
    ...
+   // Implement version checks here to ensure backward compatibility
    .Process(this->OriginalTarget)
    .Process(this->ResetRandomTarget)
    .Process(this->CurrentRandomTarget)
    ;
}

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
.Process(this->OriginalTarget)
.Process(this->ResetRandomTarget)
.Process(this->CurrentRandomTarget)
// Implement version checks here to ensure backward compatibility
.Process(this->OriginalTarget)
.Process(this->ResetRandomTarget)
.Process(this->CurrentRandomTarget)

@@ -5,6 +5,8 @@
#include <Ext/House/Body.h>
#include <Ext/WarheadType/Body.h>
#include <Ext/WeaponType/Body.h>
#include <Ext/BuildingType/Body.h>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The inclusion of <Ext/BuildingType/Body.h> is redundant as it is already included on line 5. This could lead to potential compilation issues or confusion.

- #include <Ext/BuildingType/Body.h>

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
#include <Ext/BuildingType/Body.h>

Comment on lines 348 to 354
- Customizable straight trajectory detonation & snap distance and pass-through option (by Starkku)
- Airstrike & spy plane fixed spawn distance & height (by Starkku)
- Allow enabling application of `Verses` and `PercentAtMax` for negative damage (by Starkku)
- `Burst.Retarget` for assigning a new target in each projectile (by FS-21)
- In addition to `PlacementGrid.Translucency`, allow to set the transparency of the grid when PlacementPreview is enabled, using the `PlacementGrid.TranslucencyWithPreview` tag (by Belonit).
- Show briefing screen on singleplayer mission start (by Starkku)
- Allow setting mission par times and related messages in `missionmd.ini` (by Starkku)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 NOTE
This review was outside the diff hunks, and no overlapping diff hunk was found. Original lines [3-3]

There's a spelling mistake in the word "accomodate". It should be spelled as "accommodate".

- accomodate
+ accommodate

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Feature / Enhancement Anything new not present in vanilla/Ares
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants