diff options
| author | notoraptor <stevenbocco@gmail.com> | 2019-07-25 10:59:36 -0400 | 
|---|---|---|
| committer | Philip Paquette <pcpaquette@gmail.com> | 2019-07-25 11:15:59 -0400 | 
| commit | 48ee1a065debde5027fc17e49144d348258dc5e4 (patch) | |
| tree | b85be5b31a61ad911a89789c2089eaf7852ad4d9 /diplomacy/web/src/gui/wizards/gameCreation | |
| parent | 09f9589bfa1a9e19805c2cc7dc58cad4da93f17f (diff) | |
[Web] Added game creation interface
- Replaced fancybox with react-confirm-alert + dialog box
- Removed unused code
- Default map can be selected with 1-click
- Added ability to select map variants
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 +}; | 
