Skip to content

Commit

Permalink
Add @typescript-eslint/array-type eslint rule and Symmetrical Drawi…
Browse files Browse the repository at this point in the history
…ng Mode (#33)

Co-authored-by: cpojer <[email protected]>
Co-authored-by: Dominik Kratz <[email protected]>
GitOrigin-RevId: 124c9db050d5697a3cddfe85615f7eff541e6bea
  • Loading branch information
cpojer and dkratz committed May 19, 2024
1 parent f273fa2 commit 5331ef7
Show file tree
Hide file tree
Showing 13 changed files with 498 additions and 108 deletions.
1 change: 1 addition & 0 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ module.exports = {
],
plugins: ['@deities'],
rules: {
'@typescript-eslint/array-type': [2, { default: 'generic' }],
'@typescript-eslint/no-restricted-imports': [
2,
{
Expand Down
2 changes: 1 addition & 1 deletion apollo/lib/gameHasEnded.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ActionResponse } from '../ActionResponse.tsx';

export default function gameHasEnded(
gameState: ReadonlyArray<readonly [ActionResponse, ...unknown[]]> | null,
gameState: ReadonlyArray<readonly [ActionResponse, ...Array<unknown>]> | null,
) {
return !!(
gameState?.length &&
Expand Down
5 changes: 4 additions & 1 deletion art/Sprites.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ type Resource = Readonly<[name: string, url: string]>;
type Resources = ReadonlyArray<Resource>;
type PaletteSwapFn = typeof paletteSwap;
type PaletteSwapParameters = Parameters<PaletteSwapFn>;
type DropFirstInTuple<T extends unknown[]> = T extends [unknown, ...infer Rest]
type DropFirstInTuple<T extends Array<unknown>> = T extends [
unknown,
...infer Rest,
]
? Rest
: never;
type MaybePaletteSwapParameters = [
Expand Down
24 changes: 19 additions & 5 deletions hera/GameMap.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ import { resetBehavior, setBaseClass } from './behavior/Behavior.tsx';
import MenuBehavior from './behavior/Menu.tsx';
import NullBehavior from './behavior/NullBehavior.tsx';
import Cursor from './Cursor.tsx';
import MapEditorExtraCursors from './editor/MapEditorMirrorCursors.tsx';
import { EditorState } from './editor/Types.tsx';
import addEndTurnAnimations from './lib/addEndTurnAnimations.tsx';
import animateSupply from './lib/animateSupply.tsx';
Expand Down Expand Up @@ -1721,12 +1722,25 @@ export default class GameMap extends Component<Props, State> {
{(propsShowCursor || propsShowCursor == null) &&
showCursor &&
!replayState.isReplaying && (
<Cursor
position={position}
size={tileSize}
zIndex={zIndex - 4}
/>
<>
<Cursor
position={position}
size={tileSize}
zIndex={zIndex - 4}
/>
{editor?.mode === 'design' && (
<MapEditorExtraCursors
color="red"
drawingMode={editor?.drawingMode}
mapSize={map.size}
origin={position}
size={tileSize}
zIndex={zIndex}
/>
)}
</>
)}

<MapAnimations
actions={this._actions}
animationComplete={this._animationComplete}
Expand Down
1 change: 1 addition & 0 deletions hera/editor/MapEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ const getEditorBaseState = (
effects = new Map([...effects, ['Start', startScenario]]);
}
return {
drawingMode: 'regular',
effects,
isDrawing: false,
isErasing: false,
Expand Down
28 changes: 28 additions & 0 deletions hera/editor/MapEditorMirrorCursors.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import Vector from '@deities/athena/map/Vector.tsx';
import { SizeVector } from '@deities/athena/MapData.tsx';
import { ComponentProps } from 'react';
import Cursor from '../Cursor.tsx';
import getSymmetricPositions from './lib/getSymmetricPositions.ts';
import { DrawingMode } from './Types.tsx';

export default function MapEditorMirrorCursors({
drawingMode,
mapSize,
origin: orign,
...props
}: {
drawingMode: DrawingMode | undefined;
mapSize: SizeVector;
origin: Vector | null;
} & Omit<ComponentProps<typeof Cursor>, 'position'>) {
if (!orign || !drawingMode || drawingMode === 'regular') {
return null;
}

const vectors = getSymmetricPositions(orign, drawingMode, mapSize);
return vectors.size
? [...vectors].map((vector) => (
<Cursor {...props} key={vector.toString()} position={vector} />
))
: null;
}
7 changes: 7 additions & 0 deletions hera/editor/Types.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,16 @@ type UndoKey =

export type UndoEntry = readonly [UndoKey, MapData];
export type UndoStack = ReadonlyArray<UndoEntry>;
export type DrawingMode =
| 'regular'
| 'horizontal'
| 'vertical'
| 'horizontal-vertical'
| 'diagonal';

export type EditorState = Readonly<{
condition?: readonly [WinConditionsWithVectors, number];
drawingMode: DrawingMode;
effects: Effects;
isDrawing: boolean;
isErasing: boolean;
Expand Down
69 changes: 57 additions & 12 deletions hera/editor/behavior/DesignBehavior.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import verifyTiles from '@deities/athena/lib/verifyTiles.tsx';
import Building from '@deities/athena/map/Building.tsx';
import { getDecoratorLimit } from '@deities/athena/map/Configuration.tsx';
import Entity from '@deities/athena/map/Entity.tsx';
import { PlayerID, PlayerIDs } from '@deities/athena/map/Player.tsx';
import Unit from '@deities/athena/map/Unit.tsx';
import Vector from '@deities/athena/map/Vector.tsx';
import MapData from '@deities/athena/MapData.tsx';
Expand All @@ -48,6 +49,7 @@ import {
} from '../../Types.tsx';
import FlashFlyout from '../../ui/FlashFlyout.tsx';
import { FlyoutItem } from '../../ui/Flyout.tsx';
import getSymmetricPositions from '../lib/getSymmetricPositions.ts';
import updateUndoStack from '../lib/updateUndoStack.tsx';
import { EditorState } from '../Types.tsx';

Expand Down Expand Up @@ -162,7 +164,11 @@ export default class DesignBehavior {
subVector?: Vector,
): StateLike | null {
if (editor?.isDrawing && editor.selected) {
return this.put(vector, state, actions, editor);
const vectors = [
vector,
...getSymmetricPositions(vector, editor.drawingMode, state.map.size),
];
return this.draw(vectors, state, actions, editor);
}

const { animations, map } = state;
Expand Down Expand Up @@ -228,11 +234,46 @@ export default class DesignBehavior {
return null;
}

private draw(
vectors: Array<Vector>,
state: State,
actions: Actions,
editor: EditorState,
): StateLike | null {
let newState: StateLike | null = null;
const players = Array.from(
new Set([...state.map.active, ...PlayerIDs.filter((id) => id !== 0)]),
).slice(0, vectors.length);
vectors.forEach((vector, index) => {
const currentPlayerIndex = players.indexOf(
state.map.getCurrentPlayer().id,
);
const playerId =
players[
((currentPlayerIndex >= 0 ? currentPlayerIndex : 0) + index) %
players.length
];

newState = {
...newState,
...this.put(
vector,
{ ...state, ...newState },
actions,
editor,
playerId,
),
};
});
return newState;
}

private put(
vector: Vector,
state: State,
actions: Actions,
editor: EditorState,
playerId: PlayerID,
): StateLike | null {
if (shouldPlaceDecorator(editor)) {
return null;
Expand Down Expand Up @@ -299,7 +340,14 @@ export default class DesignBehavior {
);
} else if (selected.unit) {
this.previous = null;
return this.putUnit(selected.unit, vector, state, actions, editor);
return this.putUnit(
selected.unit,
vector,
state,
actions,
editor,
playerId,
);
} else if (selected.building) {
this.previous = null;
return this.putBuilding(
Expand All @@ -308,6 +356,7 @@ export default class DesignBehavior {
state,
actions,
editor,
playerId,
);
}
return null;
Expand Down Expand Up @@ -503,6 +552,7 @@ export default class DesignBehavior {
state: State,
actions: Actions,
editor: EditorState,
playerId: PlayerID,
): StateLike | null {
const { map } = state;
const { units } = map;
Expand All @@ -521,12 +571,7 @@ export default class DesignBehavior {
...spawn(
actions,
state,
[
[
vector,
unit.removeLeader().setPlayer(map.getCurrentPlayer().id),
],
],
[[vector, unit.removeLeader().setPlayer(playerId)]],
null,
({ map }) => {
updateUndoStack(actions, editor, [
Expand All @@ -551,6 +596,7 @@ export default class DesignBehavior {
state: State,
actions: Actions,
editor: EditorState,
playerId: PlayerID,
): StateLike | null {
const { animations, map } = state;
const { buildings, units } = map;
Expand All @@ -568,14 +614,13 @@ export default class DesignBehavior {
const config = map.config.copy({
blocklistedBuildings: new Set(),
});
const player = map.getCurrentPlayer();
const isAlwaysNeutral = building.info.isStructure();

const tryToPlaceBuilding = (state: State): StateLike | null => {
let { map } = state;
map = map.copy({
active: getActivePlayers(map),
buildings: map.buildings.set(vector, building.setPlayer(player.id)),
buildings: map.buildings.set(vector, building.setPlayer(playerId)),
});

const { editorPlaceOn, placeOn } = building.info.configuration;
Expand Down Expand Up @@ -618,7 +663,7 @@ export default class DesignBehavior {
return canBuild(
getTemporaryMapForBuilding(temporaryMap, vector, building),
building.info,
isAlwaysNeutral ? 0 : player,
isAlwaysNeutral ? 0 : playerId,
vector,
true,
) && !(building.info.isHQ() && map.currentPlayer === 0)
Expand All @@ -636,7 +681,7 @@ export default class DesignBehavior {
return newState;
},
type: 'createBuilding',
variant: isAlwaysNeutral ? 0 : player.id,
variant: isAlwaysNeutral ? 0 : playerId,
}),
map: state.map.copy({ buildings: buildings.delete(vector) }),
}
Expand Down
Loading

0 comments on commit 5331ef7

Please sign in to comment.