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

Add Battlers and Combat logic #218

Merged
merged 23 commits into from
Jul 27, 2024
Merged

Conversation

food-please
Copy link
Contributor

@food-please food-please commented Jul 24, 2024

Another PR looking to address the Combat issue.

This removes placeholder assets in the combat state and replaces them with Battlers and a handful of nodes needed to get them on the screen.

  • Battlers are generic objects that handle any given combatant. They are described by...
  • ...BattlerStats, which measure the numerical qualities of a given Battler (i.e. HP, energy, attack strength, etc.)...
  • ...and BattlerAnim(ations) which give a visual representation to a Battler. There is a small closed set of activities that a Battler undertakes, so a given character/enemy just needs its animation described in terms of these stimuli.

This should also add logic to the combat, transitioning cleanly to and from combat depending on which team wins the combat. Other elements that are included are an active turn queue (which orders how Battlers act in case of overlap) and BattlerActions which animate/apply effects/are chosen by battlers whenever it is their turn. Consequently, combat now doesn't end on keypress but rather according to which 'team' won the combat.

With regards to implementation most cues were taken from the 2D Secrets course. Several of the commits have a lesson name attached, as laid out on gdquest.com. Of course, this project has translated these to Godot 4.X.

I've axed the formulas class, since I don't see that as being immediately useful. It may work its way back in, if needed.

Of particular note also is the structure of actions. In the 2D Secrets course, there were ActionData, Actions, and then derived ActionData (e.g. AttackActionData) and derived Actions (e.g. AttackAction). This meant that you had to a) duplicate your efforts in maintaining code and b) match the correct data to the correct action type at runtime. Perhaps this wasn't a big issue in practice, but I found this really hard to wrap my head around.

In this iteration of the project, actions are simply a Resource. This has a couple of benefits:

  • First of all, actions can be duplicated and then altered, so it is fairly trivial to add the same action that has different effects to different characters. For example, maybe some enemy is REALLY good at defending. All that needs to be done is have the "Defend" action saved somewhere with some of its exported parameters tweaked for it to be assigned to the defensive enemy.
  • Secondly, there is only one occurrence of a given action, so there is no need to look around the file system for matching Data/Action pairs.

I'd love to have your feedback on this design, since it is a deviation from what was there in the course. All action and battler implementations have been put into the new /battler folder, though their BattlerAnimations co-exist with the assets in the /Assets folder (is there a best-practices issue here? I'm open to moving things around). The main issue I can see is that it's tricky to lay out/test Action animations, but I can't think of a super easy way to set that up anyways since it's all variable depending on where Battlers are positioned on the combat scene.

This PR is ready for review and the feedback/editing cycle.

combat

@NathanLovato
Copy link
Contributor

Thank you very much for your work! I checked and recognized some code patterns, cool! Regarding the two differences:

  1. Removing formulas makes a lot of sense.
  2. Simplifying actions also makes a lot of sense. I don't remember why they were split into two pieces; It was probably to work around serialization issues in Godot 3. The original code was made, maybe with Godot 3.3 or something, and resources got many improvements since then. I'm glad that you could simplify it.

I'm taking note to download and review this thoroughly this week. Do you have a Twitter account? I'll post about this, and I would like to give you a shout out.

@food-please
Copy link
Contributor Author

Hey, thanks! Looking forward to the feedback.

The BattlerActions as Node or Resource have tradeoffs, I'm not sure which I prefer at the moment. I guess that will become more apparent as concrete actions are added. I'm picturing a "projectile spawner" that will allow animating projectiles, impacts, and other VFX, but I'm not sure yet if that's the best option. Hard to tell until we get there.

Do you have a Twitter account?

Pretty sure it's something like @_food_please.

Thanks again and, as always, please don't feel the need to rush through it.

Comment on lines 54 to 71
player_turn_finished.connect(func _on_player_turn_finished() -> void:
if _queued_player_battlers.is_empty():
_is_player_playing = false
else:
_play_turn(_queued_player_battlers.pop_front())
)

for battler: Battler in _battlers:
battler.ready_to_act.connect(func on_battler_ready_to_act() -> void:
if battler.is_player_controlled() and _is_player_playing:
_queued_player_battlers.append(battler)
else:
_play_turn(battler)
)
battler.health_depleted.connect(func on_battler_health_depleted() -> void:
if not _deactivate_if_side_downed(_party_members, false):
_deactivate_if_side_downed(_enemies, true)
)
Copy link
Contributor

Choose a reason for hiding this comment

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

This is the main area where I made changes in this first refactor commit. You can simplify the code and group it by using first class functions. Back in Godot 3, you had to separate the signals and the functions called from the signals. If you needed to pass extra data to the signal callback, you had to bind arguments when connecting the signals.

You can still do that in Godot 4 and it's completely fine, but you can also use first-class functions to create closures and capture the data you need. The thing I like most about this is that now the signal connection and the callback function are not on either end of the script, but they are grouped in one place. This makes it easier to understand the code and see what is connected to what.

@NathanLovato
Copy link
Contributor

NathanLovato commented Jul 26, 2024

I started reviewing and refactoring little bits to prepare for a merge. You may expect quite a few code changes, as I'm going to be moving things around, but most changes will likely be superficial (small cleanup like e.g. renaming vars to be pseudo-private or not). I'll leave comments on the more interesting changes, if any.

@NathanLovato
Copy link
Contributor

@food-please do you want me to merge now or is there more you want to add to this PR?

@NathanLovato
Copy link
Contributor

All good overall! Like the rest of contributions, I think it'd be best to merge now and see if things should be modified later once the demo/project is fully fledged.

@food-please
Copy link
Contributor Author

do you want me to merge now or is there more you want to add to this PR?

I think this is a good point to merge the changes. The next pass will probably handle UI/player input and should be sufficiently different. Unless you see any glaring issues with design or implementation I'm happy to merge what we have now. I'm open to refactoring down the road.

You may expect quite a few code changes...

That's great! I'm not fully happy with some of the paradigms that I had included here and I always benefit from learning from the revisions. If I were to re-approach the BattlerAnim class, I think that I would probably have Battler call its methods directly rather than respond to signals. Perhaps the existing approach will make more sense once the UI changes are in.

@NathanLovato NathanLovato merged commit 9000630 into gdquest-demos:main Jul 27, 2024
@NathanLovato
Copy link
Contributor

Merged! Thank you very much for your contribution and continued participation in this project.

Regarding battler animations, you could expose more functions but also because animations may have any length you will likely always need a few signals in there. It's the kind of thing I wouldn't necessarily work on now because you have to have all the systems in place and an actually animated character to really nail the code.

There are other things like the stats with modifiers that, looking at the code now, I would like to either simplify (remove code or make it more specific) or solidify (type more strongly and keep track of modifiers more easily). But before that, you want to have the full demo to make the most informed decisions possible as to what makes sense to keep in the final project.

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.

2 participants