Commit 7776f17a authored by GUYOMARCH NICOLAS's avatar GUYOMARCH NICOLAS
Browse files

Merge branch 'end' into 'master'

Ajout de la fin du jeu

See merge request !31
parents d59477eb 5f99fa7a
Pipeline #68862 passed with stages
in 10 minutes and 46 seconds
import React, { FC } from 'react';
import { Route, Routes } from 'react-router-dom';
import { Done } from './pages/Done';
import { End } from './pages/End';
import { Game } from './pages/Game';
import { Intro } from './pages/Intro';
......@@ -16,6 +17,7 @@ export const App: FC = () => {
<div className="App">
<Routes>
<Route path="" element={<Intro />} />
<Route path="done" element={<Done />} />
<Route path="menu" element={<Menu />} />
<Route path="start" element={<Start />} />
<Route path="game" element={<Game />} />
......
......@@ -5,7 +5,7 @@ const AdsSection: CardSection = {
name: 'Publicité',
count: 3,
requirements: [
{ name: 'reputation', value: 10 },
{ name: 'reputation', value: 2 },
{ name: 'run', value: 1 }
],
cards: [
......@@ -26,17 +26,17 @@ const AdsSection: CardSection = {
{ name: 'reputation', value: 18 },
{ name: 'run', value: 2 }
],
effects: [{ name: 'reputation', value: 10 }],
effects: [{ name: 'reputation', value: 30 }],
description: 'https://youtu.be/dQw4w9WgXcQ'
},
{
id: 29,
name: 'Journal local',
requirements: [
{ name: 'reputation', value: 5 },
{ name: 'reputation', value: 2 },
{ name: 'run', value: 1 }
],
effects: [{ name: 'reputation', value: 2 }],
effects: [{ name: 'reputation', value: 10 }],
description: 'Le petit journal local que seul les personnes agées achêtent encore.'
},
{
......
......@@ -11,12 +11,11 @@ export const calcScore = (effects: EndEffect[], currentRep: number): number => {
const difficulty: EndEffect = effects.find((e) => e.name === 'difficulty') as EndEffect;
const reputation: EndEffect = effects.find((e) => e.name === 'reputation') as EndEffect;
const pop = Math.min(currentRep + reputation.value, 500);
const diff = ((difficulty.value + 120) / 185) * 2 - 1;
const diff = (difficulty.value + 69) / 240;
const r = (reputation.value + currentRep) / 500;
const base = -200 * (r + 0.05) * (diff - r) * (diff - r) + r;
const lowbonus = Math.min((1 - r) / 2, 1);
const dangermalus = (danger.value / 80) * (-4 * (r - 0.5) * (r - 0.5) + 1);
const dangerMalus = -Math.max(danger.value, 1) / 80;
const popBonus = Math.min(currentRep / 25, 1);
const base = -2 * Math.pow((diff - pop / 500) * Math.log10(Math.max(pop / 5, 1)), 2);
return Math.min(Math.max(base + popBonus + dangerMalus, -1), 1);
return Math.min(Math.max(base - dangermalus + lowbonus, -1), 1) + 0.1;
};
import React, { FC, useEffect, useState } from 'react';
import { useSelector } from 'react-redux';
import { useNavigate } from 'react-router';
import { Transition } from 'react-transition-group';
import { Button } from '../components/Button';
import { BottomBar, BottomBarContainer, BottomBarRight } from '../components/containers/BottomBar';
import { PlayContainer, PlayContainerBot, PlayContainerMid, PlayContainerTop } from '../components/containers/PlayContainer';
import { Progress } from '../components/containers/TopBar';
import { Progression } from '../components/Progression';
import { AppState, Run } from '../store/reducer';
/**
* Page d'affichage des résultats de la partie
* @returns Contenue (jsx)
*/
export const Done: FC = () => {
const runs = useSelector((state: AppState) => state.runs);
const globalPop = useSelector((state: AppState) => state.stats.reputation);
const navigate = useNavigate();
const [show, setShow] = useState<boolean>(false);
const duration = 500;
const defaultStyle: React.CSSProperties = {
transition: `opacity ${duration}ms ease-in-out`,
opacity: 0
};
const transitionStyles: { [index: string]: React.CSSProperties } = {
entering: { opacity: 1 },
entered: { opacity: 1 },
exiting: { opacity: 0 },
exited: { opacity: 0 }
};
const goNext = () => {
navigate('/menu');
};
useEffect(() => {
setTimeout(() => setShow(true), 0);
}, []);
return (
<Transition in={show} timeout={duration} onExited={() => goNext()}>
{(state) => (
<div
style={{
...defaultStyle,
...transitionStyles[state]
}}
>
<PlayContainer>
<PlayContainerTop>
<div
style={{
fontWeight: 'bold',
fontSize: '24px',
height: '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center'
}}
>
Résultats de la Partie
</div>
</PlayContainerTop>
<PlayContainerMid>
<div
style={{
display: 'flex',
alignItems: 'center',
flexDirection: 'column',
height: '100%'
}}
>
<Progression runs={runs} />
<div style={{ fontSize: '32px', marginTop: '1em', marginBottom: '0.5em' }}>
{(() => {
if (globalPop < 100) {
return '...';
} else if (globalPop < 300) {
return 'Dommage';
} else if (globalPop < 400) {
return 'Presque';
} else if (globalPop < 500) {
return 'Encore un petit effort !';
} else {
return 'Bravo !';
}
})()}
</div>
<div style={{ width: '20vw' }}>
<Progress percent={globalPop / 5} />
</div>
<div style={{ fontSize: '32px', marginTop: '0.5em', marginBottom: '0.5em' }}>{globalPop}/500</div>
{runs.map((run: Run, index: number) => {
return (
<div
key={index}
style={{ display: 'flex', justifyContent: 'center', flexDirection: 'row', width: '20vw', marginTop: '1em' }}
>
<div style={{ width: '35%' }}>Course #{index + 1}</div>
<div style={{ width: '65%' }}>
<Progress percent={((run?.score ?? -1) + 1) * 50} variant="score" />
</div>
</div>
);
})}
</div>
</PlayContainerMid>
<PlayContainerBot>
<BottomBar>
<BottomBarContainer>
<BottomBarRight>
<Button bordered={false} variation="success" onClick={() => setShow(false)}>
Rejouer
</Button>
</BottomBarRight>
</BottomBarContainer>
</BottomBar>
</PlayContainerBot>
</PlayContainer>
</div>
)}
</Transition>
);
};
......@@ -15,12 +15,14 @@ import { AppState } from '../store/reducer';
*/
export const End: FC = () => {
const run = useSelector((state: AppState) => state.stats.run);
const maxRun = useSelector((state: AppState) => state.runs.length);
const reputation = useSelector((state: AppState) => state.stats.reputation);
const effects = useSelector((state: AppState) => state.current_effects);
const runScore = calcScore(effects, reputation);
const dispatch = useDispatch();
const navigate = useNavigate();
const [show, setShow] = useState<boolean>(false);
const [done, setDone] = useState<boolean>(false);
const duration = 500;
......@@ -39,7 +41,11 @@ export const End: FC = () => {
const goNext = () => {
dispatch({ type: 'stats/run', score: runScore });
dispatch({ type: 'state/reputation', reputation: runScore * (effects.filter((e) => e.name === 'reputation')[0]?.value ?? 0) });
navigate('/start');
if (done) {
navigate('/done');
} else {
navigate('/start');
}
};
useEffect(() => {
......@@ -81,9 +87,22 @@ export const End: FC = () => {
<BottomBar>
<BottomBarContainer>
<BottomBarRight>
<Button bordered={false} variation="success" onClick={() => setShow(false)}>
Course suivante
</Button>
{run + 1 < maxRun ? (
<Button bordered={false} variation="success" onClick={() => setShow(false)}>
Course suivante
</Button>
) : (
<Button
bordered={false}
variation="success"
onClick={() => {
setDone(true);
setShow(false);
}}
>
Suivant
</Button>
)}
</BottomBarRight>
</BottomBarContainer>
</BottomBar>
......
import React, { FC, useEffect, useState } from 'react';
import { useDispatch } from 'react-redux';
import { useNavigate } from 'react-router-dom';
import { Transition } from 'react-transition-group';
import { Button } from '../components/Button';
......@@ -8,6 +9,8 @@ import { Button } from '../components/Button';
* @returns Contenue (jsx)
*/
export const Menu: FC = () => {
const dispatch = useDispatch();
const duration = 500;
const defaultStyle: React.CSSProperties = {
......@@ -51,7 +54,14 @@ export const Menu: FC = () => {
>
<div style={{ fontSize: '72px', marginBottom: '1em' }}>XTremTrail</div>
<div style={{ marginTop: '3em' }}>
<Button bordered={false} variation="success" onClick={() => setShow(false)}>
<Button
bordered={false}
variation="success"
onClick={() => {
dispatch({ type: 'stats/reset' });
setShow(false);
}}
>
Jouer
</Button>
</div>
......
......@@ -5,6 +5,7 @@ import { Transition } from 'react-transition-group';
import { Button } from '../components/Button';
import { BottomBar, BottomBarContainer, BottomBarRight } from '../components/containers/BottomBar';
import { PlayContainer, PlayContainerBot, PlayContainerMid } from '../components/containers/PlayContainer';
import { Progress } from '../components/containers/TopBar';
import { Progression } from '../components/Progression';
import { AppState } from '../store/reducer';
......@@ -12,6 +13,7 @@ export const Start = () => {
const navigate = useNavigate();
const run = useSelector((state: AppState) => state.stats.run);
const runs = useSelector((state: AppState) => state.runs);
const globalPop = useSelector((state: AppState) => state.stats.reputation);
const duration = 500;
......@@ -55,6 +57,11 @@ export const Start = () => {
>
<div style={{ fontSize: '72px', marginBottom: '1em' }}>Course #{run + 1}</div>
<Progression runs={runs} />
<div style={{ fontSize: '32px', marginTop: '1em', marginBottom: '0.5em' }}>Popularité</div>
<div style={{ width: '20vw' }}>
<Progress percent={globalPop / 5} />
</div>
<div style={{ fontSize: '32px', marginTop: '0.5em' }}>{globalPop}/500</div>
</div>
</PlayContainerMid>
<PlayContainerBot>
......
......@@ -32,32 +32,47 @@ export interface AppState {
const initialState: AppState = {
stats: {
run: 0,
reputation: 10
reputation: 0
},
runs: [{ done: false }, { done: false }, { done: false }, { done: false }, { done: false }, { done: false }, { done: false }, { done: false }],
runs: [{ done: false }, { done: false }],
current_effects: []
};
/**
* Reducer avec gestion de la réinitialisation
* @param state État actuel
* @param action Action à effectuer
* @returns État modifié
*/
export const rootReducer: Reducer = (state: AppState | undefined = JSON.parse(JSON.stringify(initialState)), action = { type: '' }) => {
if (action.type === 'stats/reset') {
state = undefined;
}
return appReducer(state, action);
};
/**
* Réducer. Reçoit des actions et agit en conséquence sur les états du jeu
* @param state État actuel
* @param action Action à effectuer
* @returns État modifié
*/
export const appReducer: Reducer = (state: AppState = initialState, action = { type: '' }) => {
export const appReducer: Reducer = (state: AppState = JSON.parse(JSON.stringify(initialState)), action = { type: '' }) => {
switch (action.type) {
case 'stats/run':
state.runs[state.stats.run].done = true;
state.runs[state.stats.run].score = action.score;
state.stats.run += 1;
return state;
break;
case 'state/reputation':
state.stats.reputation += action.reputation;
return state;
break;
case 'game/end':
state.current_effects = action.effects;
return state;
break;
default:
return state;
break;
}
return state;
};
import { createStore, Store } from 'redux';
import { appReducer } from './reducer';
import { rootReducer } from './reducer';
/**
* Stockage de l'état global du jeu dans redux
*/
export const appStore: Store = createStore(appReducer);
export const appStore: Store = createStore(rootReducer);
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment