Skip to content

Commit

Permalink
Support categories in Preset editor
Browse files Browse the repository at this point in the history
  • Loading branch information
HSZemi committed Nov 9, 2023
1 parent 82ce485 commit ec0f24c
Show file tree
Hide file tree
Showing 11 changed files with 278 additions and 12 deletions.
32 changes: 31 additions & 1 deletion src/actions/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,18 @@ export interface ISetEditorDraftOptions {
value: DraftOption[]
}

export interface ISetEditorCategoryLimitPick {
type: Actions.SET_EDITOR_CATEGORY_LIMIT_PICK
key: string
value: number | null
}

export interface ISetEditorCategoryLimitBan {
type: Actions.SET_EDITOR_CATEGORY_LIMIT_BAN
key: string
value: number | null
}

export interface ISetHighlightedAction {
type: Actions.SET_HIGHLIGHTED_ACTION,
value: number | null;
Expand Down Expand Up @@ -255,7 +267,9 @@ export type PresetEditorAction = ISetEditorPreset
| IDuplicateEditorTurn
| ISetEditorTurnOrder
| ISetEditorName
| ISetEditorDraftOptions;
| ISetEditorDraftOptions
| ISetEditorCategoryLimitPick
| ISetEditorCategoryLimitBan;

export type RecentDraftsAction = ISpectateDrafts
| IUnspectateDrafts
Expand Down Expand Up @@ -490,6 +504,22 @@ export function setEditorDraftOptions(value: DraftOption[]): ISetEditorDraftOpti
}
}

export function setEditorCategoryLimitPick(key: string, value: number | null): ISetEditorCategoryLimitPick {
return {
key,
value,
type: Actions.SET_EDITOR_CATEGORY_LIMIT_PICK
}
}

export function setEditorCategoryLimitBan(key: string, value: number | null): ISetEditorCategoryLimitBan {
return {
key,
value,
type: Actions.SET_EDITOR_CATEGORY_LIMIT_BAN
}
}

