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

[Feature] Add optional win conditions #17

Closed
cpojer opened this issue May 14, 2024 · 11 comments · Fixed by #34
Closed

[Feature] Add optional win conditions #17

cpojer opened this issue May 14, 2024 · 11 comments · Fixed by #34

Comments

@cpojer
Copy link
Contributor

cpojer commented May 14, 2024

It would be awesome to add a toggle for win conditions that makes them "optional". Together with win conditions being secret, they could add for hidden rewards within a game that do not end the game.

Code & Steps

This change likely requires adding an optional field to each win condition. The structure of encoded conditions cannot be changed, so we can only add fields to them that are optional. Sorry, there is no codegen for encoded win condition, but it's all local to WinConditions.tsx. We'll also want to add a completed field that is a Set of PlayerIDs that have completed that win condition. Similar to hidden/secret win conditions, the UI should show a checkbox to mark them as optional. I would suggest that the default win condition (defeat all units or capture HQ) cannot be made optional. Win condition validation needs to be updated to check for that, and validateMap (or maybe validateWinCondition or similar) needs to reset the completed state.

You can find the win condition panel in the map editor when pressing c, or by tapping "Conditions" in the bottom drawer.

Funding

  • We're using Polar.sh to distribute funds.
  • You receive the reward once the issue is completed & confirmed by Nakazawa Tech.
Fund with Polar
@cpojer
Copy link
Contributor Author

cpojer commented May 22, 2024

Thank you. The correct link is checkWinCondition.tsx, I edited the issue description. Apologies for the inconvenience.

@sookmax
Copy link
Contributor

sookmax commented May 24, 2024

@cpojer this might be a dumb question, but what should happen (on the screen) when a user achieves one of the optional win conditions?

It seems like an optional win condition shouldn't end the game.. so should it trigger some kind of animation, similar to 'Secret discovered banner'? And (well sorry this is a little more specific, but I'm kinda lost 😭) how should I prevent an optional win condition from ending the game? should I perhaps do something in onGameEnd() or should it never trigger 'GameEnd' action in the first place?

@cpojer
Copy link
Contributor Author

cpojer commented May 25, 2024

I agree it should be similar to "secret discovered" on the screen. I'm thinking we'd add a new ActionResponse called something like OptionalWinConditionActionResponse (See the Actions guide) that contains the condition/conditionId so it can be used to show the banner.

onGameEnd should definitely not be triggered. I would probably suggest to extend checkGameOverConditions and if an optional condition is hit, return the OptionalWinConditionActionResponse. I assume I'll have to rename some things later to make it fit with this new model, however it should work since that function returns a GameState.

@sookmax
Copy link
Contributor

sookmax commented May 25, 2024

@cpojer Hey thanks for a detailed answer! It was immensely helpful 😄

I would like to ask another question though if you don't mind.

So this completed state—that tracks players that have completed the optional win condition so far—cannot be a field in a WinCondition, right? since a WinCondition doesn't change once a game starts, but the completed state would, as the game progresses.

Maybe having it in MapData would make more sense? so that we can update completed in checkGameOverConditions() like:

map = map.copy({
  completed: [...map.completed, pickWinningPlayer(...)]
})

@cpojer
Copy link
Contributor Author

cpojer commented May 25, 2024

We can certainly edit the win conditions to add the player ids! I assume the action response will have a playerID, conditionId, and the condition, so in that case we can add a few lines in applyActionResponse that adds the player id to the completed field on the win condition.

@sookmax
Copy link
Contributor

sookmax commented May 25, 2024

Sorry, I may be misinterpreting your intent here, but it looks like applyActionRespose() simply calculates and returns a new map, depending on actionResponse.type? And what you're suggesting is to modify the condition contained in actionResponse.condition in place and return the same map as is? so something like:

case 'OptionalWin': {
  const { condition, playerId } = actionResponse;
  condition.completed?.add(playerId);
  return map;
}

If so, I should use Set<PlayerID> instead of PlayerIDSet for the type of condition.completed since add() is going to be used?

@cpojer
Copy link
Contributor Author

cpojer commented May 25, 2024

We should never mutate the map state directly, see: MapData guide. applyActionResponse always takes a map data object and returns a new one. In our case since we are editing win conditions it's a bit more finicky. Something like this:

const { condition, conditionId, playerId } = actionResponse;
const winConditions = Array.from(map.config.winConditions);
winConditions[conditionId] = {...condition, completed: new Set([...condition.completed, playerId])};
map.copy({
  config: map.config.copy({
    winConditions
  })
})

@sookmax
Copy link
Contributor

sookmax commented May 25, 2024

Ah ha! thank you so much for that code snippet! It clarifies things a lot 👍

@sookmax
Copy link
Contributor

sookmax commented May 26, 2024

@cpojer Hey, I was reading the issue description again, and was wondering about this part:

and validateMap (or maybe validateWinCondition or similar) needs to reset the completed state.

I'm not very sure when and why we should reset the completed state? Maybe when the game ends eventually?

I'd greatly appreciate it if you could elaborate on this part a little more 🥹

@cpojer
Copy link
Contributor Author

cpojer commented May 26, 2024

Sorry for being unclear. We don't need to reset the state at the end, but the way Athena Crisis works is when you save a map, it resets everything to the initial state so it's ready for games to be played. validateMap is what takes care of that. We call dropInactivePlayersFromWinConditions and I would suggest renaming that function to resetWinConditions and setting the completed field to an empty set + keep doing what the function is already doing.

Secondly, validateWinCondition should basically just verify that everything is set to the initial state. So if completed is not empty, that function should return false.

Let me know if you have any more questions, happy to answer them.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants