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

Weapon attack cleanup #5922

Open
wants to merge 20 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
175a3f1
refactor: Add function for streaming bay weapons
Saklad5 Aug 18, 2024
2db8795
refactor: Use stream composition for finding TAG spotters
Saklad5 Aug 18, 2024
be1cec7
refactor: Use stream composition for NC3 firing solutions
Saklad5 Aug 18, 2024
29643a5
refactor: Use stream composition for C3 firing solutions
Saklad5 Aug 18, 2024
34ecf28
refactor: Use stream composition to check for usable weapon bays
Saklad5 Aug 18, 2024
80404a0
refactor: Compact Naval C3 firing solution check
Saklad5 Aug 20, 2024
d3c98a9
refactor: Use stream composition for adding up bay heat
Saklad5 Aug 20, 2024
76b4441
refactor: Use stream composition for adding up non-bay DropShip heat
Saklad5 Aug 20, 2024
ca0bbd2
refactor: Use stream composition for strike attack targeting
Saklad5 Aug 20, 2024
c2827f9
refactor: Use stream composition for firing Arrow IV bays
Saklad5 Aug 21, 2024
d061789
refactor: Use stream composition for BA AP attacks
Saklad5 Aug 21, 2024
564eb35
refactor: Use stream composition for BA Narc targeting
Saklad5 Aug 21, 2024
c59a753
refactor: Use stream composition for firing BA tasers/Narcs
Saklad5 Aug 22, 2024
720a88e
refactor: Use stream composition for firing field guns
Saklad5 Aug 24, 2024
bdfb73b
refactor: Use stream composition when checking for previously-fired s…
Saklad5 Aug 24, 2024
d16dce5
refactor: Use stream composition when firing solo weapons
Saklad5 Aug 24, 2024
891a17d
refactor: Use stream composition for firing ProtoMech main/arm guns
Saklad5 Aug 24, 2024
b21fe79
refactor: Use stream composition for air-to-ground target modifiers
Saklad5 Aug 24, 2024
12ed947
refactor: Use stream composition for sensor shadows
Saklad5 Aug 24, 2024
014cb8f
refactor: Use stream composition for C3 with active probes
Saklad5 Aug 24, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 25 additions & 42 deletions megamek/src/megamek/common/actions/WeaponAttackAction.java
Original file line number Diff line number Diff line change
Expand Up @@ -555,11 +555,12 @@
|| (atype.getAmmoType() == AmmoType.T_NLRM)
|| (atype.getAmmoType() == AmmoType.T_MEK_MORTAR))
&& (munition.contains(AmmoType.Munitions.M_SEMIGUIDED))) {
for (TagInfo ti : game.getTagInfo()) {
if (target.getId() == ti.target.getId()) {
spotter = game.getEntity(ti.attackerId);
}
}
final Targetable currentTarget = target; // Required for concurrency reasons
Copy link
Member

Choose a reason for hiding this comment

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

For readability and code length, this does not feel like an improvement to me.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

target really should be final, but the current implementation for Swarm LRMs prevents that.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I find this more readable because it makes it much easier to tell that it is simply getting any entity that successfully applied TAG to the target.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If this is an issue, I could just drop this commit. I'd rather not hold up further review of this pull request due to a few controversial changes.

Copy link
Collaborator

Choose a reason for hiding this comment

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

This changes the code and may have other impacts as the old code picked the last target that matched where as the new code picks a random one.

Depending upon the rules, this may not be a valid change.

Copy link
Contributor Author

@Saklad5 Saklad5 Sep 13, 2024

Choose a reason for hiding this comment

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

This will literally never make a difference. The source of the TAG does not matter.

spotter = game.getTagInfo().stream()
.filter(ti -> currentTarget.getId() == ti.target.getId())
.findAny()
.map(ti -> game.getEntity(ti.attackerId))
.orElse(null);
}
}

Expand Down Expand Up @@ -954,11 +955,8 @@
toHit = new ToHitData();
}

Entity te = null;
if (ttype == Targetable.TYPE_ENTITY) {
//Some weapons only target valid entities
te = (Entity) target;
}
//Some weapons only target valid entities
final Entity te = ttype == Targetable.TYPE_ENTITY ? (Entity) target : null;

// If the attacker and target are in the same building & hex, they can
// always attack each other, TW pg 175.
Expand All @@ -972,7 +970,7 @@
}

