Initial Commit

Signed-off-by: Avior <florian.bouillon@delta-wings.net>
This commit is contained in:
2021-03-14 02:07:52 +01:00
commit f47ab40ffd
83 changed files with 8420 additions and 0 deletions

4
src/client/styl/stylus.d.ts vendored Normal file
View File

@@ -0,0 +1,4 @@
declare module '*.styl' {
const content: any
export = content
}

13
src/pages/_app.tsx Normal file
View File

@@ -0,0 +1,13 @@
import React from 'react'
import App from 'next/app'
import '@dzeio/components/style.css'
export default class CApp extends App {
public render() {
const { Component, pageProps } = this.props
return(<Component {...pageProps} />)
}
}

19
src/pages/_document.tsx Normal file
View File

@@ -0,0 +1,19 @@
import React from 'react'
import Document, { Html, Head, Main, NextScript } from 'next/document'
class MyDocument extends Document {
public render() {
return (
<Html lang="en">
<Head />
<body>
<Main />
<NextScript />
</body>
</Html>
)
}
}
export default MyDocument

13
src/pages/index.tsx Normal file
View File

@@ -0,0 +1,13 @@
import { Link, Text } from '@dzeio/components'
import React from 'react'
export default class Index extends React.Component {
public render = () => (
<main>
<Text>
<Link href="/pokemon-shuffle">Pokémon Shuffle</Link>
</Text>
</main>
)
}

View File

