Feedback for Combat log for Rogue like

I’m experimenting with React to create a combat log for the Rogue like or just about any turn based RPG. Any feedback experienced devs can offer, such as how to add colour text highlighting and make it more compact would be awesome.

My goal with this would be to show player stats that can be updated and animated during combat rounds.

Interesting idea. :smile:

React elements accepts a style prop that can be used for styling:

const styles = {
  message: {
    backgroundColor: crimson;
  }
}

// ...

<div className="damage-message" style={styles.message}>
  Attacks with greatsword deals 10 weapon dmg to Abnur Tharn
</div>

Alternatively, you could use something like styled-components for styling your components. With styled-components you can style your components with the CSS syntax (you can also use SCSS-like syntax to do event-trigger style changes):

import styled from 'styled-components';

const DamageMessage = styled.div`
  background: crimson;

  &:hover {
    background: pink;
  }
`;

<MessageDamage className="damage-message">
  Attacks with greatsword deals 10 weapon dmg to Abnur Tharn
</MessageDamage>

The example above is just a simple example—you can pass props into the React Component created by styled components and have it takes on different styles depending on the props passed into it, too (in that case you don’t have to create a different component for different types of messages).

I haven’t looked at your code too closely, and the example works well, but, if I’m not mistaken, there is an error inside your doCombatLog() function, where you are directly mutating the component’s state instead of using setState():

  doCombatLog(seconds) {
    let players = this.state.players;
    let items = this.state.messages;

    // ...

    switch(seconds) {
      // items is a reference to this.state.messages
      // so items.push() is directly mutating the component's state
    }
  }

I think it may be a typo since you do have this.items; alternatively, you can consider using concat() or slice(), as both of them returns a new array.

Good luck! :smile:

Thanks for your great feedback! What kind of method do you have in mind when using concat()?

Thanks to your comments and a bit of messing around with Lists and keys I got styled damage text working on an item level using IDs to style them. The visual feedback looks great. Not very robust though.

Yup I set a reference to messages state and return the temporary array because if I just called setState on the messages directly in render() it will wipe the previous combat text entry every tick.

No worries! I’m actually interested in making games with React and have been experimenting a little, so this is interesting to me, too! :smile:

Both concat() and slice() returns a shallow copy of the reference array, meaning that you won’t end up mutating the state directly if you do use it on a non-nested array:

console.log(this.state.message); // ['Nyanpasu']


// concat() example
const concatItems = this.state.messages.concat(['Pikachu']);

console.log(concatItems); // ['Nyanpasu', 'Pikachu']
console.log(this.state.message); // ['Nyanpasu']


// slice() example
const slicedItems = this.state.message.slice(0);

slicedItems.push('Pikachu');

console.log(slicedItems); // ['Nyanpasu', 'Pikachu']
console.log(this.state.message); // ['Nyanpasu']

I’m pretty sure that you are not meant to call this.setState() inside render()—particularly if your render() method depends on a state that you are setting.

Another way to think of this is that render() is meant to respond to changes in states and props, if you are setting state inside render() then it will trigger another render() which could trigger another setState() which could trigger another render()… etc. That’s an exaggerated example which will crash your app, but I’m sure you get the idea!

In any case, if you find yourself trying to use setState() inside render() it’s likely that you have something anti-pattern happening; if you just can’t seem to rewrite it, just ask! :smile:

Good idea using concat in this way. I guess this means I can decouple the ref array without the repetition of switch case statements to track the messages by time elapsed.

I’m not using setState in render() I knew that much before I started on this project. Instead I’m calling setState within the tick() function.

Pretty cool I’ve simplified it even further with an actions array which pre-defines all the actions to take place and then concats the current action passing in seconds as the index.

doCombatLog(seconds) {
   let players = this.state.players;
   let res = players.filter(function(pc) {
      // return pc.classname === "sorcerer";
      return pc.classname === "dragonknight";
   });
   let pc = res[0];
   let targets = players.filter(function(target) {
      return target.classname === "sorcerer";
   });
   let npc = targets[0];

   const styles = {
      message: {
         color: "#fff"
      },
      hit: {
         color: "crimson"
      },
      miss: {
         color: "yellow"
      }
   }
   const actions = [
      { text:this.getFullName(pc), style:styles.message },
      { text:this.doWeaponDamage(pc), style:styles.hit },
      { text:this.missTarget(npc), style:styles.miss },
      { text:this.totalDamageDone(pc), style:styles.message },
      { text:this.getFullName(npc), style:styles.message },
      { text:this.enemyAttacks(pc, npc), style:styles.hit },
      { text:'End of log', style:styles.message }
   ];
   const concatItems = this.state.messages.concat(actions[seconds]);
   console.log(concatItems);
   return concatItems;
}

I’ve pushed some new changes to the app adding weapon damage with str bonus as well as heal effects for the player.