Skip to content

Commit

Permalink
Merge pull request #120 from cohstats/feat/faction-and-units-pages-im…
Browse files Browse the repository at this point in the history
…prov

Faction and Units Pages Improv
  • Loading branch information
petrvecera authored Mar 22, 2023
2 parents 6cfafdb + a146e18 commit f5c9467
Show file tree
Hide file tree
Showing 17 changed files with 1,112 additions and 181 deletions.
190 changes: 190 additions & 0 deletions components/unit-cards/battlegroup-card.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
import { Stack, Card, Divider, Grid, Title, Accordion, Box } from "@mantine/core";
import { raceType } from "../../src/coh3/coh3-types";
import {
BattlegroupsType,
AbilitiesType,
UpgradesType,
resolveBattlegroupBranches,
BattlegroupResolvedBranchType,
} from "../../src/unitStats";
import { UnitUpgradeCard } from "./unit-upgrade-card";

function groupBy<T>(arr: T[], fn: (item: T) => any) {
return arr.reduce<Record<string, T[]>>((prev, curr) => {
const groupKey = fn(curr);
const group = prev[groupKey] || [];
group.push(curr);
return { ...prev, [groupKey]: group };
}, {});
}

const BattlegroupBranchMapping = (branch: BattlegroupResolvedBranchType) => {
const groupedRows = groupBy(branch.upgrades, (item) => item.upg.uiPosition.row);
// Create a series of grid elements per row.
return (
<Stack align="center">
<Title order={4} color="orange.5" transform="uppercase">
{branch.name}
</Title>

{Object.entries(groupedRows).map(([rowIndex, branchUpgrades]) => {
return (
<Grid
key={`${rowIndex}_${branch.name}`}
columns={branchUpgrades.length}
// sx={{ width: branchUpgrades.length === 1 ? "" : "100%" }}
w="100%"
>
{branchUpgrades.map(({ upg, ability }) => (
<Grid.Col key={upg.id} span={1}>
<Box
p="sm"
sx={(theme) => ({
borderRadius: theme.radius.md,
borderWidth: 2,
borderStyle: "solid",
borderColor:
theme.colorScheme === "dark" ? theme.colors.dark[5] : theme.colors.gray[2],
})}
>
<UnitUpgradeCard
id={upg.id}
desc={{
screen_name: upg.ui.screenName,
help_text: upg.ui.helpText,
extra_text: upg.ui.extraText,
brief_text: upg.ui.briefText,
icon_name: upg.ui.iconName,
}}
time_cost={{
manpower: ability.cost.manpower,
munition: ability.cost.munition,
fuel: ability.cost.fuel,
popcap: ability.cost.popcap,
time_seconds: ability.rechargeTime,
command: upg.cost.command,
}}
cfg={{ compact: true }}
></UnitUpgradeCard>
</Box>
</Grid.Col>
))}
</Grid>
);
})}
</Stack>
);
};

/** Battlegroup icons are not 1:1 and the current ones make use of re-used assets. */
const BattlegroupIcons = {
dak: {
armored_support: "races/afrika_corps/battlegroups/armored_ak_ger_portrait",
italian_combined_arms: "races/afrika_corps/battlegroups/combined_arms_ak_portrait",
italian_infantry: "races/afrika_corps/battlegroups/italian_infantry_ak_portrait",
} as Record<BattlegroupAfricaKorpsIds, string>,
german: {
breakthrough: "races/german/battlegroups/breakthrough_ger_portrait",
luftwaffe: "races/german/battlegroups/luftwaffe_ger_portrait",
mechanized: "races/german/battlegroups/mechanized_ger_ger_portrait",
} as Record<BattlegroupGermanIds, string>,
american: {
airborne: "races/american/battlegroups/paratroopers_us_portrait",
armored: "races/american/battlegroups/armored_us_portrait",
special_operations: "races/american/battlegroups/spec_ops_us_portrait",
} as Record<BattlegroupAmericanIds, string>,
british: {
british_air_and_sea: "races/british/battlegroups/air_and_sea_uk_portrait",
british_armored: "races/british/battlegroups/armored_uk_portrait",
indian_artillery: "races/british/battlegroups/indian_artillery_uk_portrait",
} as Record<BattlegroupBritishIds, string>,
} as const;

type BattlegroupAfricaKorpsIds = "armored_support" | "italian_combined_arms" | "italian_infantry";
type BattlegroupGermanIds = "breakthrough" | "luftwaffe" | "mechanized";
type BattlegroupBritishIds = "british_air_and_sea" | "british_armored" | "indian_artillery";
type BattlegroupAmericanIds = "airborne" | "armored" | "special_operations";

function getBattlegroupIcon(id: string, race: raceType) {
switch (race) {
case "dak":
return BattlegroupIcons.dak[id as BattlegroupAfricaKorpsIds];
case "german":
return BattlegroupIcons.german[id as BattlegroupGermanIds];
case "american":
return BattlegroupIcons.american[id as BattlegroupAmericanIds];
case "british":
return BattlegroupIcons.british[id as BattlegroupBritishIds];
}
}