// are we bracing a location that's not where the weapon is located?
if (ae.isBracing() && (ae.braceLocation() != weapon.getLocation())) {

Check warning

Code scanning / CodeQL

Dereferenced variable may be null Warning

Variable
weapon
may be null at this access as suggested by
this
null guard.
Variable
weapon
may be null at this access as suggested by
this
null guard.
Variable
weapon
may be null at this access as suggested by
this
null guard.
Variable
weapon
may be null at this access as suggested by
this
null guard.
Variable
weapon
may be null at this access as suggested by
this
null guard.
Variable
weapon
may be null at this access as suggested by
this
null guard.
Variable
weapon
may be null at this access as suggested by
this
null guard.
Variable
weapon
may be null at this access as suggested by
this
null guard.
Variable
weapon
may be null at this access as suggested by
this
null guard.
Variable
weapon
may be null at this access as suggested by
this
null guard.
Variable
weapon
may be null at this access as suggested by
this
null guard.
Variable
weapon
may be null at this access as suggested by
this
null guard.
Variable
weapon
may be null at this access as suggested by
this
null guard.
return String.format(Messages.getString("WeaponAttackAction.BracingOtherLocation"),
ae.getLocationName(ae.braceLocation()), ae.getLocationName(weapon.getLocation()));
}
Expand Down Expand Up @@ -1275,13 +1273,9 @@
&& ae.isSpaceborne()) {
boolean networkFiringSolution = false;
//Check to see if the attacker has a firing solution. Naval C3 networks share targeting data
if (ae.hasNavalC3()) {
for (Entity en : game.getC3NetworkMembers(ae)) {
if (te != null && en.hasFiringSolutionFor(te.getId())) {
networkFiringSolution = true;
break;
}
}
if (ae.hasNavalC3() && te != null
Saklad5 marked this conversation as resolved.
Show resolved Hide resolved
&& game.getC3NetworkMembers(ae).stream().anyMatch(en -> en.hasFiringSolutionFor(te.getId()))) {
networkFiringSolution = true;
}
if (!networkFiringSolution) {
//If we don't check for target type here, we can't fire screens and missiles at hexes...
Expand All @@ -1303,15 +1297,14 @@
&& (te != null) && te.hasSeenEntity(ae.getOwner()))
&& !isArtilleryIndirect && !isIndirect && !isBearingsOnlyMissile) {
boolean networkSee = false;
if (ae.hasC3() || ae.hasC3i() || ae.hasActiveNovaCEWS()) {
if (ae.hasC3() || ae.hasC3i() || ae.hasActiveNovaCEWS()
&& game.getEntitiesVector().stream().anyMatch(en ->
!en.isEnemyOf(ae)
&& en.onSameC3NetworkAs(ae)
&& Compute.canSee(game, en, target))) {
Comment on lines +1299 to +1303
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please don't do this inside of a conditional block. If you're going to do a stream, please do so either inside or outside of it.

// c3 units can fire if any other unit in their network is in
// visual or sensor range
for (Entity en : game.getEntitiesVector()) {
if (!en.isEnemyOf(ae) && en.onSameC3NetworkAs(ae) && Compute.canSee(game, en, target)) {
networkSee = true;
break;
}
}
networkSee = true;
}
if (!networkSee) {
if (!Compute.inSensorRange(game, ae, target, null)) {
Expand Down Expand Up @@ -1410,27 +1403,17 @@

// limit large craft to zero net heat and to heat by arc
final int heatCapacity = ae.getHeatCapacity();
if (ae.usesWeaponBays() && (weapon != null) && !weapon.getBayWeapons().isEmpty()) {
if (ae.usesWeaponBays() && (weapon != null)) {
int totalHeat = 0;

// first check to see if there are any usable weapons
boolean usable = false;
for (WeaponMounted m : weapon.getBayWeapons()) {
WeaponType bayWType = m.getType();
boolean bayWUsesAmmo = (bayWType.getAmmoType() != AmmoType.T_NA);
if (m.canFire()) {
if (bayWUsesAmmo) {
if ((m.getLinked() != null) && (m.getLinked().getUsableShotsLeft() > 0)) {
usable = true;
break;
}
} else {
usable = true;
break;
}
}
}
if (!usable) {
if (!weapon.streamBayWeapons()
Copy link
Member

Choose a reason for hiding this comment

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

Request to keep streams out of if statements. I'd rather have an extra line and boolean assignment and a variable name that says what this test is about.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't see how having a distinct boolean would add clarity beyond what the existing comment provides.

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 want me to change the comment to be an initialized boolean anyway?

Copy link
Collaborator

Choose a reason for hiding this comment

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

I agree with @SJuliez. It makes the code harder to read.

.filter(WeaponMounted::canFire)
.anyMatch(m ->
m.getType().getAmmoType() == AmmoType.T_NA
|| Optional.ofNullable(m.getLinked()).map(a -> a.getUsableShotsLeft() > 0).orElse(false)
)
) {
return Messages.getString("WeaponAttackAction.BayNotReady");
}

Expand Down
14 changes: 11 additions & 3 deletions megamek/src/megamek/common/equipment/WeaponMounted.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import java.util.Objects;
import java.util.Vector;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class WeaponMounted extends Mounted<WeaponType> {

Expand Down Expand Up @@ -207,12 +208,19 @@ public void clearBayWeapons() {
}

/**
* @return All the weapon mounts in the bay.
* @return A stream containing the weapon mounts in the bay.
*/
public List<WeaponMounted> getBayWeapons() {
public Stream<WeaponMounted> streamBayWeapons() {
return bayWeapons.stream()
.map(i -> getEntity().getWeapon(i))
.filter(Objects::nonNull)
.filter(Objects::nonNull);
}

/**
* @return All the weapon mounts in the bay.
*/
public List<WeaponMounted> getBayWeapons() {
return streamBayWeapons()
.collect(Collectors.toList());
}

Expand Down