@@ -0,0 +1,292 @@
import { Button, Table, Text, Util } from '@dzeio/components'
import React from 'react'
import css from './pokemon-shuffle.styl'
interface Cell {
id: number
horizontalCombo?: true
verticalCombo?: true
justSpawned?: true
isFalling?: true
}
interface States {
items: Array<Array<Cell | undefined>>
loading?: true
movingItem?: {x: number, y: number, cell: Cell}
points: number
turn: number
combo: number
comboMax: number
cursorPos: {x: number, y: number}
}
const ITEM_COUNT = 4
const BOARD_SIZE = 6
let n = BOARD_SIZE
export default class PokemonShuffle extends React.Component<unknown, States> {
public state: States = {
items: [[]],
points: 0,
turn: 0,
combo: 0,
comboMax: 0,
cursorPos: {x: 0, y: 0}
}
public render = () => (
<main>
<ul>
<li><Text>Tour: {this.state.turn}</Text></li>
<li><Text>Combo: {this.state.combo}, Max: {this.state.comboMax}</Text></li>
<li><Text>Points: {this.state.points}</Text></li>
</ul>
<Table >
<tbody className={`${css.table} ${this.state.loading ? css.loading : ''}`}>
{this.state.items.map((row, y) => (
<tr key={y}>
{row.map((cell, x) => (
<td
key={cell?.isFalling ? n++ : x}
onClick={this.onCellClick(x, y)}
className={css.cellParent}
>
{cell && (
<Text className={Util.buildClassName(
css[`icon-${cell.id}`],
css.cell,
[css.isFalling, cell.isFalling],
[css.justSpawned, cell.justSpawned],
[css.explode, cell.horizontalCombo || cell.verticalCombo]
)}>
<div></div>
</Text>
)}
</td>
))}
</tr>
))}
</tbody>
</Table>
<Button onClick={() => this.calculate()}>Calculate!</Button>
<Button onClick={() => this.start()}>Start!</Button>
{/* <Input block type="textarea" value={JSON.stringify(this.state.items)}/> */}
{this.state.movingItem && (
<div style={{
position: 'absolute',
left: this.state.cursorPos.x,
top: this.state.cursorPos.y,
transform: 'translate(-50%, -50%)',
pointerEvents: 'none'
}}>
<Text className={Util.buildClassName(css[`icon-${this.state.movingItem.cell?.id}`], css.cell)}>
<div></div>
</Text>
</div>
)}
<Text>
TODO list:
</Text>
<ul>
<li><Text>Lancement Initial sans combo possible</Text></li>
<li><Text>Meilleurs Animation de destruction</Text></li>
<li><Text>Annuler le mouvement si rien n&apos;est claim</Text></li>
</ul>
</main>
)
private mouveMove = (ev: MouseEvent) => {
this.setState({cursorPos: {
x: ev.pageX,
y: ev.pageY
}})
}
private start() {
if (this.state.loading) {return}
this.setState({
loading: true,
items: Array
.from(Array(BOARD_SIZE))
.map(
() => Array.from(Array(BOARD_SIZE))
.map(() => ({id: random(0, ITEM_COUNT)}))
)
}, () => this.calculate())
}
private onCellClick = (x: number, y: number) => async () => {
// console.log(x, y)
if (this.state.loading) {
return window.alert('Cant play while Calculating')
}
if (!this.state.movingItem) {
const cell = this.state.items[y][x]
if (!cell) {
return window.alert('Cant move nothing')
}
document.addEventListener('mousemove', this.mouveMove)
this.setState({movingItem: {x,y,cell}})
this.state.items[y][x] = undefined
return
} else {
document.removeEventListener('mousemove', this.mouveMove)
const items = this.state.items
const temp = items[y][x]
items[y][x] = this.state.movingItem.cell
items[this.state.movingItem.y][this.state.movingItem.x] = temp
this.setState({
movingItem: undefined,
loading: true,
items
}, () => this.calculate())
}
}
private asyncSetState = (states: Partial<States>) => new Promise<void>(
(res) => this.setState(states as States, () => res())
)
private async calculate() {
const items = this.state.items.map((r) => r.map((c) => {
if (!c) {
return c
}
c.horizontalCombo = undefined
c.verticalCombo = undefined
return c
}))
let newPoints = 0
let checkupCount = 0
// Checkup horizontal
for (let y = 0; y < items.length; y++) {
const row = items[y]
for (let x = 0; x < row.length; x++) {
const cell = row[x]
if (!cell || cell.horizontalCombo) {continue}
const id = cell.id
let sameCount = 0
while((x + ++sameCount) < items.length) {
console.log(y + sameCount, x)
const tmp = row[x + sameCount]
if (!tmp || tmp.id !== id) {break}
}
if (sameCount >= 3) {
checkupCount += 1
for (let i = x; i < (x + sameCount); i++) {
const tmp = items[y][i]
if (!tmp) {continue}
tmp.horizontalCombo = true
newPoints++
}
}
}
}
// Check vertical
for (let y = 0; y < items.length; y++) {
const row = items[y]
for (let x = 0; x < row.length; x++) {
const cell = row[x]
if (!cell || cell.verticalCombo) {continue}
const id = cell.id
let sameCount = 0
while((y + ++sameCount) < items.length) {
// console.log(y + sameCount, x)
const tmp = items[y + sameCount][x]
if (!tmp || tmp.id !== id) {break}
}
// if ((y + sameCount) > items.length) {
// sameCount++
// }
if (sameCount >= 3) {
checkupCount += 1
for (let i = y; i < (y + sameCount); i++) {
const tmp = items[i][x]
if (!tmp) {continue}
tmp.verticalCombo = true
newPoints++
}
// console.log(x, y)
}
}
}
if (checkupCount) {
await this.asyncSetState({
items,
points: this.state.points + newPoints,
combo: this.state.combo+checkupCount,
comboMax: Math.max(this.state.comboMax, this.state.combo+checkupCount)
})
await new Promise((res) => setTimeout(res, 500))
}
// return
// Clear items
// eslint-disable-next-line @typescript-eslint/prefer-for-of
for (let y = 0; y < items.length; y++) {
const row = items[y]
for (let x = 0; x < row.length; x++) {
const cell = row[x]
if (!cell || (!cell.horizontalCombo && !cell.verticalCombo)) {continue}
items[y][x] = undefined
}
}
let itemHasFallen = false
let needNewTurn = false
do {
// Make items fall
itemHasFallen = false
for (let y = (items.length - 1); y >= 0; y--) {
const row = items[y]
for (let x = 0; x < row.length; x++) {
const cell = row[x]
if (cell) {
cell.justSpawned = undefined
cell.isFalling = undefined
}
if (cell && y+1 < row.length && !items[y+1][x]) {
cell.isFalling = true
needNewTurn = true
itemHasFallen = true
items[y+1][x] = cell
items[y][x] = undefined
}
}
}
// Fill to top lane
for (let x = 0; x < items[0].length; x++) {
const cell = items[0][x]
if (!cell) {
itemHasFallen = true
items[0][x] = {id: random(0, ITEM_COUNT), justSpawned: true}
}
}
if (itemHasFallen) {
await this.asyncSetState({items})
await new Promise((res) => setTimeout(res, 300))
}
} while (itemHasFallen)
// If an item has fallen re calculate
if (needNewTurn) {
this.setState({items}, () => this.calculate())
return
}
this.setState({items, loading: undefined, turn: this.state.turn+1, combo: 0})
}
}
function random(min = 0, max = 100) {
return Math.floor(Math.random() * (max - min) + min)
}

