diff --git a/assets/i18n/en-US.json b/assets/i18n/en-US.json index 254c051..c3124d8 100644 --- a/assets/i18n/en-US.json +++ b/assets/i18n/en-US.json @@ -38,6 +38,7 @@ "Boss Battle": "Boss Battle", "Battle Avoid": "Battle Avoid", "Air Strike": "Air Strike", + "Air Defense": "Air Defense", "Escort Success": "Escort Success", "Transport Munitions": "Transport Munitions", "Manual Selection": "Manual Selection", diff --git a/assets/i18n/ko-KR.json b/assets/i18n/ko-KR.json index 8943bfb..c7cc42d 100644 --- a/assets/i18n/ko-KR.json +++ b/assets/i18n/ko-KR.json @@ -37,6 +37,7 @@ "Boss Battle": "보스전", "Battle Avoid": "기분탓", "Air Strike": "항공전", + "Air Defense": "방공", "Escort Success": "호위 성공", "Transport Munitions": "수송 물자", "Manual Selection": "능동 분기", diff --git a/src/views/battle-view-area.tsx b/src/views/battle-view-area.tsx index 94829f5..0d78e1f 100644 --- a/src/views/battle-view-area.tsx +++ b/src/views/battle-view-area.tsx @@ -1,8 +1,9 @@ import FontAwesome from 'react-fontawesome' -import React, { FC } from 'react' +import React, { FC, useEffect, useState } from 'react' import { createSelector } from 'reselect' import _ from 'lodash' import { useSelector } from 'react-redux' +import { Collapse } from '@blueprintjs/core' import { Tooltip } from 'views/components/etc/overlay' import { useTranslation } from 'react-i18next' import styled from 'styled-components' @@ -96,6 +97,31 @@ const CombatVS = styled.div<{ visible?: boolean }>` opacity: ${({ visible }) => (visible ? 1 : 0)}; ` +const AirRaidPanel = styled.div` + margin-bottom: 6px; + border-bottom: 1px solid rgba(128, 128, 128, 0.4); +` + +const AirRaidHeader = styled.div<{ isOpen?: boolean }>` + display: flex; + align-items: center; + cursor: pointer; + user-select: none; + line-height: 24px; + opacity: 0.8; + + &:hover { + opacity: 1; + } + + /* one icon rotated, so the label does not shift when toggling */ + .svg-inline--fa { + margin-right: 1ex; + width: 1ex; + transform: rotate(${({ isOpen }) => (isOpen ? 90 : 0)}deg); + } +` + const inEventSelector = createSelector( [(state: PoiRootState) => state.const?.$maps], (maps = {}) => Object.keys(maps).some((mapId) => +mapId > 100), @@ -190,42 +216,58 @@ const BattleViewArea: FC = ({ const fleetName = useSelector((state: PoiRootState) => state.info?.fleets?.[fleetIds[0]]?.api_name ?? 'Sortie Fleet', ) - const friendTitle = buildFriendTitle({ - showEnemyTitle, - combinedFlag, - fleetName, - isBaseDefense, - }) - - const View = isBaseDefense ? SquadView : ShipView + const [airRaidOpen, setAirRaidOpen] = useState(true) + const toggleAirRaid = () => setAirRaidOpen((open) => !open) + + // every raid starts expanded, even if the previous one was collapsed by hand + useEffect(() => { + if (isBaseDefense) setAirRaidOpen(true) + }, [isBaseDefense, sortieMapId, currentNode]) + const times = !horizontalLayout ? 1 : 2 const fleetCount = _.sumBy([mainFleet, escortFleet], (fleet) => fleet != null ? 1 : 0) const enemyCount = _.sumBy([enemyFleet, enemyEscort], (fleet) => fleet != null ? 1 : 0) - const fleetWidth = escortFleet && !isBaseDefense ? 2 : 1 - const enemyWidth = enemyEscort && !isBaseDefense ? 2 : 1 const { getShip, getItem } = _.pick(result, ['getShip', 'getItem']) as { getShip?: number; getItem?: number } - const alliedForce = ( - - - - - ) + // `baseDefense` selects which battle is being drawn: the land base air defense + // (shown in the collapsible panel) or the sortie fleet (shown as usual below it). + // The air raid's own stats (air force, rank, formations) belong to the panel only; + // the sortie area relies on an air raid always leaving `sortieState` at Navigation, + // which keeps it on `NextSpotInfo` instead of the raid's `BattleInfo`. + const renderArea = (baseDefense: boolean): React.ReactElement => { + const friendTitle = buildFriendTitle({ + showEnemyTitle, + combinedFlag, + fleetName, + isBaseDefense: baseDefense, + }) + const View = baseDefense ? SquadView : ShipView + // air force / battle result belong to the air raid whenever there is one + const shownAirForce = baseDefense === Boolean(isBaseDefense) ? airForce : [] + const showEnemy = sortieState > SortieState.Navigation || baseDefense + const fleetWidth = escortFleet && !baseDefense ? 2 : 1 + const enemyWidth = enemyEscort && !baseDefense ? 2 : 1 + + const alliedForce = ( + + + + + ) - const enemyForce = - sortieState > SortieState.Navigation || isBaseDefense ? ( + const enemyForce = showEnemy ? ( = ({