JSFiddle - React, Tailwind, and code Playground
by tlgreg
HTML
<link rel="stylesheet" href="https://cdn.foundation5.zurb.com/foundation.css">
<div id="app">
<section class="row">
<div class="small-6 columns">
<h1 class="text-center">YOU</h1>
<div class="healthbar">
<div class="healthbar text-center" style="margin: 0; color: white;" :style="{ width: `${player.health}%`, backgroundColor: healthColor(player.health) }">
<div>{{ player.health }}</div>
</div>
</div>
</div>
<div class="small-6 columns">
<h1 class="text-center">MONSTER</h1>
<div class="healthbar">
<div class="healthbar text-center" style="margin: 0; color: white;" :style="{ width: `${monster.health}%`, backgroundColor: healthColor(monster.health) }">
<div>{{ monster.health }}</div>
</div>
</div>
</div>
</section>
<section class="row controls" v-if="!ingame">
<div class="small-12 columns">
<button id="start-game" @click="startGame">START NEW GAME</button>
</div>
</section>
<section class="row controls" v-if="ingame">
<div class="small-12 columns">
<button id="attack" @click="playerRound('attack')">ATTACK</button>
<button id="special-attack" @click="playerRound('special')">SPECIAL ATTACK</button>
<button id="heal" @click="playerRound('heal')">HEAL</button>
<button id="give-up" @click="playerRound('giveup')">GIVE UP</button>
</div>
</section>
<section class="row log" v-if="actions.length > 0">
<div class="small-12 columns">
<ul>
<li v-for="log in actions" :class="{ 'player-turn': log.player, 'monster-turn': !log.player }">
<span class="action">{{ actionIcon(log.action, log.success, log.player) }}</span> {{ log.player ? 'YOU' : 'The MONSTER' }} {{ actionText(log.action, log.success, log.ammount) }}
...
SCSS
.text-center {
text-align: center;
}
.healthbar {
width: 80%;
min-width: 1em;
height: 40px;
background-color: #eee;
margin: auto;
transition: width 500ms;
&.text-center > div {
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
}
.controls, .log {
margin-top: 30px;
text-align: center;
padding: 10px;
border: 1px solid #ccc;
box-shadow: 0px 3px 6px #ccc;
}
.turn {
margin-top: 20px;
margin-bottom: 20px;
font-weight: bold;
font-size: 22px;
}
.log ul {
list-style: none;
font-weight: bold;
text-transform: uppercase;
}
.log ul li {
margin: 5px;
}
.log ul .player-turn {
color: blue;
background-color: #e4e8ff;
}
.log ul .monster-turn {
color: red;
background-color: #ffc0c1;
}
button {
font-size: 20px;
background-color: #eee;
padding: 12px;
box-shadow: 0 1px 1px black;
margin: 10px;
}
#start-game {
background-color: #aaffb0;
}
#start-game:hover {
background-color: #76ff7e;
}
#attack {
background-color: #ff7367;
}
#attack:hover {
background-color: #ff3f43;
}
#special-attack {
background-color: #ffaf4f;
}
#special-attack:hover {
background-color: #ff9a2b;
}
#heal {
background-color: #aaffb0;
}
#heal:hover {
background-color: #76ff7e;
}
#give-up {
background-color: #ffffff;
}
#give-up:hover {
background-color: #c7c7c7;
}
button {
user-select: none;
}
Babel + JSX
// easy monster, less random wins if alternating between "attack" and "special attack"
// spamming only "attack" or only "special attack" mostly means a loss
// with alternating healing is rarely needed
const logger = { log: [] }
class Character {
constructor() {
this.health = this.maxHealth = 100
this.mana = this.maxMana = 10
this.attr = {
attack: 0.5,
defense: 0.5,
strength: 10,
skill: 3,
magic: 10,
}
this.magic = {
heal: 2,
}
this.rng = {
attack: () => this.rand(0, 1),
damage: () => this.rand(0, 10),
special: () => this.rand(0, 0.5),
heal: () => this.rand(5, 10),
}
}
toString() {
return 'Character'
}
between(min, max, num) {
return num < min ? min : num > max ? max : num
}
rand(min, max) {
return min + Math.ceil(Math.random() * (max - min))
}
attack(target) {
const success = this.between(0, 1, this.attr.attack + this.rng.attack()) > target.defense()
if (success) {
logger.log.push({ character: this, success: true, message: `Attacked the ${target}!` })
target.damage(this.attr.strength + this.rng.damage())
} else {
logger.log.push({ character: this, success: false, message: `Missed an attack!` })
}
}
special(target) {
const success = this.between(0, 1, this.attr.attack + this.rng.special()) > target.defense()
if (success) {
logger.log.push({ character: this, success: true, message: 'Done a special attack!' })
target.damage(this.attr.strength * (this.skill / 2) + this.rng.damage() * this.skill)
} else {
logger.log.push({ character: this, success: false, message: 'Failed with a special attack!' })
}
}
heal() {
const cost = this.magic.heal
const success = this.mana >= cost
if (success) {
this.mana = this.between(0, this.maxMana, this.mana - cost)
const pow = this.attr.magic + this.rng.heal()
const dmg = this.maxHealth - this.health
const healing = pow...