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 between = (from, to, num) => num < from ? from : num > to ? to : num
const randBetween = (min, max) => min + Math.ceil(Math.random() * (max - min))
const PLAYER_DEFAULTS = {
health: 100,
skill: { attack: 0.9, special: 0.25, heal: 1 },
rage: 0,
}
const MONSTER_DEFAULTS = {
health: 100,
skill: { attack: 0.9, special: 0.35, heal: 1 },
uberHeal: !(Math.random().toFixed(1)*10 % 2), // random true/false
rage: 0.2,
}
const ACTIONS = {
ATTACK: { MIN: 5, MAX: 12, POW: 0.7, CHANCE: 1 },
SPECIAL: { MIN: 20, MAX: 30, POW: 0.2, CHANCE: 0.2 },
HEAL: { MIN: 5, MAX: 15, POW: 0.8, CHANCE: 0.1 },
}
new Vue({
el: '#app',
data: {
ingame: false,
player: PLAYER_DEFAULTS,
monster: MONSTER_DEFAULTS,
actions: [],
},
methods: {
startGame() {
this.resetGame()
this.ingame = true
},
resetGame() {
this.actions = []
this.player = Object.assign({}, PLAYER_DEFAULTS)
this.monster = Object.assign({}, MONSTER_DEFAULTS)
},
endGame() {
this.ingame = false
},
// gameplay
playerRound(action) {
const [success, ammount] = this.tryAction(action, this.player)
this.actions.push({ player: true, action, success, ammount })
if (this.monster.health <= 0) {
this.actions.push({ player: true, success: true, action: 'win' })
this.endGame()
}
if (this.ingame) this.monsterRound(action, success, ammount)
},
tryAction(action, character) {
const skill = action === 'giveup'
? 1
: character.skill[action] + ACTIONS[action.toUpperCase()].POW
if (Math.random() <= skill + (action === 'special' ? character.rage : 0)) return [true, this[action](character)]
return [false, null]
},
monsterRound(playerAction,...