Updated code

Signed-off-by: Avior <florian.bouillon@delta-wings.net>
This commit is contained in:
Florian Bouillon 2021-05-23 00:52:34 +02:00
parent f47ab40ffd
commit cbfae26516
Signed by: Florian Bouillon
GPG Key ID: 50BD648F12C86AB6
8 changed files with 2274 additions and 4288 deletions

117
dzeio.next.config.js Normal file
View File

@ -0,0 +1,117 @@
// V1.1.1 - Added futures blurry Placeholder and left optimizedLoading enabled
// Updated to commit from 2021-05-18
// https://github.com/vercel/next.js/commits/canary/packages/next/next-server/server/config-shared.ts
/**
* @type {import("next/dist/next-server/server/config-shared").NextConfig & import("next/dist/next-server/server/config-shared").defaultConfig}
*/
const nextConfig = {
// Experimentals
experimental: {
plugins: true,
profiling: process.env.NODE_ENV === 'developpment',
sprFlushToDisk: true,
workerThreads: true,
pageEnv: true,
optimizeImages: true,
optimizeCss: true,
scrollRestoration: true,
scriptLoader: true,
stats: process.env.NODE_ENV === 'developpment',
externalDir: true,
serialWebpackBuild: true,
conformance: true,
turboMode: true,
eslint: true,
// Bugged
// https://github.com/vercel/next.js/issues/18913
// reactRoot: true,
enableBlurryPlaceholder: true,
disableOptimizedLoading: false,
},
// Non experimental config
// target: 'serverless',
poweredByHeader: false,
trailingSlash: false,
optimizeFonts: true,
reactStrictMode: true,
// Futures
future: {
webpack5: true,
strictPostcssConfiguration: true,
excludeDefaultMomentLocales: true
},
// Headers and rewrites
async headers() {
// CSS no CSP, x-xss-protection
const CSP = {
key: 'Content-Security-Policy',
value:
// default-src is set to self because prefetch-src is not working propelly see: https://bugs.chromium.org/p/chromium/issues/detail?id=801561
"default-src 'self'; " +
"frame-ancestors 'none'; " +
"form-action 'self'; " +
"manifest-src 'self'; " +
"prefetch-src 'self'; " +
"script-src 'self' 'unsafe-inline' 'unsafe-eval' https://stats.dzeio.com; " +
"style-src 'self' 'unsafe-inline'; " +
"img-src data: 'self'; " +
"font-src 'self'; " +
"connect-src 'self' https://stats.dzeio.com; " +
"base-uri 'self';"
}
const XXssProtection = {
key: 'X-XSS-Protection',
value: '1; mode=block'
}
// JS no x-xss-protection
const headers = [{
key: 'X-Frame-Options',
value: 'DENY'
}, {
key: 'X-Content-Type-Options',
value: 'nosniff'
}, {
key: 'Referrer-Policy',
value: 'strict-origin-when-cross-origin'
}, {
key: 'Permissions-Policy',
value: 'geolocation=(), microphone=(), interest-cohort=()'
}, {
key: 'Strict-Transport-Security',
value: 'max-age=63072000; includeSubDomains; preload'
}, {
key: 'X-Download-Options',
value: 'noopen'
}, {
key: 'Expect-CT',
value: 'max-age=86400, enforce'
}]
const excludedExtensions = ['js', 'css', 'json', 'ico', 'png']
.map((ext) => `(?!\\.${ext}$)`).join('|')
return [{
source: `/:path*((?!^\\/_next\\/image)|${excludedExtensions})`,
headers: [...headers, XXssProtection, CSP]
}, {
source: '/',
headers: [...headers, XXssProtection, CSP]
}, {
// No CSP, XXssProtection
source: `/:path*(\\.${excludedExtensions}$)`,
headers: headers
}, {
// No CSP, XXssProtection
source: '/_next/image',
headers: headers
}]
},
}
module.exports = nextConfig

View File

