Skip to content

Refactor tactical action service#3506

Merged
heisenbugged merged 8 commits intomasterfrom
tactical-huge-refactor
Aug 19, 2025
Merged

Refactor tactical action service#3506
heisenbugged merged 8 commits intomasterfrom
tactical-huge-refactor

Conversation

@heisenbugged
Copy link
Collaborator

@heisenbugged heisenbugged commented Aug 15, 2025

This refactors tactical actions to have colocated responsibilities

The core tactical action code is moved to TacticalActionDisplacementService .. which does all things around handling the displacement map representing movement

All the "button assembly" code to let users trigger their abilities have been grouped into three categories

movement abilities -- things you trigger during general movement
post movement abilities -- things you trigger once your movement is done from a system
planet abilities (few) -- things you trigger on planets

this follows a similar pattern to the agenda resolver, using a registry instead of just interminable if/else with shared interfaces

@jrkd
Copy link
Collaborator

jrkd commented Aug 15, 2025

@heisenbugged Is there any reason to not simply call 'displacement' -> 'movement' instead? Seems like an unneccessary distinction

@heisenbugged
Copy link
Collaborator Author

heisenbugged commented Aug 15, 2025

@jrkd sticking with the existing terminology mostly. movement is tracked via a 'displacement map' (which is the differential) so it makes sense to me

@jrkd
Copy link
Collaborator

jrkd commented Aug 15, 2025

@heisenbugged no worries. Ive been away from the code for a while so keeping current terms sounds right to me.

Also, Ive made a bunch inline comments - feel free to ignore or disagree. I havent pulled it cause i dont have a local java setup anymore and better to also get someone whos currently active like Will have a go through too but saw the review come through on discord and thought i could offer a once over

import ti4.service.emoji.FactionEmojis;
import ti4.service.emoji.SourceEmojis;

public final class MoveAbilities {
Copy link
Collaborator

Choose a reason for hiding this comment

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

This smells like an enum to me.

Tile tile,
String planetName,
UnitType type,
int amt,
Copy link
Collaborator

Choose a reason for hiding this comment

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

use full name for clarity (amount)

public final class MoveAbilities {
private MoveAbilities() {}

public static final List<MoveAbility> ABILITIES = List.of(
Copy link
Collaborator

Choose a reason for hiding this comment

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

Personally i would have these as small classes in a folder of move abilities rather than a massive list inside a single file

}

public void contribute(MoveContext ctx, List<Button> buttons) {
buttons.add(Buttons.gray("exhaustAgent_belkoseaagent", "Use Belkosea Agent", FactionEmojis.belkosea));
Copy link
Collaborator

Choose a reason for hiding this comment

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

If the purpose of this is to extract game logic from discord, not using 'Button' directly will probably help - maybe something like 'Available player actions' or do this glue binding between an ability class and the buttons as a separate file away from the core ability code- appreciate this is probably out of scope


public static final class Aetherstream implements MoveAbility {
public boolean enabled(MoveContext ctx) {
return ctx.player.hasTech("as")
Copy link
Collaborator

Choose a reason for hiding this comment

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

Id define all these "<x ability/tech/leader id>" as constants at the top of related ability classes separete from the logic

return planet.getUnitCountForState(unitType, owner, state) >= count;
}

public String buildLandButtonId(int count, UnitType unitType, Player owner) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

another mention of button - this is different though cause the id is still valid regardless of discord/web/whatever - perhaps this could be called something more generic like InteractionId?

void contribute(LandingContext ctx, List<Button> buttons);
}

public static final List<PlanetButtonAbility> ABILITIES = List.of(new DihmohnAgent(), new TnelisDeploy());
Copy link
Collaborator

Choose a reason for hiding this comment

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

Are we able to DI these ability sets rather than a const list?

public static final class ShroudOfLith implements PostMovementButtonAbility {
public boolean enabled(PostMovementButtonContext ctx) {
return ctx.player.hasAbility("shroud_of_lith")
&& ButtonHelperFactionSpecific.getKolleccReleaseButtons(ctx.player, ctx.game)
Copy link
Collaborator

Choose a reason for hiding this comment

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

This logic is particularly entangled with discord buttons - as whether you can do it is based on the current number of buttons available - perhaps theres another approach here that doesnt require checking on button state?

return ctx.player.hasLeaderUnlocked("muaathero")
&& !ctx.tile.isMecatol()
&& !ctx.tile.isHomeSystem(ctx.game)
&& ButtonHelper.getTilesOfPlayersSpecificUnits(ctx.game, ctx.player, UnitType.Warsun)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Similar to line 84 - this ability logic is tied directly to the button helper defining who owns tiles with warsuns - perhaps this could be refactored?

@heisenbugged
Copy link
Collaborator Author

@jrkd
thanks! rather than respond to every individual comment high level

On DI
100% agree, but there are some differing team opinions on this. I actually have an open pr showing spring DI integration but haven't merged it yet, so totally on the same page!

On abilities having button code
Your review has made it clear to my my naming is bad. All the "abilities" here aren't ability execution rather
ability button registering, so it's all intrinsically tied to discord. (I don't love the contribute() method but side-point). I will rename to make sure the word Button is included so that's clearer.

On ability refactoring
You made some comments about the internals of the ability, most of which I agree with, but I'm mostly not touching the actual implementation details here and simply porting them over (don't want to introduce bugs)

This is "round 1" and there are several rounds to come, continuing to separate more. The Web API will need to trigger discord buttons as it will only handle the movement part of a tactical action, so exactly what that boundary looks like I leave to future implementation

thank you so much!

@heisenbugged heisenbugged merged commit 170e9bc into master Aug 19, 2025
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants