oskarrough / slaytheweb

Slay the Web is a singleplayer, deck builder, roguelike card crawl game for the web based on Slay the Spire
https://slaytheweb.cards
GNU Affero General Public License v3.0
199 stars 40 forks source link

Rewards #46

Closed oskarrough closed 4 years ago

oskarrough commented 4 years ago

It's first a proper dungeon crawler once we can get rewards and improve our deck. After clearing a room, we could let the player choose one or more rewards.

oskarrough commented 4 years ago

It should be up to the UI to create this. So what actions do we need?

oskarrough commented 4 years ago

To start with, we could create a list of "rewards" and when you clear a room, we offer 3 random rewards no matter which room you are in. Later it would be fun if the rewards would get better the further down the dungeon you go.

GregVes commented 4 years ago

Salut!

I think here, I could enqueue (and write) a "addRewards" action.

Does it make sense?

oskarrough commented 4 years ago

Difficult question, trying to think. A little more background:

First, we know when a room has been won like this. It basically checks whether all monsters in the current room are dead. If so, we show an overlay. Inside this overlay we show the button to go to the next room.

Here I imagine showing three rewards that you can choose from as well. Then you'd be able to pick a reward and THEN goToNextRoom()?

https://github.com/oskarrough/slaytheweb/blob/82f61ae9d7a4b22124a5445f3c32a856a5f8b336/public/ui/app.js#L155-L169

The overlay could be updated to something like this

You have defeated all monsters in the room. Congratulations!

Choose a reward:

- Add a card (random card from game/cards.js?) (not implemented yet)
- Remove a card from your deck (not implemented yet)
- Upgrade a card (not implemented yet)
oskarrough commented 4 years ago

I wouldn't think too much about it yet. Let's just try something!

But we need to be able to…

GregVes commented 4 years ago

OK! A quick question example on the data flow, especially the one leading to win/loose game.

  1. In the , you configure the state object
  2. Forwards "state.player" object to a component
  3. Now say, that Player is life is really low and only one attack can kill him. So hitting button that triggers endTurn() action...
  4. ... triggers another takeMonsterTurn() action. If Intent causes damage to player, we dispatch removeHealth() action that substracts health to "player.currentHealth"
  5. In , this edit on "state.player" object triggers re-rendering as "isDead" variable has now changed (it is < 1), which shows the overlay "Try again?".

Am I right?

oskarrough commented 4 years ago
  1. What configures the state object?
  2. The App component creates the new game state and sets it as this.state, yes.
  3. Yes
  4. Yes
  5. In what? But yes!

I'm not sure about the current design where some actions call other actions. Like you say, endTurn() also calls takeMonsterTurn(). This means they are not separate actions to the action manager/queue, which means it's also one "step" when you undo. It also means it's impossible for the UI to react between endTurn and monster turn. Maybe it's ok for now. Just some thoughts.

GregVes commented 4 years ago
  1. In the < App /> component

An example of state management with Redux. For the sake of the simplicity (as they say in tutorials haha), I just focus on "player" object state. We do not have to do it for this project as I think you already wrote a lot of actions logic, but it is soooooo powerful...once you are done messing around with the setup.

Your store file where all your state are

import { createStore } from "redux"
import playerReducer from "./reducers/player" // does the job of operating on state

const store = createStore(playerReducer); // in a real-world, you pass a "rootReducer" that combines several reducers (eg. player, drawPile, deck etc...)

export default store; // for the <App/> to use it

Your user reducer file that handles operations on state

const initialState = {} // empty player, could be with predefined states

export default userReducer = (state=initialState, action) => {
    switch(action.type) {
        case "DECREMENT_HEALTH:
            return {
                ...state,
                currentHealth: currentHealth - action.payload 
           }
    }
}

Your user action file that describes operations to do on state by your reducer

export const removeHealth = (amount) => {
     return {
        type: "DECREMENT_HEALTH",
        payload: amount 
    }
}
... other actions

Your app

import { Provider } from 'react-redux';
import store from "../store/index"

const App = () => {
   <Provider store={store}>
     <MyComponent/>
   </Provider>
}

Component that changes player state

import { useDispatch, useSelector } from "react-redux";
import { removeHeal/ dispatch an action to store without passing around state.th } from "../store/actions"

const dispatch = useDispatch(); // our interface between component and state

export default HealthRemover = () => {

   const handleRemoveHealth = () => {
      dispatch(removeHealth(10)); // no need to pass the state around anymore!
   }

  return (
    <p>Click to reduce player health level</p>
    <button onClick={handleRemoveHealth}>Click me</button>
  ) 
}

Component that reacts to changes in currentHealth property in state

export default PlayerHealthBar = () => {
   let [health] = useSelector(state => state.player.currentHealth) // I subscribe to change in state

  return (
    <p>Health level is {health}</p>
  ) 
}
GregVes commented 4 years ago

That being said, now writing test and logic for rewards!

GregVes commented 4 years ago

Quick question on rewards. Should they be one of the cards?

In this case, if player chooses an "attack 6" reward, (one of the) monster(s) in the next round would start with less a damage of 6.

GregVes commented 4 years ago

Further thoughts: -remove next monster's health of 5 -start next round with a defense of 5 -start next round with 6 cards instead of 5

oskarrough commented 4 years ago

Thanks for the redux example! I created a new issue so we can discuss this https://github.com/oskarrough/slaytheweb/issues/68

Cool ideas for rewards. I think, to start with, we could say rewards are always three cards and you can choose to add one of them to your deck? We could choose three random cards from game/cards.js for instance.

The starter deck currently has some cool cards in it like Bash, Flourish and Thunderclap but I don't think they should be part of the starter deck. Let's add them as rewards instead so your deck gets stronger the more you play.

oskarrough commented 4 years ago

I also like "Remove a card from your deck" as an reward. Removing a card can sometimes be more powerful than adding a new one. Once the structure is in place, we can always play around with the rewards and see what feels good :)

oskarrough commented 4 years ago

Closed by #69