diff options
Diffstat (limited to 'diplomacy/web/src/gui/wizards/gameCreation')
7 files changed, 486 insertions, 0 deletions
diff --git a/diplomacy/web/src/gui/wizards/gameCreation/gameCreationWizard.js b/diplomacy/web/src/gui/wizards/gameCreation/gameCreationWizard.js new file mode 100644 index 0000000..daaa461 --- /dev/null +++ b/diplomacy/web/src/gui/wizards/gameCreation/gameCreationWizard.js @@ -0,0 +1,109 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import {Panels} from "./panelList"; +import {PanelChooseMap} from "./panelChooseMap"; +import {PanelChoosePlayers} from "./panelChoosePlayers"; +import {PanelChoosePower} from "./panelChoosePower"; +import {PanelChooseSettings} from "./panelChooseSettings"; +import {Maps} from "./mapList"; +import {UTILS} from "../../../diplomacy/utils/utils"; + +export class GameCreationWizard extends React.Component { + constructor(props) { + super(props); + this.state = { + panel: Panels.CHOOSE_MAP, + game_id: UTILS.createGameID(this.props.username), + power_name: null, + n_controls: -1, + deadline: 0, + registration_password: '', + + map: Maps[0], + no_press: false + }; + this.backward = this.backward.bind(this); + this.forward = this.forward.bind(this); + this.updateParams = this.updateParams.bind(this); + } + + updateParams(params) { + this.setState(params); + } + + goToPanel(panelID) { + if (panelID < Panels.CHOOSE_MAP) + this.props.onCancel(); + else if (panelID > Panels.CHOOSE_SETTINGS) { + const rules = ['POWER_CHOICE']; + if (this.state.no_press) + rules.push('NO_PRESS'); + if (!this.state.deadline) { + rules.push('NO_DEADLINE'); + rules.push('REAL_TIME'); + } + this.props.onSubmit({ + game_id: this.state.game_id, + map_name: this.state.map.name, + power_name: this.state.power_name, + n_controls: this.state.n_controls, + deadline: this.state.deadline, + registration_password: this.state.registration_password || null, + rules: rules + }); + } else + this.setState({panel: panelID, registration_password: ''}); + } + + backward(step) { + this.goToPanel(this.state.panel - (step ? step : 1)); + } + + forward(step) { + this.goToPanel(this.state.panel + (step ? step : 1)); + } + + renderPanel() { + switch (this.state.panel) { + case Panels.CHOOSE_MAP: + return <PanelChooseMap forward={this.forward} + params={this.state} + onUpdateParams={this.updateParams} + cancel={this.props.onCancel}/>; + case Panels.CHOOSE_PLAYERS: + return <PanelChoosePlayers backward={this.backward} + forward={this.forward} + onUpdateParams={this.updateParams} + nbPowers={this.props.availableMaps[this.state.map.name].powers.length} + cancel={this.props.onCancel}/>; + case Panels.CHOOSE_POWER: + return <PanelChoosePower backward={this.backward} + forward={this.forward} + onUpdateParams={this.updateParams} + powers={this.props.availableMaps[this.state.map.name].powers} + cancel={this.props.onCancel}/>; + case Panels.CHOOSE_SETTINGS: + return <PanelChooseSettings backward={this.backward} + forward={this.forward} + onUpdateParams={this.updateParams} + username={this.props.username} + params={this.state} + cancel={this.props.onCancel}/>; + default: + return ''; + } + } + + render() { + return ( + <div className="game-creation-wizard">{this.renderPanel()}</div> + ); + } +} + +GameCreationWizard.propTypes = { + onCancel: PropTypes.func.isRequired, + onSubmit: PropTypes.func.isRequired, + availableMaps: PropTypes.object.isRequired, + username: PropTypes.string.isRequired +}; diff --git a/diplomacy/web/src/gui/wizards/gameCreation/mapList.js b/diplomacy/web/src/gui/wizards/gameCreation/mapList.js new file mode 100644 index 0000000..5d2c00a --- /dev/null +++ b/diplomacy/web/src/gui/wizards/gameCreation/mapList.js @@ -0,0 +1,41 @@ +class VariantInfo { + constructor(variantName, variantTitle) { + this.name = variantName; + this.title = variantTitle; + this.map = null; + } + + svgName() { + return this.map.name; + } +} + +class MapInfo { + constructor(mapName, mapTitle, variants) { + this.name = mapName; + this.title = mapTitle; + this.variants = null; + if (variants) { + this.variants = []; + for (let variant of variants) { + variant.map = this; + this.variants.push(variant); + } + } + } + + svgName() { + return this.name; + } +} + +export const Maps = [ + new MapInfo('standard', 'Standard', [ + new VariantInfo('standard', 'Default'), + new VariantInfo('standard_age_of_empires', 'Age of empires'), + new VariantInfo('standard_age_of_empires_2', 'Age of empires II'), + new VariantInfo('standard_fleet_rome', 'Fleet at Rome'), + new VariantInfo('standard_france_austria', 'France VS Austria'), + new VariantInfo('standard_germany_italy', 'Germany VS Italy') + ]), +]; diff --git a/diplomacy/web/src/gui/wizards/gameCreation/panelChooseMap.js b/diplomacy/web/src/gui/wizards/gameCreation/panelChooseMap.js new file mode 100644 index 0000000..5c40f1c --- /dev/null +++ b/diplomacy/web/src/gui/wizards/gameCreation/panelChooseMap.js @@ -0,0 +1,94 @@ +import React from "react"; +import {Maps} from "./mapList"; +import {FancyBox} from "../../components/fancyBox"; +import PropTypes from "prop-types"; + +export class PanelChooseMap extends React.Component { + render() { + const mapImg = require(`../../../maps/svg/${this.props.params.map.svgName()}.svg`); + const mapEntries = []; + let count = 0; + for (let mapInfo of Maps) { + ++count; + if (!mapInfo.variants) { + mapEntries.push( + <div key={count} className="mb-1"> + <button type="button" + className="btn btn-secondary btn-sm btn-block" + onMouseOver={() => this.props.onUpdateParams({map: mapInfo})} + onClick={() => this.props.forward()}> + {mapInfo.title} + </button> + </div> + ); + } else { + const dropDownID = `collapse-${count}-${mapInfo.name}`; + const variants = mapInfo.variants.slice(); + const defaultVariant = variants[0]; + mapEntries.push( + <div key={count}> + <div className="mb-1 d-flex flex-row justify-content-center"> + <button type="button" + className="btn btn-secondary btn-sm flex-grow-1 mr-1" + onMouseOver={() => this.props.onUpdateParams({map: defaultVariant})} + onClick={() => this.props.forward()}> + {mapInfo.title} ({defaultVariant.title}) + </button> + <button type="button" + className="btn btn-outline-secondary btn-sm collapsed" + data-toggle="collapse" + data-target={`#${dropDownID}`} + aria-expanded={false} + aria-controls={dropDownID}> + <span className="unroll"><strong>+</strong></span> + <span className="roll"><strong>-</strong></span> + </button> + </div> + <div className="collapse" id={dropDownID}> + <div> + {(() => { + const views = []; + for (let i = 1; i < variants.length; ++i) { + const variantInfo = variants[i]; + views.push( + <div key={variantInfo.name} className="mb-1"> + <button type="button" + className="btn btn-outline-secondary btn-sm btn-block" + onMouseOver={() => this.props.onUpdateParams({map: variantInfo})} + onClick={() => this.props.forward()}> + {variantInfo.title} + </button> + </div> + ); + } + return views; + })()} + </div> + </div> + </div> + ); + } + } + return ( + <FancyBox title={'Choose a map'} onClose={this.props.cancel}> + <div className="row panel-choose-map"> + <div className="col-md"> + <div className="map-list p-1 ml-0 ml-sm-1"> + {mapEntries} + </div> + </div> + <div className="col-md"> + <img className="img-fluid" src={mapImg} alt={this.props.params.map.title}/> + </div> + </div> + </FancyBox> + ); + } +} + +PanelChooseMap.propTypes = { + forward: PropTypes.func.isRequired, + cancel: PropTypes.func.isRequired, + params: PropTypes.object.isRequired, + onUpdateParams: PropTypes.func.isRequired +}; diff --git a/diplomacy/web/src/gui/wizards/gameCreation/panelChoosePlayers.js b/diplomacy/web/src/gui/wizards/gameCreation/panelChoosePlayers.js new file mode 100644 index 0000000..84a47a0 --- /dev/null +++ b/diplomacy/web/src/gui/wizards/gameCreation/panelChoosePlayers.js @@ -0,0 +1,61 @@ +import React from "react"; +import {FancyBox} from "../../components/fancyBox"; +import PropTypes from "prop-types"; +import Octicon, {ArrowLeft} from "@primer/octicons-react"; + +export class PanelChoosePlayers extends React.Component { + render() { + return ( + <FancyBox title={'Number of human players'} onClose={this.props.cancel}> + <div className="row"> + <div className="col-sm"> + <button type="button" className="btn btn-secondary btn-sm btn-block inline" onClick={() => { + this.props.onUpdateParams({n_controls: 0}); + this.props.forward(2); + }}>None - just bots + </button> + </div> + <div className="col-sm"> + <button type="button" className="btn btn-secondary btn-sm btn-block inline" onClick={() => { + this.props.onUpdateParams({n_controls: this.props.nbPowers}); + this.props.forward(); + }}>All humans - no bots + </button> + </div> + </div> + <div className="d-flex flex-row justify-content-center my-2"> + {(() => { + const choice = []; + for (let i = 0; i < this.props.nbPowers; ++i) { + choice.push( + <button key={i} type="button" + className={`btn btn-secondary btn-sm flex-grow-1 ${i === 0 ? '' : 'ml-sm-1'}`} + onClick={() => { + this.props.onUpdateParams({n_controls: i + 1}); + this.props.forward(); + }}> + {i + 1} + </button> + ); + } + return choice; + })()} + </div> + <div> + <button type="button" className="btn btn-secondary btn-sm px-3" + onClick={() => this.props.backward()}> + <Octicon icon={ArrowLeft}/> + </button> + </div> + </FancyBox> + ); + } +} + +PanelChoosePlayers.propTypes = { + backward: PropTypes.func.isRequired, + forward: PropTypes.func.isRequired, + cancel: PropTypes.func.isRequired, + onUpdateParams: PropTypes.func.isRequired, + nbPowers: PropTypes.number.isRequired +}; diff --git a/diplomacy/web/src/gui/wizards/gameCreation/panelChoosePower.js b/diplomacy/web/src/gui/wizards/gameCreation/panelChoosePower.js new file mode 100644 index 0000000..dc400bd --- /dev/null +++ b/diplomacy/web/src/gui/wizards/gameCreation/panelChoosePower.js @@ -0,0 +1,62 @@ +import React from "react"; +import {FancyBox} from "../../components/fancyBox"; +import PropTypes from "prop-types"; +import Octicon, {ArrowLeft} from "@primer/octicons-react"; + +export class PanelChoosePower extends React.Component { + render() { + return ( + <FancyBox title={'Choose your power'} onClose={this.props.cancel}> + <div className="row"> + <div className="col-sm"> + <button type="button" className="btn btn-secondary btn-sm btn-block inline" onClick={() => { + this.props.onUpdateParams({power_name: null}); + this.props.forward(); + }}>I just want to observe + </button> + </div> + <div className="col-sm"> + <button type="button" className="btn btn-secondary btn-sm btn-block inline" onClick={() => { + const powerName = this.props.powers[Math.floor(Math.random() * this.props.powers.length)]; + this.props.onUpdateParams({power_name: powerName}); + this.props.forward(); + }}>Choose randomly for me + </button> + </div> + </div> + <div className="d-flex flex-row justify-content-center my-2"> + {(() => { + const choice = []; + for (let i = 0; i < this.props.powers.length; ++i) { + choice.push( + <button key={i} type="button" + className={`btn btn-secondary btn-sm flex-grow-1 ${i === 0 ? '' : 'ml-sm-1'}`} + onClick={() => { + this.props.onUpdateParams({power_name: this.props.powers[i]}); + this.props.forward(); + }}> + {this.props.powers[i]} + </button> + ); + } + return choice; + })()} + </div> + <div> + <button type="button" className="btn btn-secondary btn-sm px-3" + onClick={() => this.props.backward()}> + <Octicon icon={ArrowLeft}/> + </button> + </div> + </FancyBox> + ); + } +} + +PanelChoosePower.propTypes = { + backward: PropTypes.func.isRequired, + forward: PropTypes.func.isRequired, + cancel: PropTypes.func.isRequired, + onUpdateParams: PropTypes.func.isRequired, + powers: PropTypes.arrayOf(PropTypes.string).isRequired +}; diff --git a/diplomacy/web/src/gui/wizards/gameCreation/panelChooseSettings.js b/diplomacy/web/src/gui/wizards/gameCreation/panelChooseSettings.js new file mode 100644 index 0000000..e509158 --- /dev/null +++ b/diplomacy/web/src/gui/wizards/gameCreation/panelChooseSettings.js @@ -0,0 +1,113 @@ +import React from "react"; +import {FancyBox} from "../../components/fancyBox"; +import PropTypes from "prop-types"; +import {UTILS} from "../../../diplomacy/utils/utils"; +import Octicon, {ArrowLeft} from "@primer/octicons-react"; + +const DEADLINES = [ + [0, '(no deadline)'], + [60, '1 min'], + [60 * 5, '5 min'], + [60 * 30, '30 min'], + [60 * 60 * 2, '2 hrs'], + [60 * 60 * 24, '24 hrs'], +]; + +export class PanelChooseSettings extends React.Component { + constructor(props) { + super(props); + this.onCheckNoPress = this.onCheckNoPress.bind(this); + this.onSelectDeadline = this.onSelectDeadline.bind(this); + this.onSetRegistrationPassword = this.onSetRegistrationPassword.bind(this); + this.onSetGameID = this.onSetGameID.bind(this); + } + + onCheckNoPress(event) { + this.props.onUpdateParams({no_press: event.target.checked}); + } + + onSelectDeadline(event) { + this.props.onUpdateParams({deadline: parseInt(event.target.value)}); + } + + onSetRegistrationPassword(event) { + this.props.onUpdateParams({registration_password: event.target.value}); + } + + onSetGameID(event) { + let gameID = event.target.value; + if (!gameID) + gameID = UTILS.createGameID(this.props.username); + this.props.onUpdateParams({game_id: gameID}); + } + + render() { + return ( + <FancyBox title={'Other settings'} onClose={this.props.cancel}> + <div> + <form> + <div className="form-group row align-items-center mb-2"> + <label className="col-md col-form-label" htmlFor="deadline">Deadline</label> + <div className="col-md"> + <select id="deadline" className="custom-select custom-select-sm" + value={this.props.params.deadline} + onChange={this.onSelectDeadline}> + {DEADLINES.map((deadline, index) => ( + <option key={index} value={deadline[0]}>{deadline[1]}</option> + ))} + </select> + </div> + </div> + <div className="form-group row mb-2"> + <label className="col-md col-form-label" htmlFor="registration-password">Login + password</label> + <div className="col-md"> + <input type="password" className="form-control form-control-sm" + id="registration-password" + value={this.props.params.registration_password} + onChange={this.onSetRegistrationPassword} placeholder="(no password)"/> + </div> + </div> + <div className="form-group row mb-2"> + <label className="col-md col-form-label" htmlFor="game-id">Game ID</label> + <div className="col-md"> + <input type="text" className="form-control form-control-sm" + id="game-id" + value={this.props.params.game_id} + onChange={this.onSetGameID}/> + </div> + </div> + <div className="custom-control custom-checkbox mb-5"> + <input type="checkbox" className="custom-control-input" id="no-press" + checked={this.props.params.no_press} onChange={this.onCheckNoPress}/> + <label className="custom-control-label" htmlFor="no-press">No messages allowed</label> + </div> + </form> + </div> + <div className="row"> + <div className="col-sm"> + <button type="button" className="btn btn-secondary btn-sm btn-block" + onClick={() => this.props.backward()}> + <Octicon icon={ArrowLeft}/> + </button> + </div> + <div className="col-sm"> + <button type="button" className="btn btn-success btn-sm btn-block inline" + onClick={() => this.props.forward()}> + <strong>create the game</strong> + </button> + </div> + </div> + </FancyBox> + ); + } +} + +PanelChooseSettings.propTypes = { + backward: PropTypes.func.isRequired, + forward: PropTypes.func.isRequired, + cancel: PropTypes.func.isRequired, + params: PropTypes.object.isRequired, + onUpdateParams: PropTypes.func.isRequired, + username: PropTypes.string.isRequired +}; diff --git a/diplomacy/web/src/gui/wizards/gameCreation/panelList.js b/diplomacy/web/src/gui/wizards/gameCreation/panelList.js new file mode 100644 index 0000000..0b6100c --- /dev/null +++ b/diplomacy/web/src/gui/wizards/gameCreation/panelList.js @@ -0,0 +1,6 @@ +export const Panels = { + CHOOSE_MAP: 0, + CHOOSE_PLAYERS: 1, + CHOOSE_POWER: 2, + CHOOSE_SETTINGS: 3 +}; |