View File

@@ -0,0 +1,129 @@
$iconSize = 121px
.table
position relative
transition filter .5s ease-in-out
&.loading .cell
filter brightness(0.5)
.cellParent
position relative
width $iconSize
padding 0 !important
height $iconSize
.cell
width $iconSize
transition filter .5s ease-in-out
height $iconSize
background-image url('/assets/pokemon-shuffle/icons.png')
animation idleAnimation ease-in-out 1s
animation-iteration-count infinite
transform-origin 50% 50%
&.icon-0
background-position (1*$iconSize) 0px
&.icon-1
background-position (2*$iconSize) 0px
&.icon-2
background-position (3*$iconSize) 0px
&.icon-3
background-position (4*$iconSize) 0px
&.icon-4
background-position (5*$iconSize) 0px
&.icon-5
background-position (6*$iconSize) 0px
&.icon-6
background-position (7*$iconSize) 0px
&.icon-7
background-position (8*$iconSize) 0px
&.icon-8
background-position (9*$iconSize) 0px
&.icon-9
background-position (10*$iconSize) 0px
&.icon-10
background-position (11*$iconSize) 0px
&.isFalling
position absolute
animation fallingAnimation linear .3s
animation-iteration-count 1
transform-origin 50% 50%
animation-fill-mode forwards
&.explode
animation destroyAnimation ease-in-out .5s
animation-iteration-count 1
transform-origin 50% 50%
animation-fill-mode forwards
&::before
content " "
position absolute
bottom 0
width 100%
height 100%
border white solid 4px
border-radius 100%
animation destroyCircleAnimation ease-in-out .3s
animation-iteration-count 1
transform-origin 50% 50%
animation-fill-mode forwards
&.justSpawned
animation spawnAnimation ease-in-out 0.5s
animation-iteration-count 1
transform-origin 50% 50%
@keyframes idleAnimation
0%
transform rotate(0)
20%
transform rotate(-10deg)
40%
transform rotate(10deg)
60%
transform rotate(-5deg)
80%
transform rotate(5deg)
100%
transform rotate(0)
@keyframes fallingAnimation
from
top -117px // revoir précisément
to
top 3px
@keyframes destroyCircleAnimation
0%
opacity 0
transform scale(0)
100%
opacity 1
transform scale(1.2)
@keyframes destroyAnimation
0%
transform scale(1)
filter brightness(1)
20%
transform scale(1.3)
filter brightness(1.7)
100%
transform scale(0)
filter brightness(1.5)
@keyframes spawnAnimation
from
transform: scale(0)
to
transform: scale(1)

14
src/pages/sitemap.xml.ts Normal file
View File

@@ -0,0 +1,14 @@
import { GetServerSideProps } from 'next'
import Sitemap from 'easy-sitemap'
export default class SitemapXml {}
export const getServerSideProps: GetServerSideProps = async ({res}) => {
const sitemap = new Sitemap('https://games.avior.me', {response: res})
sitemap.addEntry('/')
sitemap.addEntry('/pokemon-shuffle')
sitemap.build()
return {
notFound: true
}
}