Skip to content

Commit

Permalink
Fix a lot of issues around order entry and coasts
Browse files Browse the repository at this point in the history
  • Loading branch information
Oliveriver committed Aug 7, 2024
1 parent 2c51858 commit 109c6dc
Show file tree
Hide file tree
Showing 14 changed files with 270 additions and 92 deletions.
22 changes: 22 additions & 0 deletions client/src/components/user-interface/common/Checkbox.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import colours from '../../../utils/colours';

type CheckboxProps = {
title: string;
value: boolean;
onChange: (value: boolean) => void;
};

const Checkbox = ({ title, value, onChange }: CheckboxProps) => (
<div className="flex flex-col items-center gap-2 ml-4">
<p className="text-sm">{title}</p>
<button
type="button"
aria-label={title}
className="w-6 h-6 rounded border-4"
style={{ backgroundColor: value ? colours.uiHighlight : colours.uiOverlay }}
onClick={() => onChange(!value)}
/>
</div>
);

export default Checkbox;
10 changes: 4 additions & 6 deletions client/src/components/world/boards/Board.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,11 @@ import colours from '../../../utils/colours';

type BoardProps = {
board: BoardData;
isActive: boolean;
winner: Nation | null;
};

const Board = ({ board, isActive, winner }: BoardProps) => {
const Board = ({ board, winner }: BoardProps) => {
const { phase } = board;
const showWinner = isActive && winner;

const width = phase === Phase.Winter ? minorBoardWidth : majorBoardWidth;

Expand All @@ -41,12 +39,12 @@ const Board = ({ board, isActive, winner }: BoardProps) => {
minHeight: width,
height: width,
borderWidth: boardBorderWidth,
borderColor: showWinner ? getNationColour(winner) : colours.boardBorder,
boxShadow: showWinner ? `0px 0px 100px 50px ${getNationColour(winner)}` : '',
borderColor: winner ? getNationColour(winner) : colours.boardBorder,
boxShadow: winner ? `0px 0px 100px 50px ${getNationColour(winner)}` : '',
}}
>
<p className="text-md -mt-7">{getBoardName(board)}</p>
<Map board={board} isActive={isActive && !winner} />
<Map board={board} />
</div>
{phase === Phase.Winter && <Adjustment board={board} />}
</div>
Expand Down
13 changes: 11 additions & 2 deletions client/src/components/world/boards/BoardGhost.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,26 @@ import Map from './Map';
import TextInput from '../../user-interface/common/TextInput';
import Select from '../../user-interface/common/Select';
import { isInteger } from '../../../utils/numberUtils';
import Checkbox from '../../user-interface/common/Checkbox';

type LocationInputProps = {
board: Board;
onTimelineEntered: (value: string) => void;
onYearEntered: (value: string) => void;
onPhaseEntered: (value: Phase) => void;
isShowingCoasts: boolean;
onCoastsToggled: (value: boolean) => void;
};

const LocationInput = ({
board,
onTimelineEntered,
onYearEntered,
onPhaseEntered,
isShowingCoasts,
onCoastsToggled,
}: LocationInputProps) => (
<div className="absolute -mt-20 flex flex-row gap-4 w-full justify-center">
<div className="absolute -mt-20 flex flex-row gap-4 w-full justify-center items-center">
<TextInput placeholder="Timeline" onChange={onTimelineEntered} />
<Select
options={[
Expand All @@ -38,6 +43,7 @@ const LocationInput = ({
setValue={onPhaseEntered}
/>
<TextInput placeholder="Year" onChange={onYearEntered} />
<Checkbox title="Show coasts" value={isShowingCoasts} onChange={onCoastsToggled} />
</div>
);

Expand All @@ -51,6 +57,7 @@ const BoardGhost = ({ initialTimeline, initialYear, initialPhase }: BoardGhostPr
const [timeline, setTimeline] = useState(initialTimeline);
const [year, setYear] = useState(initialYear);
const [phase, setPhase] = useState(initialPhase);
const [isShowingCoasts, setIsShowingCoasts] = useState(false);

const board = {
timeline,
Expand Down Expand Up @@ -95,6 +102,8 @@ const BoardGhost = ({ initialTimeline, initialYear, initialPhase }: BoardGhostPr
onTimelineEntered={onTimelineEntered}
onYearEntered={onYearEntered}
onPhaseEntered={setPhase}
isShowingCoasts={isShowingCoasts}
onCoastsToggled={setIsShowingCoasts}
/>
<div
className="relative rounded-xl"
Expand All @@ -109,7 +118,7 @@ const BoardGhost = ({ initialTimeline, initialYear, initialPhase }: BoardGhostPr
}}
>
<p className="text-md -mt-7">{getBoardName(board)}</p>
<Map board={board} isActive={false} />
<Map board={board} isShowingCoasts={isShowingCoasts} />
</div>
</div>
);
Expand Down
19 changes: 2 additions & 17 deletions client/src/components/world/boards/BoardLayer.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useContext } from 'react';
import { getNextMajorPhase, getNextPhase } from '../../../types/enums/phase';
import { getNextMajorPhase } from '../../../types/enums/phase';
import { filterUnique } from '../../../utils/listUtils';
import Board from './Board';
import BoardSkip from './BoardSkip';
Expand All @@ -17,9 +17,6 @@ const BoardLayer = () => {
const boards = filterUnique(world.boards.map(({ year, phase }) => ({ year, phase })));

const { winner } = world;
const hasRetreats = world.boards.some((board) =>
Object.values(board.units).some((unit) => unit.mustRetreat),
);

const selectedLocation = currentOrder?.location;
const showGhostBoard =
Expand All @@ -46,21 +43,9 @@ const BoardLayer = () => {
);

const key = `${board.year}-${board.phase}`;
const nextBoard = getNextPhase(year, phase);
const hasNextBoard = world.boards.find(
(otherBoard) =>
otherBoard.timeline === timeline &&
otherBoard.year === nextBoard.year &&
otherBoard.phase === nextBoard.phase,
);

return possibleBoard ? (
<Board
key={key}
board={possibleBoard}
isActive={!hasNextBoard && !hasRetreats}
winner={winner}
/>
<Board key={key} board={possibleBoard} winner={winner} />
) : (
<BoardSkip key={key} board={{ ...board, timeline }} />
);
Expand Down
34 changes: 23 additions & 11 deletions client/src/components/world/boards/Map.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,18 @@ import regions from '../../../data/regions';
import OrderEntryContext from '../../context/OrderEntryContext';
import UnitType from '../../../types/enums/unitType';
import InputMode from '../../../types/enums/inputMode';
import { OrderType } from '../../../types/order';
import WorldContext from '../../context/WorldContext';
import { findUnit } from '../../../types/world';
import Phase from '../../../types/enums/phase';

type MapProps = {
board: Board;
isActive: boolean;
isShowingCoasts?: boolean;
};

const Map = ({ board, isActive }: MapProps) => {
const Map = ({ board, isShowingCoasts }: MapProps) => {
const { world } = useContext(WorldContext);
const { currentMode, currentOrder } = useContext(OrderEntryContext);

return (
Expand All @@ -30,17 +35,25 @@ const Map = ({ board, isActive }: MapProps) => {
phase={board.phase}
owner={board.centres[region]}
unit={board.units[region]}
isActive={isActive}
/>
);
}

// TODO fix for fleet supporting army on space with coasts and army supporting fleet to coast
const showCoast =
currentOrder?.unit?.type !== UnitType.Army &&
((currentOrder?.unit?.type === UnitType.Fleet && currentMode !== InputMode.Convoy) ||
board.units[region] !== undefined ||
currentMode === InputMode.Build);
const hasFleet = board.units[region]?.type === UnitType.Fleet;

const showCoast = {
[InputMode.None]: hasFleet || board.phase === Phase.Winter,
[InputMode.Hold]: hasFleet,
[InputMode.Move]: currentOrder?.unit?.type === UnitType.Fleet,
[InputMode.Support]:
currentOrder?.$type === OrderType.Support &&
((!currentOrder.supportLocation && hasFleet) ||
(currentOrder.supportLocation !== null &&
findUnit(world, currentOrder.supportLocation)?.type === UnitType.Fleet)),
[InputMode.Convoy]: hasFleet,
[InputMode.Build]: true,
[InputMode.Disband]: hasFleet,
}[currentMode];

return (
<Region
Expand All @@ -51,8 +64,7 @@ const Map = ({ board, isActive }: MapProps) => {
phase={board.phase}
owner={board.centres[baseRegion]}
unit={board.units[region]}
isActive={isActive}
isVisible={showCoast}
isVisible={showCoast || isShowingCoasts}
/>
);
})}
Expand Down
53 changes: 16 additions & 37 deletions client/src/components/world/boards/Region.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useContext, useState } from 'react';
import { useContext, useEffect, useState } from 'react';
import Nation, { getNationColour } from '../../../types/enums/nation';
import OrderEntryContext from '../../context/OrderEntryContext';
import { OrderEntryActionType } from '../../../types/context/orderEntryAction';
Expand All @@ -11,9 +11,9 @@ import UnitIcon from './UnitIcon';
import { compareLocations, getLocationKey } from '../../../types/location';
import { OrderType } from '../../../types/order';
import BuildOptions from '../../user-interface/BuildOption';
import InputMode from '../../../types/enums/inputMode';
import colours from '../../../utils/colours';
import RegionType from '../../../types/enums/regionType';
import useSelectLocation from '../../../hooks/useSelectLocation';

const getRegionColour = (
isHovering: boolean,
Expand All @@ -33,42 +33,17 @@ type RegionProps = {
phase: Phase;
owner?: Nation;
unit?: Unit;
isActive: boolean;
isVisible?: boolean;
};

const Region = ({
id,
timeline,
year,
phase,
owner,
unit,
isActive,
isVisible = true,
}: RegionProps) => {
const { dispatch, currentOrder, currentMode } = useContext(OrderEntryContext);
const Region = ({ id, timeline, year, phase, owner, unit, isVisible = true }: RegionProps) => {
const { dispatch, currentOrder } = useContext(OrderEntryContext);

const location = { timeline, year, phase, region: id };
const Svg = useRegionSvg(id)!;
const { x, y, type, homeNation } = regions[id];
const { x, y, type } = regions[id];

const canRetreat = unit?.mustRetreat;
const canMove = isActive && phase !== Phase.Winter && unit !== undefined;
const canBuild =
isActive &&
phase === Phase.Winter &&
currentMode !== InputMode.Disband &&
unit === undefined &&
homeNation !== undefined &&
owner === homeNation;
const canDisband = isActive && phase === Phase.Winter && unit !== undefined;

const canCreateOrder = canRetreat || canMove || canBuild || canDisband;

const hasUnfinishedOrder =
currentOrder?.location !== undefined && currentOrder.$type !== OrderType.Build;
const canSelect = canCreateOrder || hasUnfinishedOrder;
const canSelect = useSelectLocation(location, owner, unit);

const scaleFactor =
phase === Phase.Winter
Expand All @@ -77,12 +52,16 @@ const Region = ({

const [isHovering, setIsHovering] = useState(false);

let isSelected = compareLocations(currentOrder?.location, location);
if (currentOrder?.$type === OrderType.Support) {
isSelected ||= compareLocations(currentOrder.supportLocation, location);
} else if (currentOrder?.$type === OrderType.Convoy) {
isSelected ||= compareLocations(currentOrder.convoyLocation, location);
}
useEffect(() => {
if (!isVisible) setIsHovering(false);
}, [isVisible]);

const isSelected =
compareLocations(currentOrder?.location, location) ||
(currentOrder?.$type === OrderType.Support &&
compareLocations(currentOrder.supportLocation, location)) ||
(currentOrder?.$type === OrderType.Convoy &&
compareLocations(currentOrder.convoyLocation, location));

const colour = getRegionColour(isHovering, isSelected, owner, type === RegionType.Sea);

Expand Down
2 changes: 1 addition & 1 deletion client/src/components/world/orders/Disband.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const Disband = ({ location, status, isHighlighted }: DisbandProps) => {
const radius = 10;

return (
<svg className="z-20 fixed overflow-visible pointer-events-none">
<svg className="z-30 fixed overflow-visible pointer-events-none">
<path
d={`M ${coordinates.x - radius},${coordinates.y} ${coordinates.x + radius},${coordinates.y}`}
stroke={getOrderColour(status, isHighlighted)}
Expand Down
35 changes: 28 additions & 7 deletions client/src/hooks/useOrderEntryReducer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,17 @@ const handleAdjustmentOrderCreation = (
const { player, currentMode, currentOrder } = state;
const { unit, location } = action;
const filteredOrders = state.orders.filter(
(order) => !compareLocations(order.location, action.location),
(order) => !compareLocations(order.location, action.location, true),
);
const nation = regions[location.region].homeNation;

const isPlayerUnit = unit && (!player || unit.owner === player);
const isPlayerNation = nation && (!player || nation === player);

// TODO fix builds in coasts
// Beginning a build order
if (
currentMode !== InputMode.Disband &&
(!currentOrder || !compareLocations(currentOrder.location, location))
(currentMode === InputMode.Build || (!unit && currentMode === InputMode.None)) &&
!currentOrder
) {
if (!isPlayerNation) return state;

Expand Down Expand Up @@ -63,6 +62,10 @@ const handleAdjustmentOrderCreation = (
...filteredOrders,
{
...currentOrder,
location:
action.unit?.type === UnitType.Army
? { ...currentOrder.location, region: currentOrder.location.region.split('_')[0] }
: currentOrder.location,
unit: {
owner: nation,
type: unit.type,
Expand All @@ -75,10 +78,8 @@ const handleAdjustmentOrderCreation = (
};
}

// TODO disband by default if selecting an existing unit on a winter board

// Setting a disband order
if (currentMode === InputMode.Disband) {
if (currentMode === InputMode.Disband || (unit && currentMode === InputMode.None)) {
if (!isPlayerUnit) return state;

return {
Expand Down Expand Up @@ -291,6 +292,26 @@ const handleBasicOrderCreation = (
};
}

// Setting a disband order (for retreats)
if (currentMode === InputMode.Disband && unit?.mustRetreat) {
if (!isPlayerUnit) return state;

return {
...state,
orders: [
...filteredOrders,
{
$type: OrderType.Disband,
status: OrderStatus.New,
unit,
location,
},
],
currentMode: InputMode.None,
currentOrder: null,
};
}

return state;
};

Expand Down
Loading

0 comments on commit 109c6dc

Please sign in to comment.