From 00963ddc56a18dbc9f7551f56a71c1e066e7a0a9 Mon Sep 17 00:00:00 2001 From: Anibus Date: Sat, 7 Jun 2025 23:43:02 +0300 Subject: [PATCH] Complete addons --- .env | 4 +- public/images/defence.png | Bin 0 -> 16282 bytes src/classes/Sergeant.tsx | 8 +- src/classes/Vision.tsx | 25 +++ src/classes/WeaponSlot.tsx | 15 +- src/classes/building/BuildingAddon.tsx | 262 +++++++++++++++++++++++++ src/css/Building.css | 12 +- src/pages/BuildingPage.tsx | 152 ++------------ src/pages/UnitPage.tsx | 12 +- src/types/IBuilding.tsx | 1 + src/types/IBuildingShort.tsx | 2 +- 11 files changed, 328 insertions(+), 165 deletions(-) create mode 100644 public/images/defence.png create mode 100644 src/classes/Vision.tsx create mode 100644 src/classes/building/BuildingAddon.tsx diff --git a/.env b/.env index 98e138b..0a0dc3e 100644 --- a/.env +++ b/.env @@ -1,2 +1,2 @@ -REACT_APP_HOST_URL=http://wiki-backend.dawn-of-war.pro -#REACT_APP_HOST_URL=http://localhost:8082 \ No newline at end of file +#REACT_APP_HOST_URL=http://wiki-backend.dawn-of-war.pro +REACT_APP_HOST_URL=http://localhost:8082 \ No newline at end of file diff --git a/public/images/defence.png b/public/images/defence.png new file mode 100644 index 0000000000000000000000000000000000000000..2caae5cad6b3ae05c34cfb64bd122bfdd2c1220a GIT binary patch literal 16282 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 literal 0 HcmV?d00001 diff --git a/src/classes/Sergeant.tsx b/src/classes/Sergeant.tsx index 911bc4a..dc318aa 100644 --- a/src/classes/Sergeant.tsx +++ b/src/classes/Sergeant.tsx @@ -16,6 +16,7 @@ import AvTimerOutlinedIcon from "@mui/icons-material/AvTimer"; import ArmorType from "./ArmorType"; import {ExpandMore} from "@mui/icons-material"; import WeaponSlot from "./WeaponSlot"; +import Vision from "./Vision"; @@ -27,11 +28,6 @@ const Sergeant = (props: SergeantProps) => { const sergeant = props.sergeant - const detect = sergeant.detectRadius > 0 ?   {sergeant.detectRadius} : -   - let mapWithUnitWeapons: Map> = new Map(); sergeant.weapons.forEach(weapon => { @@ -111,7 +107,7 @@ const Sergeant = (props: SergeantProps) => { > detect - {detect} + diff --git a/src/classes/Vision.tsx b/src/classes/Vision.tsx new file mode 100644 index 0000000..e6edf32 --- /dev/null +++ b/src/classes/Vision.tsx @@ -0,0 +1,25 @@ +import React from "react"; +import {Tooltip} from "@mui/material"; + +interface IVision{ + sight: number, + detect: number +} + +function Vision (vision: IVision) { + + return ( +
+ + {vision.sight} + + {vision.detect !== 0 && + {vision.detect} + } +
+ ) +} + +export default Vision; diff --git a/src/classes/WeaponSlot.tsx b/src/classes/WeaponSlot.tsx index 14a3085..37da11d 100644 --- a/src/classes/WeaponSlot.tsx +++ b/src/classes/WeaponSlot.tsx @@ -4,7 +4,8 @@ import {IWeapon} from "../types/IUnit"; export interface WeaponSlotProps { hardpoint: number, - unitWeapons: Map | undefined + unitWeapons: Map | undefined, + showOnlyDefault?: boolean , } const WeaponSlot= (props: WeaponSlotProps) => { @@ -23,6 +24,18 @@ const WeaponSlot= (props: WeaponSlotProps) => { if(onlyDummy) return (
) + if(props.showOnlyDefault){ + + if(firstWeapon.filename.includes("dummy")) return (
); + + return ( +
+

{header}

+
+
+ ) + } + return (

{header}

diff --git a/src/classes/building/BuildingAddon.tsx b/src/classes/building/BuildingAddon.tsx new file mode 100644 index 0000000..938f1e6 --- /dev/null +++ b/src/classes/building/BuildingAddon.tsx @@ -0,0 +1,262 @@ +import React from "react"; +import { + Accordion, + AccordionDetails, + AccordionSummary, + Grid2, + Paper, + Table, + TableBody, + TableCell, + TableContainer, + TableRow +} from "@mui/material"; +import {IAddonModifier, IBuilding, IBuildingAddon, IBuildingAddonShort} from "../../types/IBuilding"; +import {WeaponHardpoint} from "../../types/IUnit"; +import AvTimerOutlinedIcon from "@mui/icons-material/AvTimer"; +import WeaponSlot from "../WeaponSlot"; +import {ExpandMore} from "@mui/icons-material"; +import {IconUrl} from "../../core/api"; +import {IBuildingShort} from "../../types/IBuildingShort"; +import {goToAddon} from "../../pages/BuildingPage"; + +interface IAddonModifiersProvidesTable{ + modifiers: IAddonModifier[], +} + +function AddonModifiersProvidesTable (props: IAddonModifiersProvidesTable) { + + function getModName(mt: String){ + switch(mt) { + case 'modifiers\\armour_modifier.lua': + return 'Armor'; + case 'modifiers\\health_maximum_modifier.lua': + return 'Health'; + case 'modifiers\\keen_sight_radius_modifier.lua': + return 'Detect'; + case 'modifiers\\garrison_requisition_modifier.lua': + return 'Requisition'; + default: + return mt; + } + } + + function getModIcon(mt: String){ + switch(mt) { + case 'modifiers\\armour_modifier.lua': + return ; + case 'modifiers\\health_maximum_modifier.lua': + return ; + case 'modifiers\\keen_sight_radius_modifier.lua': + return ; + case 'modifiers\\garrison_requisition_modifier.lua': + return ; + default: + return mt; + } + } + + function getChangeDescription(ref: String) { + switch(ref) { + case 'type_modifierusagetype\\tp_mod_usage_addition.lua': + return '+'; + case 'type_modifierusagetype\\tp_mod_usage_multiplication.lua': + return 'x'; + case 'type_modifierusagetype\\tp_mod_usage_percentage.lua': + return '%'; + case 'type_modifierusagetype\\tp_mod_usage_enable.lua': + return ' enable '; + default: + return ref; + } + } + + let noWeaponMods = props.modifiers.filter(m => !m.reference.includes("default_weapon_modifier_hardpoint")) + + return ( + + + {noWeaponMods.map(m => + + {getModName(m.reference)} + {getModIcon(m.reference)} {getChangeDescription(m.usageType)}{m.value} + + )} +
+
+ ) +} + +interface IAddonModifiersProvidesWeapons{ + modifiers: IAddonModifier[], + replacedAddonModifiers: IAddonModifier[], + weapons: WeaponHardpoint[] +} + +function AddonModifiersProvidesWeapons (props: IAddonModifiersProvidesWeapons) { + + + function getWeaponByAddonMod(ar: IAddonModifier){ + + let hardpoint = Number(ar.reference.replace("modifiers\\default_weapon_modifier_hardpoint", "").replace(".lua", "")); + let value = ar.value; + + //let prevAddonWeaponMod = props.replacedAddonModifiers.find(m => m.reference && m.reference.includes("modifiers\\default_weapon_modifier_hardpoint" + hardpoint))?.value ?? 0 + + let weapon = props.weapons.find(w => w.hardpoint === hardpoint && w.hardpointOrder === 1 + value )?.weapon + if (weapon != null){ + return ; + }else{ + return "Can't find weapon, replaced by addon:" + value + " - " + } + + } + + let weaponMods = props.modifiers.filter(m => m.reference.includes("default_weapon_modifier_hardpoint")) + + return ( +
+ {weaponMods.map(w => getWeaponByAddonMod(w))} +
+ ) +} + + +interface IBuildingAddonProps { + addon: IBuildingAddon, + building: IBuilding +} + +function BuildingAddon(props: IBuildingAddonProps){ + + const addon = props.addon + const building = props.building + + + const prevAddonModifiers: IAddonModifier[] = (function() { + if (addon.addonRequirement && addon.addonRequirement.requireAddon !== null && !addon.addonRequirement.replaceWhenDone){ + return building.addons.find(a => a.id === addon.addonRequirement.requireAddon.id)?.addonModifiers ?? []; + } else return []; + })() + + function renderRequirementBuilding(requirementBuildings: IBuildingShort[], building: IBuilding) { + return requirementBuildings.length > 0 && ( + + ); + } + + function renderRequirementGlobalAddons(rgas: IBuildingAddonShort[], building: IBuilding) { + return
{rgas.map(rga => +   Global addon: +   + {rga.name} + + )}
+ } + + + return
+ } + aria-controls="panel1-content" + > + + {addon.icon && } +   {addon.name} + + + + + + + + + + Cost + + {addon.addonCostRequisition > 0 && +    + {addon.addonCostRequisition.toFixed(0)}} + {addon.addonCostPower > 0 &&    + {addon.addonCostPower.toFixed(0)}} + {(addon.addonCostPopulation !== undefined && addon.addonCostPopulation > 0) && +    + {addon.addonCostPopulation.toFixed(0)}} + {(addon.addonCostFaith !== undefined && addon.addonCostFaith > 0) && +    + {addon.addonCostFaith}} + {(addon.addonCostSouls !== undefined && addon.addonCostSouls > 0) && +    + {addon.addonCostSouls.toFixed(0)}} + {(addon.addonCostTime !== undefined && addon.addonCostTime > 0) && +    + {addon.addonCostTime}s} + + + + +
+

+ +
+ +
+ {addon.description} +
+
+ {addon.addonModifiers !== null && + + + } + {addon.addonRequirement !== null && + +

Required:

+ {addon.addonRequirement.requiredTotalPop !== undefined && addon.addonRequirement.requiredTotalPop !== null && +
  Population:   + {addon.addonRequirement.requiredTotalPop}
+ } + {addon.addonRequirement.requireAddon !== null && +
  Addon:   + goToAddon(`#addon-` + addon.addonRequirement.requireAddon.id)}>{addon.addonRequirement.requireAddon.name} + {addon.addonRequirement.replaceWhenDone && (old provides replace)} +
+ } + {addon.addonRequirement.requirementBuildings.map(b => +
  Building:   + {b.name}
) + } + {addon.addonRequirement.requirementBuildingsEither.length !== 0 && + renderRequirementBuilding(addon.addonRequirement.requirementBuildingsEither, building)} + {addon.addonRequirement.requirementsGlobalAddons.length !== 0 && + renderRequirementGlobalAddons(addon.addonRequirement.requirementsGlobalAddons, building)} +
} +
+ {addon.filename} +
+
+} + + +export default BuildingAddon; + diff --git a/src/css/Building.css b/src/css/Building.css index 961fb22..050ffc0 100644 --- a/src/css/Building.css +++ b/src/css/Building.css @@ -3,14 +3,8 @@ scroll-margin-top: 15px; } -@-webkit-keyframes blink2 { - 100% { color: rgba(34, 34, 34, 0); } -} -@keyframes blink2 { - 100% { color: rgba(34, 34, 34, 0); } -} -.selected-addon div > h3 { - -webkit-animation: blink2 1s linear infinite; - animation: blink2 1s linear infinite; + +.selected-addon div { + background-color: antiquewhite; } \ No newline at end of file diff --git a/src/pages/BuildingPage.tsx b/src/pages/BuildingPage.tsx index e71eb83..e554716 100644 --- a/src/pages/BuildingPage.tsx +++ b/src/pages/BuildingPage.tsx @@ -1,29 +1,18 @@ -import {AvailableBuildings, AvailableMods, AvailableUnits, IconUrl} from "../core/api"; -import React, {MutableRefObject, useEffect, useRef} from "react"; +import {AvailableBuildings, AvailableMods, IconUrl} from "../core/api"; +import React from "react"; import {withRouter} from "../core/withrouter"; import {IWeapon} from "../types/IUnit"; import '../css/Building.css' -import { - Accordion, - AccordionDetails, - AccordionSummary, - Button, - Grid2, - Paper, - Table, - TableBody, - TableCell, - TableContainer, - TableRow -} from "@mui/material"; -import {ArrowBack, ExpandMore} from "@mui/icons-material"; +import {Button, Grid2, Paper, Table, TableBody, TableCell, TableContainer, TableRow} from "@mui/material"; +import {ArrowBack} from "@mui/icons-material"; import ArmorType from "../classes/ArmorType"; import AvTimerOutlinedIcon from '@mui/icons-material/AvTimer'; import WeaponSlot from "../classes/WeaponSlot"; import UnitsTable from "../classes/UnitsTable"; import {IMod} from "../types/Imod"; -import {IBuilding, IBuildingAddon, IBuildingAddonShort} from "../types/IBuilding"; -import {IBuildingShort} from "../types/IBuildingShort"; +import {IBuilding} from "../types/IBuilding"; +import Vision from "../classes/Vision"; +import BuildingAddon from "../classes/building/BuildingAddon"; interface UintPageState { building: IBuilding, @@ -33,10 +22,6 @@ interface UintPageState { function Building(building: IBuilding, mod: IMod) { - const detect = building.detectRadius > 0 ?   {building.detectRadius} : -   let mapBuildingWeapons: Map> = new Map(); @@ -53,117 +38,6 @@ function Building(building: IBuilding, mod: IMod) { - function BuildingAddon(addon: IBuildingAddon){ - - return
- } - aria-controls="panel1-content" - > - - {addon.icon && } -   {addon.name} - - - - - - - - - - Cost - - {addon.addonCostRequisition > 0 && -    - {addon.addonCostRequisition.toFixed(0)}} - {addon.addonCostPower > 0 &&    - {addon.addonCostPower.toFixed(0)}} - {(addon.addonCostPopulation !== undefined && addon.addonCostPopulation > 0) && -    - {addon.addonCostPopulation.toFixed(0)}} - {(addon.addonCostFaith !== undefined && addon.addonCostFaith > 0) && -    - {addon.addonCostFaith}} - {(addon.addonCostSouls !== undefined && addon.addonCostSouls > 0) && -    - {addon.addonCostSouls.toFixed(0)}} - {(addon.addonCostTime !== undefined && addon.addonCostTime > 0) && -    - {addon.addonCostTime}s} - - - - -
-
-
- -
- {addon.description} -
-
- {addon.addonRequirement !== null && - -

Required:

- {addon.addonRequirement.requiredTotalPop !== undefined && addon.addonRequirement.requiredTotalPop !== null && -
  Population:   - {addon.addonRequirement.requiredTotalPop}
- } - {addon.addonRequirement.requireAddon !== null && - - } - {addon.addonRequirement.requirementBuildings.map(b => -
  Building:   - {b.name}
) - } - {addon.addonRequirement.requirementBuildingsEither.length !== 0 && - renderRequirementBuilding(addon.addonRequirement.requirementBuildingsEither, building)} - {addon.addonRequirement.requirementsGlobalAddons.length !== 0 && - renderRequirementGlobalAddons(addon.addonRequirement.requirementsGlobalAddons, building)} -
} -
- {addon.filename} -
-
- } - - function renderRequirementBuilding(requirementBuildings: IBuildingShort[], building: IBuilding) { - return requirementBuildings.length > 0 && ( - - ); - } - - function renderRequirementGlobalAddons(rgas: IBuildingAddonShort[], building: IBuilding) { - return
{rgas.map(rga => -   Global addon: -   - {rga.name} - - )}
- } - - return (

{mod.name} ({mod.version})

@@ -227,9 +101,9 @@ function Building(building: IBuilding, mod: IMod) { - Detect + Sight - {detect} + 0 &&

Addons

{building.addons.map(b => - BuildingAddon(b) + )}
} {[...mapBuildingWeapons.keys()].sort(function (a, b) { return a - b; - }).map(h => )} + }).map(h => )} {building.filename} @@ -312,4 +186,6 @@ class BuildingPage extends React.Component { } } -export default withRouter(BuildingPage); \ No newline at end of file +export default withRouter(BuildingPage); + +export { goToAddon }; \ No newline at end of file diff --git a/src/pages/UnitPage.tsx b/src/pages/UnitPage.tsx index e1f9e97..8413f8e 100644 --- a/src/pages/UnitPage.tsx +++ b/src/pages/UnitPage.tsx @@ -23,6 +23,7 @@ import Sergeant from "../classes/Sergeant"; import WeaponSlot from "../classes/WeaponSlot"; import UnitsTable from "../classes/UnitsTable"; import {IMod} from "../types/Imod"; +import Vision from "../classes/Vision"; interface UintPageState { unit: IUnit, @@ -40,11 +41,6 @@ function Unit(unit: IUnit, mod: IMod) { : "-" - const detect = unit.detectRadius > 0 ?   {unit.detectRadius} : -   - let mapWithUnitWeapons: Map> = new Map(); unit.weapons.forEach(weapon => { @@ -214,12 +210,12 @@ function Unit(unit: IUnit, mod: IMod) { - Detect + Vision - {detect} + - {unit.repairMax !== undefined && + {unit.repairMax !== undefined && unit.repairMax !== null && Repair max diff --git a/src/types/IBuilding.tsx b/src/types/IBuilding.tsx index 9e1cf3d..60b75c7 100644 --- a/src/types/IBuilding.tsx +++ b/src/types/IBuilding.tsx @@ -55,6 +55,7 @@ export interface IAddonRequirement { requirementBuildings: IBuildingShort[]; requirementBuildingsEither: IBuildingShort[]; requirementsGlobalAddons: IBuildingAddonShort[]; + replaceWhenDone: Boolean; requireAddon: IBuildingAddonShort; requiredTotalPop?: number; } diff --git a/src/types/IBuildingShort.tsx b/src/types/IBuildingShort.tsx index f783cc0..072837b 100644 --- a/src/types/IBuildingShort.tsx +++ b/src/types/IBuildingShort.tsx @@ -10,5 +10,5 @@ export interface IBuildingShort { icon: string id: number canDetect: boolean - armourTypeName: string + armorTypeName: string }