export const BattlegroupCard = (
race: raceType,
data: {
battlegroupData: BattlegroupsType[];
abilitiesData: AbilitiesType[];
upgradesData: UpgradesType[];
},
) => {
const resolvedBattlegroups = resolveBattlegroupBranches(
race,
data.battlegroupData,
data.upgradesData,
data.abilitiesData,
);
return (
<Stack>
{resolvedBattlegroups.map(({ id, uiParent, branches }) => {
return (
<Card key={id} p="sm" radius="md" withBorder>
{/* Header Section */}
<UnitUpgradeCard
id={id}
desc={{
screen_name: uiParent.screenName,
help_text: "",
extra_text: "",
brief_text: uiParent.briefText,
icon_name: getBattlegroupIcon(id, race),
}}
time_cost={{
fuel: undefined,
munition: undefined,
manpower: undefined,
popcap: undefined,
time_seconds: undefined,
}}
></UnitUpgradeCard>

{/* Branches Section */}
<Divider my={12} size="md"></Divider>

<Grid columns={2} gutter={0}>
<Grid.Col md={1}>
<Accordion p={0} chevronPosition="right">
<Accordion.Item value="left_branch">
<Accordion.Control>
<Title order={4}>{branches.LEFT.name}</Title>
</Accordion.Control>
<Accordion.Panel>{BattlegroupBranchMapping(branches.LEFT)}</Accordion.Panel>
</Accordion.Item>
</Accordion>
</Grid.Col>

<Grid.Col md={1}>
<Accordion p={0} chevronPosition="right">
<Accordion.Item value="right_branch">
<Accordion.Control>
<Title order={4}>{branches.RIGHT.name}</Title>
</Accordion.Control>
<Accordion.Panel>{BattlegroupBranchMapping(branches.RIGHT)}</Accordion.Panel>
</Accordion.Item>
</Accordion>
</Grid.Col>
</Grid>
</Card>
);
})}
</Stack>
);
};
29 changes: 18 additions & 11 deletions components/unit-cards/building-description-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ import {
} from "@mantine/core";
import { UnitDescription, UnitDescriptionCard } from "./unit-description-card";
import { UnitUpgrade, UnitUpgradeCard } from "./unit-upgrade-card";
import { ResourceValues, StatsCosts } from "./cost-card";
import { StatsCosts } from "./cost-card";
import { BuildingType } from "../../src/coh3";
import { hasCost, ResourceValues } from "../../src/unitStats";

type BuildingDescription = {
/** Locstring value. Found at `screen_name/locstring/value`. */
Expand Down Expand Up @@ -103,15 +104,21 @@ const BuildingCardHeader = (
<Text ml={24}>{health.hitpoints}</Text>
</Flex>

<Divider />

<StatsCosts
manpower={cost.manpower}
fuel={cost.fuel}
time_seconds={cost.time_seconds}
munition={cost.munition}
popcap={cost.popcap}
></StatsCosts>
{hasCost(cost) ? (
<>
<Divider />
<StatsCosts
manpower={cost.manpower}
fuel={cost.fuel}
time_seconds={cost.time_seconds}
munition={cost.munition}
popcap={cost.popcap}
command={cost.command}
></StatsCosts>
</>
) : (
<></>
)}
</Stack>
</Grid.Col>
</Grid>
Expand Down Expand Up @@ -210,7 +217,7 @@ export const BuildingCard = ({ desc, units, time_cost, health, upgrades }: Build

<Divider mt={8}></Divider>

<Accordion chevronPosition="right">
<Accordion multiple chevronPosition="right">
{productionSection}
{upgradeSection}
</Accordion>
Expand Down
28 changes: 4 additions & 24 deletions components/unit-cards/cost-card.tsx
Original file line number Diff line number Diff line change
@@ -1,34 +1,14 @@
import { Flex, Image, Stack, Text, Title } from "@mantine/core";
import { Fragment } from "react";

/**
* These fields can be found at `ebps` / `upgrade` inside each entity object.
*
* - For the units, look at `ebps` -> `exts` -> `template_reference` which gives
* the group within the Essence editor as `ebpextensions\\cost_ext`.
* - For the upgrades, look at `upgrade` -> `upgrade_bag`.
*
* For both entities, the field is called `time_cost`.
*/
export type ResourceValues = {
/** Value at `cost/fuel`. */
fuel?: number;
/** Value at `cost/munition`. */
munition?: number;
/** Value at `cost/munition`. */
manpower?: number;
/** Value at `cost/popcap`. */
popcap?: number;
/** Value at `time_seconds`. */
time_seconds?: number;
};
import { ResourceValues } from "../../src/unitStats";

const ResourceIcons = [
{ icon: "/icons/common/resources/resource_buildtime_extra.png", type: "time_seconds" },
{ icon: "/icons/common/resources/resource_manpower.png", type: "manpower" },
{ icon: "/icons/common/resources/resource_munition.png", type: "munition" },
{ icon: "/icons/common/resources/resource_fuel.png", type: "fuel" },
{ icon: "/icons/common/resources/resource_population.png", type: "popcap" },
{ icon: "/icons/common/resources/resource_skill_points.png", type: "command" },
] as const;

export const StatsCosts = (costs: ResourceValues) => (
Expand All @@ -37,11 +17,11 @@ export const StatsCosts = (costs: ResourceValues) => (
<Title order={6} transform="uppercase">
Costs
</Title>
<Flex key="stats_costs_list" align="center" gap={16} mt={4}>
<Flex key="stats_costs_list" align="center" gap={8} mt={4} wrap="wrap">
{ResourceIcons.map(({ icon, type }) => {
return costs[type] ? (
<Flex key={type} direction="row" align="center" gap={4}>
<Image height={24} width={24} fit="contain" src={icon} alt="Test text" />
<Image height={20} width={20} fit="contain" src={icon} alt="Test text" />
<Text>{costs[type]}</Text>
</Flex>
) : (
Expand Down
Loading

0 comments on commit f5c9467

Please sign in to comment.