Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
{
"extends": "hrundel/browser"
"extends": "hrundel/browser",
"parserOptions": {
"sourceType": "module"
}
}
56 changes: 56 additions & 0 deletions drawer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/* global mina:true*/
import snap from 'snapsvg';

class Drawer {

drawHero() {
this.layout = snap('.hero__picture');
this.head = this.layout.circle(60, 50, 50).attr({ fill: '#cca0a0' });
this.eyes = this.layout.group(
this.layout.circle(80, 30, 10),
this.layout.circle(40, 30, 10)
);
this.eyes.attr({ fill: '#CF4D6F' });
this.mouth = this.layout.path('M20,70C40,90,80,90,100,70').attr({ fill: '#C97282' });
this.nose = this.layout.path('').attr({ fill: '#76818E' });
this.nose = this.layout.group(
this.layout.circle(60, 50, 12).attr({ fill: '#ecc5c4' }),
this.layout.circle(54, 50, 3).attr({ fill: '#b85f69' }),
this.layout.circle(66, 50, 3).attr({ fill: '#b85f69' }),
);
this.paws = this.layout.group(
this.layout.circle(10, 55, 10),
this.layout.circle(110, 55, 10),
this.layout.circle(40, 100, 10),
this.layout.circle(80, 100, 10)
);
this.paws.attr({ fill: '#ecc5c4' });
}

animateDeath() {
this.stopSpeak();
this.eyes.attr({ fill: '#000' });
this.mouth.animate({ d: 'M20,80C40,60,80,60,100,80' }, 1000);
}

animateWake() {
this.eyes.attr({ fill: 'none' }).animate({ fill: '#CF4D6F' }, 1500, mina.easeinout);
this.paws.animate({ transform: 't0, 2' }, 500, mina.easein, () => {
this.paws.animate({ transform: 't0, -2' }, 500, mina.easein);
});
}

stopSpeak() {
if (this.ears) {
this.ears.attr({ opacity: 0 });
}
}

startSpeak() {
this.ears = this.layout.group(this.layout.polygon([14, 30, 40, 4, 0, 0]),
this.layout.polygon([106, 30, 80, 4, 110, 0]));
this.ears.attr({ fill: '#b85f69' }, 10000, mina.easein);
}
}

export default new Drawer();
132 changes: 132 additions & 0 deletions hrunogochi.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
'use srtict';

// eslint-disable-next-line no-empty-function
const bounded = (value) => Math.max(0, Math.min(100, value));
const tickInterval = 1750;

const phrases = [
'хрю-хрю-хрю',
'давай играть',
'ох-ох'
];

export default class Hrunogochi {
constructor(state) {
this.init(state);
}

init(state) {
this.state = state || this.getState();
this.eating = false;
this.sleeping = false;
this.speaking = false;
}

getState() {
return {
'bellyful': 100,
'energy': 100,
'mood': 100
};
}

isDead() {
const { bellyful, energy, mood } = this;

return [bellyful, energy, mood].filter(value => value === 0).length > 1;
}

get energy() {
return this.state.energy;
}

set energy(value) {
this.state.energy = bounded(value);
}

get bellyful() {
return this.state.bellyful;
}

set bellyful(value) {
this.state.bellyful = bounded(value);
}

get mood() {
return this.state.mood;
}

set mood(value) {
this.state.mood = bounded(value);
}

setState({ bellyful, energy, mood }) {
this.bellyful = bellyful;
this.energy = energy;
this.mood = mood;

this.onUpdate();
}

updateState(newState) {
const mergedState = Object.assign({}, this.state, newState);
this.setState(mergedState);
}

saveState(saveAction) {
saveAction(this.state);
}

start() {
// eslint-disable-next-line max-statements
const tick = () => {
if (this.isDead()) {
this.onDeath();

return this.stop();
}

if (Math.random() > 0.8) {
const text = phrases[Math.floor(Math.random() * phrases.length)];
this.onSpeak(text);
}

let { bellyful, energy, mood } = this;

if (this.speaking) {
mood += 4;
} else if (this.sleeping) {
energy += 4;
} else if (this.eating) {
bellyful += 4;
}

bellyful--;
energy--;
mood--;

this.updateState({
bellyful,
energy,
mood
});

this.tickId = setTimeout(tick, tickInterval);
};

tick();
this.onStart();
}

stop() {
clearTimeout(this.tickId);
}

reset() {
this.onReset();
this.stop();
this.setState(this.getState());
this.start();
}

}
58 changes: 58 additions & 0 deletions index.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
html,
body
{
margin: 0;
padding: 0;
height: 100%;
}

body
{
display: flex;
}

header
{
text-align: center;
margin-bottom: 1em;
}

.game
{
width: 35%;
margin: auto;
border: 2px black solid;
padding: 2em;
}

.state,
.controls
{
display: flex;
justify-content: space-between;
}

.state__item,
.controls__item
{
text-align: center;
}

.controls__item
{
padding: 5px 10px;
font-size: 15px;
}

.hero
{
margin: 10px;
display: flex;
flex-direction: column;
align-items: center;
}

.hero__speech
{
margin: 10px;
}
29 changes: 29 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,36 @@
<meta charset="utf-8">
<title>Хрюногочи</title>
<link rel="stylesheet" href="./index.css">
<script defer type="text/javascript" src="./index.js"></script>
</head>
<body>

<div class="game">
<header>
HRUNOGOCHI
</header>
<div class="state">
<div class="state__item bellyful">
<span class="state__item__name">Cытость</span>: <span class="state__item__value">0</span>%
</div>
<div class="state__item energy">
<span class="state__item__name">Энергия</span>: <span class="state__item__value">0</span>%
</div>
<div class="state__item mood">
<span class="state__item__name">Настроение</span>: <span class="state__item__value">0</span>%
</div>
</div>

<div class="controls">
<button class="controls__item feed">Накормить</button>
<button class="controls__item reset">Начать заново</button>
<input class="controls__item volume" type="range">
</div>

<div class="hero">
<svg class="hero__picture" width="120" height="110"></svg>
<div class="hero__speech">Готов поговорить с тобой!</div>
</div>
</div>
</body>
</html>
Loading