@ -1,10 +1,13 @@
const stylus = require('@zeit/next-stylus')
const css = require('@zeit/next-css')
// Add support for Stylus/LESS
const preCSS = require('next-pre-css')
// Use Compose plugin for easier maintenance
const withPlugins = require('next-compose-plugins')
const {PHASE_DEVELOPMENT_SERVER} = require('next/constants')
const nextConfig = require('./dzeio.next.config')
module.exports = withPlugins([
[stylus, {
[preCSS, {
cssModules: true,
cssLoaderOptions: {
localIdentName: "[hash:base64:6]",
@ -14,9 +17,7 @@ module.exports = withPlugins([
localIdentName: "[path][name]__[local]"
}
}
}],
[css, {
cssModules: false
}]
]
],
nextConfig
)

View File

@ -1,5 +1,5 @@
{
"name": "@dzeio/url-shortener",
"name": "@avior/games",
"version": "1.1.0",
"private": true,
"scripts": {
@ -10,31 +10,32 @@
"test": "jest --config jext.config.js"
},
"dependencies": {
"@dzeio/components": "^0.2.1",
"@zeit/next-css": "^1.0.1",
"@zeit/next-stylus": "^1.0.1",
"@dzeio/components": "^0.10.1",
"critters": "^0.0.10",
"easy-sitemap": "^1.0.0",
"next": "^10.0.3",
"next-compose-plugins": "^2.2.0",
"next-plausible": "^1.6.1",
"next-pre-css": "^1.0.0",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"react-feather": "^2.0.9",
"stylus": "^0.54.7",
"stylus-loader": "^6.0.0",
"typescript": "^4.1.3",
"webpack": "^4.46.0"
"webpack": "^5.37.1"
},
"devDependencies": {
"@babel/core": "^7.8.7",
"@types/favicons": "^6.2.0",
"@types/node": "^14.0.0",
"@types/node": "^15.6.0",
"@types/react": "^17.0.0",
"@typescript-eslint/eslint-plugin": "^4.14.1",
"@typescript-eslint/parser": "^4.14.1",
"babel-preset-react-app": "^10.0.0",
"eslint": "^7.1.0",
"eslint-config-next": "^10.2.2",
"eslint-plugin-react": "^7.18.3",
"favicons": "^6.2.0",
"ts-node": "^9.1.1",
"vercel": "^21.2.2"
"vercel": "^22.0.1"
}
}

View File

@ -1,6 +1,7 @@
import React from 'react'
import App from 'next/app'
import PlausibleProvider from 'next-plausible'
import '@dzeio/components/style.css'
export default class CApp extends App {
@ -8,6 +9,14 @@ export default class CApp extends App {
public render() {
const { Component, pageProps } = this.props
return(<Component {...pageProps} />)
return (
<PlausibleProvider
domain="games.avior.me"
trackOutboundLinks
integrity="sha384-Bwk7iNMK9H56PgZeINNhN5Mk42LZoNIXe6Ztx5lfALsrTkNWC9yh2J2UFO0xShAv"
>
<Component {...pageProps} />
</PlausibleProvider>
)
}
}

View File

@ -1,6 +1,7 @@
import { Link, Text } from '@dzeio/components'
import React from 'react'
import { Link, Text } from '@dzeio/components'
export default class Index extends React.Component {
public render = () => (

View File

@ -1,9 +1,10 @@
import { Button, Table, Text, Util } from '@dzeio/components'
import React from 'react'
import css from './pokemon-shuffle.styl'
import { Button, Table, Text, Util, NotificationManager } from '@dzeio/components'
import React, { MouseEvent as ReactMouseEvent } from 'react'
import css from './pokemon-shuffle.module.styl'
interface Cell {
id: number
id2: number
horizontalCombo?: true
verticalCombo?: true
justSpawned?: true
@ -14,7 +15,7 @@ interface States {
items: Array<Array<Cell | undefined>>
loading?: true
movingItem?: {x: number, y: number, cell: Cell}
points: number
damage: number
turn: number
combo: number
comboMax: number
@ -29,30 +30,35 @@ export default class PokemonShuffle extends React.Component<unknown, States> {
public state: States = {
items: [[]],
points: 0,
damage: 0,
turn: 0,
combo: 0,
comboMax: 0,
cursorPos: {x: 0, y: 0}
}
public async componentDidMount() {
await this.start()
}
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>
<li><Text>Points: {this.state.damage}</Text></li>
</ul>
<Table >
<tbody className={`${css.table} ${this.state.loading ? css.loading : ''}`}>
<tbody className={Util.buildClassName(css.table, [css.loading, this.state.loading])}>
{this.state.items.map((row, y) => (
<tr key={y}>
{row.map((cell, x) => (
<td
key={cell?.isFalling ? n++ : x}
key={cell?.id2 ?? x}
onClick={this.onCellClick(x, y)}
className={css.cellParent}
>
{/* <Text>{JSON.stringify(cell)}</Text> */}
{cell && (
<Text className={Util.buildClassName(
css[`icon-${cell.id}`],
@ -70,16 +76,11 @@ export default class PokemonShuffle extends React.Component<unknown, States> {
))}
</tbody>
</Table>
<Button onClick={() => this.calculate()}>Calculate!</Button>
<Button onClick={() => this.start()}>Start!</Button>
{/* <Input block type="textarea" value={JSON.stringify(this.state.items)}/> */}
<Button onClick={this.start}>Start!</Button>
{this.state.movingItem && (
<div style={{
position: 'absolute',
<div className={css.hoverItem} style={{
left: this.state.cursorPos.x,
top: this.state.cursorPos.y,
transform: 'translate(-50%, -50%)',
pointerEvents: 'none'
top: this.state.cursorPos.y
}}>
<Text className={Util.buildClassName(css[`icon-${this.state.movingItem.cell?.id}`], css.cell)}>
<div></div>
@ -90,46 +91,52 @@ export default class PokemonShuffle extends React.Component<unknown, States> {
TODO list:
</Text>
<ul>
<li><Text>Lancement Initial sans combo possible</Text></li>
<li><Text>Faire que les clear ce fasse de manière Async</Text></li>
<li><Text>Meilleurs Animation de destruction</Text></li>
<li><Text>Annuler le mouvement si rien n&apos;est claim</Text></li>
<li><Text>Utiliser le système de damages de Pokémon Shuffle https://bulbapedia.bulbagarden.net/wiki/Pok%C3%A9mon_Shuffle#Damage</Text></li>
</ul>
<NotificationManager />
</main>
)
private mouveMove = (ev: MouseEvent) => {
this.setState({cursorPos: {
x: ev.pageX,
y: ev.pageY
x: ev.clientX,
y: ev.clientY
}})
}
private start() {
private start = async () => {
if (this.state.loading) {return}
this.setState({
await this.asyncSetState({
loading: true,
// generate datas
items: Array
.from(Array(BOARD_SIZE))
.map(
() => Array.from(Array(BOARD_SIZE))
.map(() => ({id: random(0, ITEM_COUNT)}))
.map(() => ({id: random(0, ITEM_COUNT), id2: n++}))
)
}, () => this.calculate())
})
// Quickly calculate everythings to make it look like it was perfecly generated
await this.calculate(true)
this.setState({turn: 1, damage: 0, comboMax: 0, combo: 0})
}
private onCellClick = (x: number, y: number) => async () => {
// console.log(x, y)
private onCellClick = (x: number, y: number) => async (ev: ReactMouseEvent) => {
if (this.state.loading) {
return window.alert('Cant play while Calculating')
return NotificationManager.addNotification('Cant play while Calculating')
}
if (!this.state.movingItem) {
const cell = this.state.items[y][x]
if (!cell) {
return window.alert('Cant move nothing')
return NotificationManager.addNotification('Cant move nothing')
}
document.addEventListener('mousemove', this.mouveMove)
this.setState({movingItem: {x,y,cell}})
this.state.items[y][x] = undefined
this.mouveMove(ev.nativeEvent)
return
} else {
document.removeEventListener('mousemove', this.mouveMove)
@ -150,102 +157,97 @@ export default class PokemonShuffle extends React.Component<unknown, States> {
(res) => this.setState(states as States, () => res())
)
private async calculate() {
/**
* Check if items has combos
* @returns if items were changed
*/
private async checkup(): Promise<boolean> {
const items = this.state.items
let checkupCount = 0
let newPoints = 0
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) {continue}
const id = cell.id
// Checkup horizontal
if (!cell.horizontalCombo && !(cell.isFalling || cell.justSpawned)) {
let sameCount = 0
while((x + ++sameCount) < items.length) {
console.log(y + sameCount, x)
const tmp = row[x + sameCount]
if (!tmp || tmp.id !== id || tmp.isFalling || tmp.justSpawned) {break}
}
if (sameCount >= 3) {
checkupCount += 1
let len = 0
for (let i = x; i < (x + sameCount); i++) {
const tmp = items[y][i]
if (!tmp) {continue}
tmp.horizontalCombo = true
len++
}
newPoints += calculateScore(len, this.state.combo)
}
}
// Checkup Vertical
if (!cell.verticalCombo && !(cell.isFalling || cell.justSpawned)) {
let sameCount = 0
while((y + ++sameCount) < items.length) {
// console.log(y + sameCount, x)
const tmp = items[y + sameCount][x]
if (!tmp || tmp.id !== id || tmp.isFalling || tmp.justSpawned) {break}
}
if (sameCount >= 3) {
checkupCount += 1
let len = 0
for (let i = y; i < (y + sameCount); i++) {
const tmp = items[i][x]
if (!tmp) {continue}
tmp.verticalCombo = true
len++
}
newPoints += calculateScore(len, this.state.combo)
// console.log(x, y)
}
}
}
}
// If combos were found
if (checkupCount) {
const combo = this.state.combo + checkupCount
await this.asyncSetState({
items,
damage: this.state.damage + newPoints,
combo,
comboMax: Math.max(this.state.comboMax, combo)
})
}
return !!checkupCount
}
private async endTurn(state?: Partial<States>) {
await this.asyncSetState({...state, loading: undefined, turn: this.state.turn + 1, combo: 0})
}
private async calculate(initial = false) {
// remove combos
const items = this.state.items.map((r) => r.map((c) => {
if (!c) {
return c
}
c.horizontalCombo = undefined
c.verticalCombo = undefined
delete c.horizontalCombo
delete c.verticalCombo
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
let needContinue = false
do {
// Make items fall
itemHasFallen = false
needContinue = false
for (let y = (items.length - 1); y >= 0; y--) {
const row = items[y]
for (let x = 0; x < row.length; x++) {
@ -256,37 +258,107 @@ export default class PokemonShuffle extends React.Component<unknown, States> {
}
if (cell && y+1 < row.length && !items[y+1][x]) {
cell.isFalling = true
needNewTurn = true
itemHasFallen = true
needContinue = true
// Move cell down
items[y+1][x] = cell
items[y][x] = undefined
}
}
}
// Fill to top lane
// Fill the 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}
needContinue = true
items[0][x] = {id: random(0, ITEM_COUNT), id2: n++, 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})
// Need to wait for the falling animation
if (needContinue) {
await this.asyncSetState({items})
if (!initial) {
await wait(300)
}
}
// Checkup if there is combos
const checkup = await this.checkup()
if (!checkup && !needContinue) {
return await this.endTurn({items})
}
// Clear items
let hasCleared = false
for (const row of items) {
for (let x = 0; x < row.length; x++) {
const cell = row[x]
if (!cell || (!cell.horizontalCombo && !cell.verticalCombo)) {continue}
row[x] = undefined
hasCleared = true
needContinue = true
}
}
if (hasCleared && !initial) {
await wait(500)
}
} while (needContinue)
}
}
function calculateScore(len: number, combo: number) {
let score = len * 40 // currently the damage
if (len > 3) {
switch (len) {
case 4:
score *= 1.5
break
case 5:
score *= 2
break
case 6:
score *= 3
break
default:
break
}
}
if (combo > 1) {
if (combo >= 2 && combo <= 4) {
score *= 1.1
}
if (combo >= 5 && combo <= 9) {
score *= 1.1
}
if (combo >= 10 && combo <= 24) {
score *= 1.2
}
if (combo >= 25 && combo <= 49) {
score *= 1.3
}
if (combo >= 50 && combo <= 74) {
score *= 1.4
}
if (combo >= 75 && combo <= 99) {
score *= 1.5
}
if (combo >= 100 && combo <= 199) {
score *= 2
}
if (combo >= 200) {
score *= 2.5
}
}
return score
}
function random(min = 0, max = 100) {
return Math.floor(Math.random() * (max - min) + min)
}
function wait(time: number): Promise<void> {
return new Promise((res) => setTimeout(() => res(), time))
}

View File

@ -1,5 +1,10 @@
$iconSize = 121px
.hoverItem
position fixed
transform translate(-50%, -50%)
pointer-events none
.table
position relative
transition filter .5s ease-in-out
@ -21,28 +26,9 @@ $iconSize = 121px
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
for num in (0..30)
&.icon-{num}
background-position ((num + 1) * $iconSize) 0px
&.isFalling
position absolute
@ -94,7 +80,7 @@ $iconSize = 121px
from
top -117px // revoir précisément
to
top 3px
top 0
@keyframes destroyCircleAnimation
0%

6033
yarn.lock

File diff suppressed because it is too large Load Diff