export function setCountdownValue(value: ICountdownValues): ICountdownEvent {
return {
type: ServerActions.SET_COUNTDOWN_VALUE,
Expand Down
1 change: 1 addition & 0 deletions src/components/PresetEditor/ActionDropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ class ActionDropdown extends React.Component<Props, object> {
options.push(<option key={i++} value={Action.REVEAL_PICKS}>{Action.REVEAL_PICKS}</option>);
options.push(<option key={i++} value={Action.REVEAL_BANS}>{Action.REVEAL_BANS}</option>);
options.push(<option key={i++} value={Action.REVEAL_SNIPES}>{Action.REVEAL_SNIPES}</option>);
options.push(<option key={i++} value={Action.RESET_CL}>{Action.RESET_CL}</option>);
options.push(<option key={i++} value={Action.PAUSE}>{Action.PAUSE}</option>);
options.push(<option key={i++} value={Action.PICK}>{Action.PICK}</option>);
options.push(<option key={i++} value={Action.BAN}>{Action.BAN}</option>);
Expand Down
5 changes: 4 additions & 1 deletion src/components/PresetEditor/PlayerTurnSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@ import ExclusivityDropdown from "./ExclusivityDropdown";
import ParallelCheckbox from "./ParallelCheckbox";
import AsOpponentCheckbox from "./AsOpponentCheckbox";
import AsPlayerDropdown from "./AsPlayerDropdown";
import TurnCategoriesInput from "./TurnCategoriesInput";

interface Props {
turn: Turn,
player: Player,
index: number
index: number,
}

class PlayerTurnSettings extends React.Component<Props, object> {
Expand All @@ -25,6 +26,7 @@ class PlayerTurnSettings extends React.Component<Props, object> {
<ExclusivityDropdown turn={this.props.turn} index={this.props.index}/>
<br/>
<AsPlayerDropdown turn={this.props.turn} index={this.props.index}/>
<TurnCategoriesInput turn={this.props.turn} index={this.props.index} key={'tci'+this.props.index}/>
</div>;
}
return <React.Fragment>
Expand All @@ -36,6 +38,7 @@ class PlayerTurnSettings extends React.Component<Props, object> {
<ParallelCheckbox turn={this.props.turn} index={this.props.index}/>
&nbsp;
<AsOpponentCheckbox turn={this.props.turn} index={this.props.index}/>
<TurnCategoriesInput turn={this.props.turn} index={this.props.index} key={'tci'+this.props.index}/>
</React.Fragment>;
}
}
Expand Down
37 changes: 36 additions & 1 deletion src/components/PresetEditor/PresetEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import {Dispatch} from "redux";
import * as actions from "../../actions";
import {
IDuplicateEditorTurn,
ISetEditorCategoryLimitBan,
ISetEditorCategoryLimitPick,
ISetEditorDraftOptions,
ISetEditorName,
ISetEditorPreset,
Expand Down Expand Up @@ -42,6 +44,8 @@ interface Props extends WithTranslation, RouteComponentProps<any> {
onTurnOrderChange: (turns: Turn[]) => ISetEditorTurnOrder,
onPresetNameChange: (value: string) => ISetEditorName,
onPresetDraftOptionsChange: (value: DraftOption[]) => ISetEditorDraftOptions
onSetCategoryLimitPick: (key: string, value: number | null) => ISetEditorCategoryLimitPick
onSetCategoryLimitBan: (key: string, value: number | null) => ISetEditorCategoryLimitBan
}

interface State {
Expand Down Expand Up @@ -150,6 +154,18 @@ class PresetEditor extends React.Component<Props, State> {
const customOptions: boolean = this.state.defaultDraftOptions.length === 0;
let optionsSelection = customOptions ? <PresetEditorCustomOptions/> : <PresetEditorCivSelection availableOptions={this.state.defaultDraftOptions}/>;

const categories = [...new Set(this.props.preset.options.map(value => value.category))].sort();
const categoryInputsPick = categories.map(category => <li>{category}: <input type="number"
value={this.props.preset?.categoryLimits.pick[category]}
onChange={event => this.props.onSetCategoryLimitPick(category, parseInt(event.target.value) || null)}
key={'categoryInputsBan-' + category}/>
</li>);
const categoryInputsBan = categories.map(category => <li>{category}: <input type="number"
value={this.props.preset?.categoryLimits.ban[category]}
onChange={event => this.props.onSetCategoryLimitBan(category, parseInt(event.target.value) || null)}
key={'categoryInputsBan-' + category}/>
</li>);

return (
<React.Fragment>
<div className={'content box'}>
Expand Down Expand Up @@ -291,7 +307,24 @@ class PresetEditor extends React.Component<Props, State> {
</div>
</div>
<hr/>
<h3>3. <Trans i18nKey="presetEditor.createSaveDraft">Create Draft or Save</Trans></h3>

<h3>3. <Trans i18nKey="presetEditor.categoryLimits">Category Limits</Trans></h3>
<p><Trans i18nKey="presetEditor.categoryLimitsExplanation">Here you may, for each category,
define a maximum number of times Draft Options from it can be picked or banned.
Leave the input emtpy to set no limit for a category.</Trans></p>
<div className="columns">
<div className="column">
<h4><Trans i18nKey="presetEditor.categoryLimitsPick">Pick</Trans></h4>
<ul>{categoryInputsPick}</ul>
</div>
<div className="column">
<h4><Trans i18nKey="presetEditor.categoryLimitsBan">Ban</Trans></h4>
<ul>{categoryInputsBan}</ul>
</div>
</div>
<hr/>

<h3>4. <Trans i18nKey="presetEditor.createSaveDraft">Create Draft or Save</Trans></h3>
<div className="field is-grouped">
<p className="control">
<input type={'text'} value={this.props.preset.name} className="input"
Expand Down Expand Up @@ -343,6 +376,8 @@ export function mapDispatchToProps(dispatch: Dispatch<actions.Action>) {
onTurnOrderChange: (turns: Turn[]) => dispatch(actions.setEditorTurnOrder(turns)),
onPresetDraftOptionsChange: (value: DraftOption[]) => dispatch(actions.setEditorDraftOptions(value)),
onPresetNameChange: (value: string) => dispatch(actions.setEditorName(value)),
onSetCategoryLimitPick: (key: string, value: number | null) => dispatch(actions.setEditorCategoryLimitPick(key, value)),
onSetCategoryLimitBan: (key: string, value: number | null) => dispatch(actions.setEditorCategoryLimitBan(key, value)),
}
}

Expand Down
24 changes: 24 additions & 0 deletions src/components/PresetEditor/PresetOption.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,21 @@ class PresetOption extends React.Component<IProps, object> {
}}/>
</div>
</div>
<div className="field is-horizontal">
<div className={'field-label is-small'}>
<label className="label is-small">
<Trans i18nKey="presetEditor.option.categoryName">Category</Trans>
</label>
</div>
<div className={'field-body'}>
<input className="input is-small" type="text"
placeholder="default"
value={draftOption.category}
onChange={(event) => {
this.updateCategory(event.target.value);
}}/>
</div>
</div>
<div className={'columns options-preview'}>
<div className={'column has-text-centered'}>
<DraftOptionPanel draftOption={draftOption} active={false} highlighted={false}
Expand Down Expand Up @@ -214,6 +229,15 @@ class PresetOption extends React.Component<IProps, object> {
});
this.props.onPresetDraftOptionsChange(draftOptions);
}
private updateCategory(value: string) {
if (this.props.preset === null || this.props.preset === undefined || this.props.preset.options === undefined) {
return;
}
const draftOptions = [...this.props.preset?.options];
const oldDraftOption = draftOptions[this.props.draftOptionIndex];
draftOptions[this.props.draftOptionIndex] = new DraftOption(oldDraftOption.id, oldDraftOption.name, oldDraftOption.imageUrls, oldDraftOption.i18nPrefix, value);
this.props.onPresetDraftOptionsChange(draftOptions);
}
}

export function mapStateToProps(state: ApplicationState) {
Expand Down
74 changes: 74 additions & 0 deletions src/components/PresetEditor/TurnCategoriesInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import React from "react";
import {Action as ReducerAction, ISetEditorTurn, setEditorTurn} from "../../actions";
import Turn from "../../models/Turn";
import {Dispatch} from "redux";
import {connect} from "react-redux";
import Preset from "../../models/Preset";
import {ApplicationState} from "../../types";
import {Trans} from "react-i18next";

interface Props {
turn: Turn,
preset: Preset | null,
index: number,
onValueChange: (turn: Turn, index: number) => ISetEditorTurn
}

class TurnCategoriesInput extends React.Component<Props, object> {
private readonly delimiter = '|';

render() {
return <div key={'dafuq-' + this.props.index}>
<div className="field is-horizontal">
<div className="field-label is-small">
<label htmlFor={'categoryinput-' + this.props.index} className="label">
<Trans i18nKey="presetEditor.turnCategories">Categories</Trans>
</label>
</div>
<div className="field-body">
<div className="field">
<p className="control">
<input className={'input is-small'} id={'categoryinput-' + this.props.index}
value={this.props.turn.categories.join(this.delimiter)}
onChange={(event) => {
this.updateAllowedCategories(event.target.value);
}} key={'categoryinput-' + this.props.index}/>
</p>
</div>
<div className="field">
<button className={'button is-small'} onClick={() => {
this.setAllCategoriesAsAllowed();
}}><Trans i18nKey="presetEditor.turnCategoriesAll">all</Trans></button>
</div>
</div>
</div>
</div>
}

private updateAllowedCategories(value: string) {
const t = this.props.turn;
const newTurn = new Turn(t.player, t.action, t.exclusivity, t.hidden, t.parallel, t.executingPlayer, value.split(this.delimiter), t.id);
this.props.onValueChange(newTurn, this.props.index);
}

private setAllCategoriesAsAllowed() {
const categories = [...new Set(this.props.preset?.options.map(value => value.category))].sort();
const t = this.props.turn;
const newTurn = new Turn(t.player, t.action, t.exclusivity, t.hidden, t.parallel, t.executingPlayer, categories, t.id);
this.props.onValueChange(newTurn, this.props.index);
}
}

export function mapStateToProps(state: ApplicationState) {
return {
preset: state.presetEditor.editorPreset,
}
}

export function mapDispatchToProps(dispatch: Dispatch<ReducerAction>) {
return {
onValueChange: (turn: Turn, index: number) => dispatch(setEditorTurn(turn, index)),
}
}

export default connect(mapStateToProps, mapDispatchToProps)(TurnCategoriesInput);
2 changes: 2 additions & 0 deletions src/constants/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ export enum Actions {
SET_EDITOR_TURN_ORDER = 'SET_EDITOR_TURN_ORDER',
SET_EDITOR_NAME = 'SET_EDITOR_NAME',
SET_EDITOR_DRAFT_OPTIONS = 'SET_EDITOR_DRAFT_OPTIONS',
SET_EDITOR_CATEGORY_LIMIT_PICK = 'SET_EDITOR_CATEGORY_LIMIT_PICK',
SET_EDITOR_CATEGORY_LIMIT_BAN = 'SET_EDITOR_CATEGORY_LIMIT_BAN',
SET_DRAFT_EVENTS = 'SET_DRAFT_EVENTS',
SET_COLOR_SCHEME = 'SET_COLOR_SCHEME',
SET_HIGHLIGHTED_ACTION = 'SET_HIGHLIGHTED_ACTION',
Expand Down
12 changes: 11 additions & 1 deletion src/languages/de_DE.json
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,9 @@
"VLD_913": "Admin-Züge können nicht versteckt sein",
"VLD_914": "Nur Admin-Züge dürfen vorherige versteckte Züge aufdecken",
"VLD_915": "Die Beobachterinnenrolle darf keine Züge ausführen",
"VLD_916": "In den erlaubten Kategorien für einen Zug befindet sich eine Kategorie, welche nicht in den Draft-Optionen vorkommt",
"VLD_917": "Eine Draft-Option hat eine Kategorie, die in keinem Zug vorkommt",
"VLD_918": "In den Kategorie-Limits ist ein Limit für eine Kategorie definiert, welche in keinem Zug vorkommt",
"VLD_999": "Irgendetwas ist bei der Validierung gehörig schief gelaufen"
},
"menu": {
Expand Down Expand Up @@ -327,6 +330,12 @@
"aoe4Civs": "AoE4-Zivilisationen",
"customOptions": "Individuell",
"turns": "Züge",
"turnCategories": "Kategorien",
"turnCategoriesAll": "alle",
"categoryLimits": "Kategorie-Limits",
"categoryLimitsExplanation": "Hier kannst du für jede Kategorie die maximale Anzahl von Picks oder Bans über alle Optionen der Kategorie hinweg definieren. Lasse das Eingabefeld leer, um für eine Kategorie kein Limit zu setzen.",
"categoryLimitsPick": "Pick",
"categoryLimitsBan": "Ban",
"createSaveDraft": "Draft erstellen oder speichern",
"host": "Host",
"guest": "Gast",
Expand All @@ -340,7 +349,8 @@
"unitImageUrl": "URL der Einheitengrafik",
"emblemImageUrl": "URL der Emblem-Grafik",
"animatedUnitImageUrlLeft": "URL der animierten Einheitengrafik (links)",
"animatedUnitImageUrlRight": "URL der animierten Einheitengrafik (rechts)"
"animatedUnitImageUrlRight": "URL der animierten Einheitengrafik (rechts)",
"categoryName": "Kategorie"
}
},
"createNewDraft": "Neuen Draft mit diesem Preset erstellen",
Expand Down
13 changes: 12 additions & 1 deletion src/languages/en_GB.json
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,9 @@
"VLD_913": "Admin turns cannot be hidden",
"VLD_914": "Only Admin turns can use the reveal action",
"VLD_915": "Turns must not involve the specator role",
"VLD_916": "There is a category in the allowed categories for a Turn which is not the category of any Draft Option",
"VLD_917": "There is a Draft Option which has a category that cannot be used in any Turn",
"VLD_918": "The Category Limits contain a category that cannot be used in any Turn",
"VLD_999": "Something went very wrong during validation"
},
"menu": {
Expand Down Expand Up @@ -320,12 +323,19 @@
"presetEditor": {
"helpAndInstructions": "Help and Instructions",
"availableDraftOptions": "Available Draft Options",
"aoe1Civs": "AoE1 civs",
"aoe2Civs": "AoE2 civs",
"aoe2Maps": "AoE2 maps",
"aoe3Civs": "AoE3 civs",
"aoe4Civs": "AoE4 civs",
"customOptions": "Custom",
"turns": "Turns",
"turnCategories": "Categories",
"turnCategoriesAll": "all",
"categoryLimits": "Category Limits",
"categoryLimitsExplanation": "Here you may, for each category, define a maximum number of times Draft Options from it can be picked or banned. Leave the input emtpy to set no limit for a category.",
"categoryLimitsPick": "Pick",
"categoryLimitsBan": "Ban",
"createSaveDraft": "Create Draft or Save",
"host": "Host",
"guest": "Guest",
Expand All @@ -339,7 +349,8 @@
"unitImageUrl": "Unit image URL",
"emblemImageUrl": "Emblem image URL",
"animatedUnitImageUrlLeft": "Animated unit image URL (left)",
"animatedUnitImageUrlRight": "Animated unit image URL (right)"
"animatedUnitImageUrlRight": "Animated unit image URL (right)",
"categoryName": "Category"
}
},
"createNewDraft": "Create new draft from this Preset",
Expand Down
6 changes: 3 additions & 3 deletions src/models/Turn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ class Turn {
public readonly id: string;
public readonly categories: string[];

constructor(player: Player, action: Action, exclusivity: Exclusivity, hidden: boolean = false, parallel: boolean = false, executingPlayer: Player = player, categories: string[] = ['default']) {
this.id = uuidv4();
constructor(player: Player, action: Action, exclusivity: Exclusivity, hidden: boolean = false, parallel: boolean = false, executingPlayer: Player = player, categories: string[] = ['default'], id: string = uuidv4()) {
this.id = id;
this.player = player;
this.action = action;
this.exclusivity = exclusivity;
Expand All @@ -70,7 +70,7 @@ class Turn {
Assert.isBoolean(turn.hidden);
Assert.isBoolean(turn.parallel);
Assert.isOptionalStringArray(turn.categories);
retval.push(new Turn(turn.player, turn.action, turn.exclusivity, turn.hidden, turn.parallel, turn.executingPlayer, turn.categories));
retval.push(new Turn(turn.player, turn.action, turn.exclusivity, turn.hidden, turn.parallel, turn.executingPlayer, turn.categories, turn.id));
}
return retval;
}
Expand Down
Loading

0 comments on commit ec0f24c

Please sign in to comment.