Global update

- add requirements for units, sergeants, buildings and weapons
- add uiIndexHint for units, buildings, researches and addons
- add addon affects
- improve campaign entities clear
- add dds converter definitive edition
- add all files lowercase function before mod parse
- add addon modifiers info
- add advanced build menu grid
- add title according building/unit/mod/race
This commit is contained in:
Anibus
2025-10-11 01:34:18 +03:00
parent 851bb71601
commit f0fde93d7b
20 changed files with 216 additions and 224 deletions
+4 -14
View File
@@ -1,16 +1,6 @@
import './App.css';
import React from "react";
import {
AppBar,
Button,
Container,
createTheme,
Grid2,
IconButton,
ThemeProvider,
Toolbar,
Typography
} from "@mui/material";
import {AppBar, Container, createTheme, IconButton, ThemeProvider, Toolbar, Typography} from "@mui/material";
import {MyRoutes} from "./Routes";
function App() {
@@ -43,11 +33,11 @@ function App() {
>
</IconButton>
<Typography variant="h6" component="div" sx={{flexGrow: 1}}>
Autogeneration wiki (by Anibus)
Autogeneration wiki
</Typography>
<Button color="inherit" onClick={steamLogin}>
{/*<Button color="inherit" onClick={steamLogin}>
Log in through Steam
</Button>
</Button>*/}
</Toolbar>
</AppBar>
<Container>
+19 -17
View File
@@ -2,8 +2,9 @@ import {Paper, Table, TableCell, TableContainer, TableRow} from "@mui/material";
import React from "react";
import {IModifier} from "../types/IModifier";
import {IconUrl} from "../core/api";
import {IResearch} from "../types/IResearch";
import {Irace} from "../types/Irace";
import {IAffectedData} from "../types/IAffectedData";
import {IMod} from "../types/Imod";
interface IModifiersProvidesTable{
modifiers: IModifier[],
@@ -11,7 +12,8 @@ interface IModifiersProvidesTable{
interface IModifiersProvidesResearchTable{
modifiers: IModifier[],
research: IResearch,
affectedData: IAffectedData,
mod: IMod,
race: Irace,
}
@@ -130,43 +132,43 @@ export function ModifiersProvidesAddonTable (props: IModifiersProvidesTable) {
)
}
export function ModifiersProvidesResearchTable (props: IModifiersProvidesResearchTable) {
export function ModifiersProvidesTable (props: IModifiersProvidesResearchTable) {
let mods = props.modifiers
function getTarget(target: String, research: IResearch, race: Irace) {
console.log(research.affectedUnits.map(au => au.filename))
var affectedUnit = research.affectedUnits.find(au => au.filename.replaceAll('.rgd', '').split(";")[0] === target || au.filename.replaceAll('.rgd', '').split(";")[1] === target);
function getTarget(target: String) {
console.log(props.affectedData.affectedUnits.map(au => au.filename))
var affectedUnit = props.affectedData.affectedUnits.find(au => au.filename.replaceAll('.rgd', '').split(";")[0] === target || au.filename.replaceAll('.rgd', '').split(";")[1] === target);
if(affectedUnit != null) {
return <span><img style={{verticalAlign: "top", height: 25}}
src={getIcon(affectedUnit.icon)}/><a
href={`/mod/${research.modId}/race/${race.id}/unit/${affectedUnit.id}`}>
href={`/mod/${props.mod.id}/race/${props.race.id}/unit/${affectedUnit.id}`}>
{affectedUnit.name}
</a></span>
}
var affectedSergeant = research.affectedSergeants.find(as => as.filename.replaceAll('.rgd', '') === target);
var affectedSergeant = props.affectedData.affectedSergeants.find(as => as.filename.replaceAll('.rgd', '') === target);
if(affectedSergeant != null) {
return <span><img style={{verticalAlign: "top", height: 25}}
src={getIcon(affectedSergeant.icon)}/>{affectedSergeant.name} at <img style={{verticalAlign: "top", height: 25}}
src={getIcon(affectedSergeant.unit.icon)}/><a
href={`/mod/${research.modId}/race/${race.id}/unit/${affectedSergeant.unit.id}`}>
href={`/mod/${props.mod.id}/race/${props.race.id}/unit/${affectedSergeant.unit.id}`}>
{affectedSergeant.unit.name}
</a></span>
}
var affectedBuilding = research.affectedBuildings.find(ab => ab.filename.replaceAll('.rgd', '') === target);
var affectedBuilding = props.affectedData.affectedBuildings.find(ab => ab.filename.replaceAll('.rgd', '') === target);
if(affectedBuilding != null) {
return <span><img style={{verticalAlign: "top", height: 25}} src={getIcon(affectedBuilding.icon)}/><a
href={`/mod/${research.modId}/race/${race.id}/building/${affectedBuilding.id}`}>
href={`/mod/${props.mod.id}/race/${props.race.id}/building/${affectedBuilding.id}`}>
{affectedBuilding.name}
</a> </span>
}
var affectedWeapon = research.affectedWeapons.find(aw => aw.filename.replaceAll('.rgd', '') === target);
var affectedWeapon = props.affectedData.affectedWeapons.find(aw => aw.filename.replaceAll('.rgd', '') === target);
if (affectedWeapon != null) {
return <span>{affectedWeapon.name ? affectedWeapon.name : affectedWeapon.filename.replaceAll('_', ' ').replace('.rgd', '')} at {
affectedWeapon.units.map(unit =>
<span><img style={{verticalAlign: "top", height: 25}} src={getIcon(unit.icon)}/><a
href={`/mod/${research.modId}/race/${race.id}/unit/${unit.id}`}>
href={`/mod/${props.mod.id}/race/${props.race.id}/unit/${unit.id}`}>
{unit.name}
</a> </span>
)
@@ -177,7 +179,7 @@ export function ModifiersProvidesResearchTable (props: IModifiersProvidesResearc
src={getIcon(affectedSergeant.icon)}/>{affectedSergeant.name} at <img
style={{verticalAlign: "top", height: 25}}
src={getIcon(affectedSergeant.unit.icon)}/><a
href={`/mod/${research.modId}/race/${race.id}/unit/${affectedSergeant.unit.id}`}>
href={`/mod//${props.mod.id}/race/${props.race.id}/unit/${affectedSergeant.unit.id}`}>
{affectedSergeant.unit.name}
</a> </span>
)
@@ -185,7 +187,7 @@ export function ModifiersProvidesResearchTable (props: IModifiersProvidesResearc
{
affectedWeapon.buildings.map(affectedBuilding =>
<span><img style={{verticalAlign: "top", height: 25}} src={getIcon(affectedBuilding.icon)}/><a
href={`/mod/${research.modId}/race/${race.id}/building/${affectedBuilding.id}`}>
href={`/mod//${props.mod.id}/race/${props.race.id}/building/${affectedBuilding.id}`}>
{affectedBuilding.name}
</a> </span>
)
@@ -194,7 +196,7 @@ export function ModifiersProvidesResearchTable (props: IModifiersProvidesResearc
}
return <span>Unknown target <i>{target}</i></span>;
return <span><i>{target}</i></span>;
}
return (
@@ -204,7 +206,7 @@ export function ModifiersProvidesResearchTable (props: IModifiersProvidesResearc
<TableRow
sx={{'&:last-child td, &:last-child th': {border: 0}}}
>
<TableCell component="th" scope="row">{getTarget(m.target, props.research, props.race)}</TableCell>
<TableCell component="th" scope="row">{getTarget(m.target)}</TableCell>
<TableCell style={{minWidth: 120}} component="th" scope="row">{getModName(m.reference)}</TableCell>
<TableCell component="th" scope="row">{getModIcon(m.reference)} {getChangeDescription(m.usageType, m.value)}</TableCell>
</TableRow>
+40 -20
View File
@@ -1,58 +1,74 @@
import {Paper, Table, TableCell, TableContainer, TableRow} from "@mui/material";
import React from "react";
import {IModifier} from "../types/IModifier";
import {IBuildingShort} from "../types/IBuildingShort";
import {IBuilding, IBuildingAddonShort} from "../types/IBuilding";
import {IBuildingAddonShort} from "../types/IBuilding";
import {IconUrl} from "../core/api";
import {IResearchRequirements} from "../types/IResearchRequirements";
import {goToAddon} from "../pages/BuildingPage";
import {IRequirement} from "../types/IRequirement";
import {IRequirement, IRequirementLimitByBuildingDto} from "../types/IRequirement";
interface IRequiredProps{
requirement: IRequirement,
building: IBuilding,
raceId: String,
modId: number,
}
function Required (props: IRequiredProps) {
function renderRequirementGlobalAddons(rgas: IBuildingAddonShort[], building: IBuilding, modId: number) {
function renderRequirementGlobalAddons(rgas: IBuildingAddonShort[],) {
return <div> {rgas.map(rga =>
<span>&nbsp; Global addon: <img style={{verticalAlign: "top", height: 25}}
src={getIcon(rga.icon)}/>
&nbsp;<a href={`/mod/${modId}/race/${building.race.id}/building/${rga.buildingId}#addon-${rga.id}`}>
&nbsp;<a href={`/mod/${props.modId}/race/${props.raceId}/building/${rga.buildingId}#addon-${rga.id}`}>
{rga.name}
</a></span>
)}</div>
}
function renderRequirementBuilding(requirementBuildings: IBuildingShort[], building: IBuilding, modId: number) {
function renderRequirementBuilding(requirementBuildings: IBuildingShort[],) {
return requirementBuildings.length > 0 && (
<div>&nbsp; Buildings: <img style={{verticalAlign: "top", height: 25}}
src={IconUrl + requirementBuildings[0].icon.replaceAll('\\', '/')}/>
&nbsp;{" "}<a href={`/mod/${modId}/race/${building.race.id}/building/${requirementBuildings[0].id}`}>
&nbsp;{" "}<a href={`/mod/${props.modId}/race/${props.raceId}/building/${requirementBuildings[0].id}`}>
{requirementBuildings[0].name}
</a> {requirementBuildings[1] !== undefined && <span>or &nbsp;<img style={{verticalAlign: "top", height: 25}}
src={IconUrl + requirementBuildings[1].icon.replaceAll('\\', '/')}/>
&nbsp;{" "}<a href={`/mod/${modId}/race/${building.race.id}/building/${requirementBuildings[1].id}`}>
&nbsp;{" "}<a href={`/mod/${props.modId}/race/${props.raceId}/building/${requirementBuildings[1].id}`}>
{requirementBuildings[1].name}</a></span> } </div>
);
}
function renderRequirementResearches(requirementResearches: IResearchRequirements[], modId: number, raceId: string) {
function renderRequirementOwnership(requirementOwnership: string[],) {
return requirementOwnership.length > 0 && <span>&nbsp; Owner: {requirementOwnership.map(o => {
switch(o) {
case 'relic_struct':
return "Relic point ";
default:
return o;
}
}
)} </span>
}
function renderLimitPerBuilding(limitPerBuilding: IRequirementLimitByBuildingDto,) {
return <span>&nbsp; One <img style={{verticalAlign: "top", height: 25}}
src={IconUrl + limitPerBuilding.building.icon.replaceAll('\\', '/')}/>
&nbsp;{" "}<a href={`/mod/${props.modId}/race/${props.raceId}/building/${limitPerBuilding.building.id}`}>
{limitPerBuilding.building.name} </a> allow produce max {limitPerBuilding.limit}</span>
}
function renderRequirementResearches(requirementResearches: IResearchRequirements[],) {
return <div> {requirementResearches.map(rs =>
<span>&nbsp; Research {rs.researchMustNotBeComplete ? "not" : ""}: <img style={{verticalAlign: "top", height: 25}}
src={getIcon(rs.researchShortDto.icon)}/>
&nbsp;<a href={`/mod/${modId}/race/${raceId}/building/${rs.researchShortDto.buildingId}#research-${rs.researchShortDto.id}`}>
&nbsp;<a href={`/mod/${props.modId}/race/${props.raceId}/building/${rs.researchShortDto.buildingId}#research-${rs.researchShortDto.id}`}>
{rs.researchShortDto.name}
</a></span>
)}</div>
}
const requirement = props.requirement
const building = props.building
return (
@@ -65,22 +81,26 @@ function Required (props: IRequiredProps) {
}
{requirement.requireAddon !== null &&
<div>&nbsp; Addon: <img style={{verticalAlign: "top", height: 25}}
src={IconUrl + requirement.requireAddon.icon.replaceAll('\\', '/')}/>&nbsp;
<a href={"#addon-" + requirement.requireAddon.id} onClick={() => goToAddon(`#addon-` + requirement.requireAddon.id)}>{requirement.requireAddon.name}</a>
src={IconUrl + requirement.requireAddon.icon.replaceAll('\\', '/')}/>&nbsp;
<a href={`/mod/${props.modId}/race/${props.raceId}/building/${requirement.requireAddon.buildingId}#addon-${requirement.requireAddon.id}`}>
{requirement.requireAddon.name}
</a>
{requirement.replaceWhenDone && <i> (old provides replace)</i>}
</div>
}
{requirement.requirementBuildings.map(b =>
<div>&nbsp; Building: <img style={{verticalAlign: "top", height: 25}}
src={IconUrl + b.icon.replaceAll('\\', '/')}/>&nbsp;
<a href= {'/mod/'+ building.modId +'/race/'+ building.race.id +'/building/' + b.id + "/"}>{b.name}</a></div>)
<a href= {'/mod/'+ props.modId +'/race/'+ props.raceId +'/building/' + b.id + "/"}>{b.name}</a></div>)
}
{requirement.requirementResearches.length !== 0 &&
renderRequirementResearches(requirement.requirementResearches, building.modId, building.race.id)}
renderRequirementResearches(requirement.requirementResearches,)}
{requirement.requirementBuildingsEither.length !== 0 &&
renderRequirementBuilding(requirement.requirementBuildingsEither, building, building.modId)}
renderRequirementBuilding(requirement.requirementBuildingsEither,)}
{requirement.limitByBuilding !== undefined && requirement.limitByBuilding !== null && renderLimitPerBuilding(requirement.limitByBuilding)}
{requirement.requirementsGlobalAddons.length !== 0 &&
renderRequirementGlobalAddons(requirement.requirementsGlobalAddons, building, building.modId)}
renderRequirementGlobalAddons(requirement.requirementsGlobalAddons,)}
{renderRequirementOwnership(requirement.requiredOwnership)}
</div>
)
}
+9 -5
View File
@@ -10,7 +10,7 @@ import {
TableBody,
TableCell,
TableContainer,
TableRow
TableRow, Tooltip
} from "@mui/material";
import AvTimerOutlinedIcon from "@mui/icons-material/AvTimer";
import ArmorType from "./ArmorType";
@@ -20,6 +20,7 @@ import Vision from "./Vision";
import {IMod} from "../types/Imod";
import {renderAffectedResearches} from "./building/Research";
import {Irace} from "../types/Irace";
import Required from "./Required";
@@ -103,9 +104,8 @@ const Sergeant = (props: SergeantProps) => {
>
<TableCell component="th" scope="row">Armor type</TableCell>
<TableCell component="th" scope="row">
<ArmorType name={sergeant.armorType.name} compact={false}/>
{sergeant.armorType2 && <span style={{fontSize: 12}} ><br/><i>after upgrade can become:<br/><ArmorType
name={sergeant.armorType2.name} compact={false}/></i></span> }
{sergeant.armorType2 == null ? <ArmorType name={sergeant.armorType.name} compact={false}/>
: <Tooltip title={"upgrade/debuff can turns to " + sergeant.armorType2.name}><span><ArmorType name={sergeant.armorType.name} compact={false}/></span></Tooltip> }
</TableCell>
</TableRow>
<TableRow
@@ -145,11 +145,15 @@ const Sergeant = (props: SergeantProps) => {
{sergeant.description}
</div>
</Grid2>
{sergeant.requirements !== null &&
<Grid2 size={{xs: 12, md: 12}}>
<Required requirement={sergeant.requirements} modId={props.mod.id} raceId={props.race.id}/>
</Grid2>}
<Grid2 size={12}>
{[...mapWithUnitWeapons.keys()].sort(function (a, b) {
return a - b;
}).map(h => <WeaponSlot race={props.race} mod={props.mod} unitWeapons={mapWithUnitWeapons.get(h)} hardpoint={h}/>)}
}).map(h => <WeaponSlot key ={h} race={props.race} mod={props.mod} unitWeapons={mapWithUnitWeapons.get(h)} hardpoint={h}/>)}
</Grid2>
<Grid2 size={12}>
{renderAffectedResearches(sergeant.affectedResearches, props.mod.id, props.race.id)}
+9 -5
View File
@@ -23,22 +23,22 @@ import Sergeant from "./Sergeant";
import {IMod} from "../types/Imod";
import {Irace} from "../types/Irace";
import {renderAffectedResearches} from "./building/Research";
import Required from "./Required";
interface IWeaponProps {
weapon: IWeapon,
isDefault: Boolean,
renderRequired: Boolean,
mod: IMod,
race: Irace,
}
interface IWeaponState {
currentTable: "dps" | "one hit",
haveReinforceMenu: Boolean,
}
class Weapon extends React.Component<IWeaponProps, any> {
public static defaultProps = {
isDefault: false
isDefault: false,
renderRequired: true,
};
humanReadableName(unit: string) {
@@ -427,6 +427,10 @@ class Weapon extends React.Component<IWeaponProps, any> {
</TableContainer>
<i className="rgdFrom">{weapon.filename}</i>
</Grid2>
{weapon.requirements !== null && !this.props.isDefault && this.props.haveReinforceMenu &&
<Grid2 size={{xs: 12, md: 12}}>
<Required requirement={weapon.requirements} modId={this.props.mod.id} raceId={this.props.race.id}/>
</Grid2>}
<Grid2 size={12}>
{renderAffectedResearches(weapon.affectedResearches, this.props.mod.id, this.props.race.id)}
</Grid2>
+4 -14
View File
@@ -9,7 +9,7 @@ export interface WeaponSlotProps {
unitWeapons: Map<number, IWeapon> | undefined,
mod: IMod,
race: Irace,
showOnlyDefault?: boolean ,
haveReinforceMenu?: boolean ,
}
const WeaponSlot= (props: WeaponSlotProps) => {
@@ -26,26 +26,16 @@ const WeaponSlot= (props: WeaponSlotProps) => {
let onlyDummy = weaponsSorted.filter(wp => !wp[1].filename.includes("dummy")).length === 0
let haveReinforceMenu = props.haveReinforceMenu === true
if(onlyDummy) return (<div></div>)
if(props.showOnlyDefault){
if(firstWeapon.filename.includes("dummy")) return (<div></div>);
return (
<div>
<h3>{header}</h3>
<div style={{marginLeft: 20}}><Weapon race={props.race} mod={props.mod} isDefault={true} weapon={firstWeapon}/></div>
</div>
)
}
return (
<div>
<h3>{header}</h3>
{weaponsSorted.filter(wp => !wp[1].filename.includes("dummy")).map(wp =>
<div style={{marginLeft: 20}}><Weapon race={props.race} mod={props.mod} isDefault={!haveDummy || wp[1].filename === firstWeapon.filename} weapon={wp[1]}/></div>
<div style={{marginLeft: 20}}><Weapon race={props.race} mod={props.mod} isDefault={!haveDummy || wp[1].filename === firstWeapon.filename} weapon={wp[1]} haveReinforceMenu={haveReinforceMenu}/></div>
)}
</div>
)
+3 -55
View File
@@ -12,53 +12,11 @@ import {
TableRow
} from "@mui/material";
import {IBuilding, IBuildingAddon} 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 {IMod} from "../../types/Imod";
import {IModifier} from "../../types/IModifier";
import {getIcon, ModifiersProvidesAddonTable,} from "../ModifiersProvideTable";
import {getIcon, ModifiersProvidesTable,} from "../ModifiersProvideTable";
import Required from "../Required";
import {Irace} from "../../types/Irace";
interface IAddonModifiersProvidesWeapons{
modifiers: IModifier[],
replacedAddonModifiers: IModifier[],
weapons: WeaponHardpoint[],
mod: IMod,
race: Irace
}
function AddonModifiersProvidesWeapons (props: IAddonModifiersProvidesWeapons) {
function getWeaponByAddonMod(ar: IModifier){
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 <WeaponSlot race={props.race} mod={props.mod} unitWeapons={new Map().set(hardpoint, weapon)} hardpoint={hardpoint}/>;
}else{
return "Can't find weapon, replaced by addon:" + value + " - "
}
}
let weaponMods = props.modifiers.filter(m => m.reference.includes("default_weapon_modifier_hardpoint"))
return (
<div>
{weaponMods.map(w => getWeaponByAddonMod(w))}
</div>
)
}
interface IBuildingAddonProps {
addon: IBuildingAddon,
@@ -72,12 +30,6 @@ function BuildingAddon(props: IBuildingAddonProps){
const building = props.building
const prevAddonModifiers: IModifier[] = (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 [];
})()
return <div className='addon-research-accordion' id={"addon-" + addon.id} ><Accordion>
<AccordionSummary
@@ -129,20 +81,16 @@ function BuildingAddon(props: IBuildingAddonProps){
</TableBody>
</Table>
</TableContainer><br/>
<ModifiersProvidesAddonTable modifiers={addon.addonModifiers}/>
</Grid2>
<Grid2 size={{xs: 12, md: 8}}>
<div style={{whiteSpace: "pre-wrap"}}>
{addon.description}
</div>
</Grid2>
{addon.addonModifiers !== null &&
<Grid2 size={{xs: 12, md: 12}}>
<AddonModifiersProvidesWeapons race={building.race} mod={props.mod} modifiers={addon.addonModifiers} replacedAddonModifiers={prevAddonModifiers} weapons={building.weapons}/>
</Grid2>}
<ModifiersProvidesTable modifiers={addon.addonModifiers} affectedData={addon.affectedData} mod={props.mod} race={building.race}/>
{addon.addonRequirement !== null &&
<Grid2 size={{xs: 12, md: 12}}>
<Required requirement={addon.addonRequirement} building={building}/>
<Required requirement={addon.addonRequirement} modId={building.modId} raceId={building.race.id}/>
</Grid2>}
</Grid2>
<i className="rgdFrom">{addon.filename}</i>
+5 -5
View File
@@ -13,7 +13,7 @@ import {
TableRow
} from "@mui/material";
import {ExpandMore} from "@mui/icons-material";
import {getIcon, ModifiersProvidesResearchTable} from "../ModifiersProvideTable";
import {getIcon, ModifiersProvidesTable} from "../ModifiersProvideTable";
import AvTimerOutlinedIcon from "@mui/icons-material/AvTimer";
import Required from "../Required";
import React from "react";
@@ -90,11 +90,11 @@ function ResearchFull(props: IResearchProps){
</Grid2>
<Grid2 size={{xs: 12, md: 12}}>
<ModifiersProvidesResearchTable modifiers={research.modifiers} research={research} race={building.race}/>
<ModifiersProvidesTable modifiers={research.modifiers} affectedData={research.affectedData} mod={props.mod} race={building.race}/>
</Grid2>
{research.requirements !== null &&
<Grid2 size={{xs: 12, md: 12}}>
<Required requirement={research.requirements} building={building}/>
<Required requirement={research.requirements} modId={building.modId} raceId={building.race.id}/>
</Grid2>}
</Grid2>
<i className="rgdFrom">{research.filename}</i>
@@ -114,8 +114,8 @@ export function renderAffectedResearches(researches: IResearchShort[], modId: nu
}
return <div> {researches.map(rs =>
rs.buildingId == null ? <span></span> :
<span>Research affect: {researchLink(rs)}<br/></span>
rs.buildingId == null ? <span key={rs.id}></span> :
<span key={rs.id}>Research affect: {researchLink(rs)}<br/></span>
)}</div>
}
+19 -34
View File
@@ -1,5 +1,5 @@
import {AvailableBuildings, AvailableMods, IconUrl} from "../core/api";
import React from "react";
import React, {useEffect} from "react";
import {withRouter} from "../core/withrouter";
import {IWeapon} from "../types/IUnit";
import '../css/Building.css'
@@ -13,7 +13,7 @@ import {
TableBody,
TableCell,
TableContainer,
TableRow
TableRow, Tooltip
} from "@mui/material";
import {ArrowBack} from "@mui/icons-material";
import ArmorType from "../classes/ArmorType";
@@ -26,6 +26,7 @@ import Vision from "../classes/Vision";
import BuildingAddon from "../classes/building/BuildingAddon";
import {IUnitShort} from "../types/IUnitShort";
import Research, {renderAffectedResearches} from "../classes/building/Research";
import Required from "../classes/Required";
interface UintPageState {
building: IBuilding,
@@ -34,18 +35,19 @@ interface UintPageState {
function Unit (unit: IUnitShort, modId: number, raceId: String) {
return (<Link href={"/mod/" + modId + "/race/" + raceId + "/unit/" + unit.id}><ListItem>
return (<Grid2 size={{xs: 12, md: 3}}><Link href={"/mod/" + modId + "/race/" + raceId + "/unit/" + unit.id}><ListItem>
{unit.icon && <img className="unitIcon" src={IconUrl + unit.icon.replaceAll('\\', '/')}/> }
{unit.name}
{unit.canDetect && <span>&nbsp;<img style={{verticalAlign: "top"}}
src="/images/DETECT_YES.webp"/></span>}
</ListItem></Link>)
</ListItem></Link></Grid2>)
}
function Building(building: IBuilding, mod: IMod) {
document.title = building.name
let mapBuildingWeapons: Map<number, Map<number, IWeapon>> = new Map();
@@ -130,8 +132,8 @@ function Building(building: IBuilding, mod: IMod) {
>
<TableCell component="th" scope="row">Armor type</TableCell>
<TableCell component="th" scope="row">
<ArmorType name={building.armorType.name} compact={false}/>
{building.armorType2 && <span><br/><ArmorType name={building.armorType2.name} compact={false}/></span> }
{building.armorType2 == null ? <ArmorType name={building.armorType.name} compact={false}/>
: <Tooltip title={"upgrade/debuff can turns to " + building.armorType2.name}><span><ArmorType name={building.armorType.name} compact={false}/></span></Tooltip> }
</TableCell>
</TableRow>
<TableRow
@@ -170,11 +172,17 @@ function Building(building: IBuilding, mod: IMod) {
{building.description}
</div>
</Grid2>
{building.requirements !== null &&
<Grid2 size={{xs: 12, md: 12}}>
<Required requirement={building.requirements} modId={building.modId} raceId={building.race.id}/>
</Grid2>}
{building.units.length > 0 && <Grid2 size={12}>
<h3>Unit production</h3>
{building.units.map(unit =>
Unit(unit, mod.id, building.race.id)
)}
<Grid2 container spacing={2}>
{building.units.map(unit =>
Unit(unit, mod.id, building.race.id)
)}
</Grid2>
</Grid2>}
{building.addons.length > 0 && <Grid2 size={12}>
<h3>Addons</h3>
@@ -191,7 +199,7 @@ function Building(building: IBuilding, mod: IMod) {
<Grid2 size={12}>
{[...mapBuildingWeapons.keys()].sort(function (a, b) {
return a - b;
}).map(h => <WeaponSlot race={building.race} mod={mod} showOnlyDefault={true} unitWeapons={mapBuildingWeapons.get(h)} hardpoint={h}/>)}
}).map(h => <WeaponSlot race={building.race} mod={mod} unitWeapons={mapBuildingWeapons.get(h)} hardpoint={h}/>)}
</Grid2>
<Grid2 size={12}>
{renderAffectedResearches(building.affectedResearches, mod.id, building.race.id)}
@@ -203,25 +211,6 @@ function Building(building: IBuilding, mod: IMod) {
</div>)
}
function goToAddon(addonHash: string) {
let addonId = addonHash.replace("#", "");
[].forEach.call(document.querySelectorAll('div'), function (el: HTMLElement) {
el?.classList?.remove('selected-addon');
});
document?.getElementById(addonId)?.classList?.add("selected-addon");
document?.getElementById(addonId)?.scrollIntoView()
}
function goToResearch(researchHash: string) {
let researchId = researchHash.replace("#", "");
[].forEach.call(document.querySelectorAll('div'), function (el: HTMLElement) {
el?.classList?.remove('selected-research');
});
document?.getElementById(researchId)?.classList?.add("selected-research");
document?.getElementById(researchId)?.scrollIntoView()
}
class BuildingPage extends React.Component<any, UintPageState> {
async componentDidMount() {
@@ -238,8 +227,6 @@ class BuildingPage extends React.Component<any, UintPageState> {
this.setState({
mod: modData
});
goToAddon(window.location.hash);
goToResearch(window.location.hash);
}
render() {
@@ -258,5 +245,3 @@ class BuildingPage extends React.Component<any, UintPageState> {
}
export default withRouter(BuildingPage);
export { goToAddon, goToResearch };
+2
View File
@@ -35,6 +35,8 @@ class ModPage extends React.Component<any, ModPageState> {
render() {
if(this.state != null && this.state.mod != null ){
document.title = this.state.mod.name
return <div>
<a href="/"><Button id="back-button" variant="contained"
startIcon={<ArrowBack/>}> Back</Button></a>
+46 -32
View File
@@ -39,18 +39,18 @@ interface RacePageState {
}
function Unit (unit: IUnitShort, modId: number, raceId: String) {
function Unit(unit: IUnitShort, modId: number, raceId: String) {
return (<Link href={"/mod/" + modId + "/race/" + raceId + "/unit/" + unit.id}><ListItem>
{unit.icon && <img className="unitIcon" src={IconUrl + unit.icon.replaceAll('\\', '/')}/> }
{unit.name}
{unit.canDetect && <span>&nbsp;<img style={{verticalAlign: "top"}}
src="/images/DETECT_YES.webp"/></span>}
{unit.icon && <img className="unitIcon" src={IconUrl + unit.icon.replaceAll('\\', '/')}/>}
{unit.name}
{unit.canDetect && <span>&nbsp;<img style={{verticalAlign: "top"}}
src="/images/DETECT_YES.webp"/></span>}
</ListItem></Link>)
}
function UnitSmall (unit: IUnitShort, modId: number, raceId: String) {
function UnitSmall(unit: IUnitShort, modId: number, raceId: String) {
var unitName = ""
if (unit.name.length > 21) {
@@ -63,10 +63,10 @@ function UnitSmall (unit: IUnitShort, modId: number, raceId: String) {
{unit.icon && <img className="unitIconSmall" src={IconUrl + unit.icon.replaceAll('\\', '/')}/>}
<span style={{fontSize: 12}}>{unitName}</span>
{unit.canDetect && <span>&nbsp;<img
src="/images/DETECT_YES.webp"/></span>}<br/></Link>)
src="/images/DETECT_YES.webp"/></span>}<br/></Link>)
}
function Building (building: IBuildingShort, modId: number, raceId: String) {
function Building(building: IBuildingShort, modId: number, raceId: String) {
return (<Grid2 size={{xs: 12, md: 3}}><Link href={"/mod/" + modId + "/race/" + raceId + "/building/" + building.id}><ListItem>
{building.icon && <img className="unitIcon" src={IconUrl + building.icon.replaceAll('\\', '/')}/>}
@@ -74,16 +74,15 @@ function Building (building: IBuildingShort, modId: number, raceId: String) {
{building.canDetect && <span>&nbsp;<img style={{verticalAlign: "top"}}
src="/images/DETECT_YES.webp"/></span>}
</ListItem></Link>
{building.units.map(unit => UnitSmall(unit, modId, raceId))}
</Grid2>)
}
}
interface UnitsProps{
raceId: string,
modId: number,
}
interface UnitsProps {
raceId: string,
modId: number,
}
interface UnitsState {
selectedUnits: String | null,
@@ -95,8 +94,9 @@ class Units extends React.Component<UnitsProps, UnitsState> {
constructor(props: any) {
super(props);
let urlUnits = AvailableUnits + "/" + this.props.modId + "/" + this.props.raceId;
let urlUnits = AvailableUnits + "/" + this.props.modId + "/" + this.props.raceId;
fetch(urlUnits)
@@ -107,7 +107,7 @@ class Units extends React.Component<UnitsProps, UnitsState> {
})
})
let urlBuildings = AvailableBuildings + "/" + this.props.modId + "/" + this.props.raceId;
let urlBuildings = AvailableBuildings + "/" + this.props.modId + "/" + this.props.raceId;
fetch(urlBuildings)
.then(res => res.json())
@@ -118,18 +118,24 @@ class Units extends React.Component<UnitsProps, UnitsState> {
})
}
render () {
render() {
if (this.state && this.state.units) {
return (<div>
if(this.state.buildings?.race.name !== undefined){
document.title = this.state.buildings?.race.name
}
{this.state.units != null && this.state.buildings != null ?
return (this.state.buildings != null ?
<div>
<Grid2 container spacing={2}>
{this.state.buildings.buildings.map(building => Building(building, this.props.modId, this.props.raceId))}
{this.state.buildings.buildings.map(building => Building(building, this.props.modId, this.props.raceId))}
</Grid2>
{this.state.buildings.buildingsAdvanced.length > 0 && <div><hr/><Grid2 container spacing={2}>
{this.state.buildings.buildingsAdvanced.map(building => Building(building, this.props.modId, this.props.raceId))}
</Grid2><br/></div>}
<Grid2>
<Grid2 size={{xs: 12, md: 12}}>
<Accordion>
<AccordionSummary
@@ -141,19 +147,27 @@ class Units extends React.Component<UnitsProps, UnitsState> {
</AccordionSummary>
<AccordionDetails>
<Grid2 container spacing={2}>
<Grid2 size= {{xs: 12, md: 4}}>
<Grid2 size={{xs: 12, md: 4}}>
<h3>Infantry</h3>
{this.state.units.infantry.map(unit => Unit(unit, this.props.modId, this.props.raceId))}
</Grid2>
<Grid2 size={{xs: 12, md: 4}}>
<h3>Tech</h3>
<List sx={{width: '100%', maxWidth: 360, bgcolor: 'background.paper'}}>
<List sx={{
width: '100%',
maxWidth: 360,
bgcolor: 'background.paper'
}}>
{this.state.units.tech.map(unit => Unit(unit, this.props.modId, this.props.raceId))}
</List>
</Grid2>
<Grid2 size={{xs: 12, md: 4}}>
<h3>Support</h3>
<List sx={{width: '100%', maxWidth: 360, bgcolor: 'background.paper'}}>
<List sx={{
width: '100%',
maxWidth: 360,
bgcolor: 'background.paper'
}}>
{this.state.units.support.map(unit => Unit(unit, this.props.modId, this.props.raceId))}
</List>
</Grid2>
@@ -162,9 +176,8 @@ class Units extends React.Component<UnitsProps, UnitsState> {
</Accordion>
</Grid2><br/>
</Grid2>
: "Loading"}
</div>)
</div> : "Loading"
)
} else {
return "Loading...";
}
@@ -188,20 +201,21 @@ class RacePageFast extends React.Component<any, RacePageState> {
mod: modData
});
const response = await fetch(AvailableRacesPart + "/" + this.props.match.params.raceId);
const response = await fetch(AvailableRacesPart + "/" + this.props.match.params.raceId);
const racesData: Irace = await response.json();
this.setState({
race : racesData
race: racesData
});
}
render() {
if(this.state != null && this.state.mod != null && this.state.race != null ){
if (this.state != null && this.state.mod != null && this.state.race != null) {
const backRef = "/mod/" + this.state.mod.id
return <div><Link href={backRef}><Button id="back-button" variant="contained" startIcon={<ArrowBack/>}> Back</Button></Link>
return <div><Link href={backRef}><Button id="back-button" variant="contained"
startIcon={<ArrowBack/>}> Back</Button></Link>
<h1>{this.state.mod.name} ({this.state.mod.version}) - {this.state.race.name}</h1>
<Units raceId={this.state.race.id} modId={this.state.mod.id}/>
</div>;
+22 -15
View File
@@ -14,7 +14,8 @@ import {
TableBody,
TableCell,
TableContainer,
TableRow
TableRow,
Tooltip
} from "@mui/material";
import {ArrowBack, ExpandMore} from "@mui/icons-material";
import ArmorType from "../classes/ArmorType";
@@ -24,9 +25,7 @@ import WeaponSlot from "../classes/WeaponSlot";
import UnitsTable from "../classes/UnitsTable";
import {IMod} from "../types/Imod";
import Vision from "../classes/Vision";
import {IResearchRequirements} from "../types/IResearchRequirements";
import {getIcon} from "../classes/Required";
import {IResearchShort} from "../types/IResearchShort";
import Required from "../classes/Required";
import {renderAffectedResearches} from "../classes/building/Research";
interface UintPageState {
@@ -37,6 +36,8 @@ interface UintPageState {
function Unit(unit: IUnit, mod: IMod) {
document.title = unit.name
const morale = (unit.moraleMax !== null) ? <span>
<img style={{verticalAlign: "top"}} src="/images/ARM_Morale.webp"/>&nbsp;{unit.moraleMax}
+{unit.moraleRegeneration}/s
@@ -77,10 +78,6 @@ function Unit(unit: IUnit, mod: IMod) {
}
console.log(unit);
return (
<div>
<h1>{mod.name} ({mod.version})</h1>
@@ -192,9 +189,7 @@ function Unit(unit: IUnit, mod: IMod) {
>
<TableCell component="th" scope="row">Armor type</TableCell>
<TableCell component="th" scope="row">
<ArmorType name={unit.armorType.name} compact={false}/>
{unit.armorType2 && <span style={{fontSize: 12}} ><br/><i>after upgrade can become:<br/><ArmorType
name={unit.armorType2.name} compact={false}/></i></span> }
{unit.armorType2 == null ? <ArmorType name={unit.armorType.name} compact={false}/> : <Tooltip title={"upgrade/debuff can turns to " + unit.armorType2.name}><span><ArmorType name={unit.armorType.name} compact={false}/></span></Tooltip> }
</TableCell>
</TableRow>
<TableRow
@@ -257,6 +252,14 @@ function Unit(unit: IUnit, mod: IMod) {
</TableCell>
</TableRow>
}
{unit.squadLimit !== undefined && unit.squadLimit !== null &&
<TableRow>
<TableCell>Limit</TableCell>
<TableCell>
{unit.squadLimit}
</TableCell>
</TableRow>
}
</TableBody>
</Table>
</TableContainer>
@@ -266,9 +269,13 @@ function Unit(unit: IUnit, mod: IMod) {
{unit.description}
</div>
</Grid2>
{unit.requirements !== null &&
<Grid2 size={{xs: 12, md: 12}}>
<Required requirement={unit.requirements} modId={unit.modId} raceId={unit.race.id}/>
</Grid2>}
<Grid2 size={12}>
{unit.sergeants.map(s =>
<Accordion>
<Accordion key={s.id}>
<AccordionSummary
expandIcon={<ExpandMore/>}
aria-controls="panel1-content"
@@ -286,7 +293,7 @@ function Unit(unit: IUnit, mod: IMod) {
<Grid2 size={12}>
{[...mapWithUnitWeapons.keys()].sort(function (a, b) {
return a - b;
}).map(h => <WeaponSlot race={unit.race} mod={mod} unitWeapons={mapWithUnitWeapons.get(h)} hardpoint={h}/>)}
}).map(h => <WeaponSlot haveReinforceMenu={unit.haveReinforceMenu} key ={h} race={unit.race} mod={mod} unitWeapons={mapWithUnitWeapons.get(h)} hardpoint={h}/>)}
</Grid2>
<Grid2 size={12}>
{renderAffectedResearches(unit.affectedResearches, mod.id, unit.race.id)}
@@ -325,12 +332,12 @@ class UnitPage extends React.Component<any, UintPageState> {
if (this.state != null && this.state.unit != null && this.state.mod != null) {
const backRef = "/mod/" + this.props.match.params.modId + "/race/" + this.props.match.params.raceId
return <div><a href={backRef}><Button id="back-button" variant="contained"
return <div key={this.state.unit.id}><a href={backRef}><Button id="back-button" variant="contained"
startIcon={<ArrowBack/>}> Back</Button></a>
{Unit(this.state.unit, this.state.mod)}
</div>;
} else {
return "loading...";
return <div key = {'loading'}>loading...</div>;
}
}
}
+10
View File
@@ -0,0 +1,10 @@
import {IModifier} from "./IModifier";
import {IRequirement} from "./IRequirement";
import {IAffectedEntity, ISergeantUnitShortDto, IWeaponUnitShortDto} from "./IAffectedEntity";
export interface IAffectedData {
affectedUnits: IAffectedEntity[]
affectedSergeants: ISergeantUnitShortDto[]
affectedBuildings: IAffectedEntity[]
affectedWeapons: IWeaponUnitShortDto[]
}
+3
View File
@@ -6,6 +6,7 @@ import {IModifier} from "./IModifier";
import {IRequirement} from "./IRequirement";
import {IResearch} from "./IResearch";
import {IResearchShort} from "./IResearchShort";
import {IAffectedData} from "./IAffectedData";
export interface IBuilding {
id: number
@@ -37,6 +38,7 @@ export interface IBuilding {
researches: IResearch[]
units: IUnitShort[]
affectedResearches: IResearchShort[],
requirements: IRequirement,
}
export interface IBuildingAddon {
@@ -50,6 +52,7 @@ export interface IBuildingAddon {
addonCostFaith: number;
addonCostSouls: number;
addonCostTime: number;
affectedData: IAffectedData;
addonModifiers: IModifier[];
addonRequirement: IRequirement;
icon?: string;
+3 -1
View File
@@ -4,6 +4,7 @@ import {IUnitShort} from "./IUnitShort";
export interface IRaceBuildings {
race: Irace
buildings: IBuildingShort[]
buildingsAdvanced: IBuildingShort[]
}
export interface IBuildingShort {
@@ -12,5 +13,6 @@ export interface IBuildingShort {
id: number
units: IUnitShort[]
canDetect: boolean
armorTypeName: string
armorTypeName: string,
uiIndexHint: number,
}
+9
View File
@@ -10,4 +10,13 @@ export interface IRequirement {
replaceWhenDone: Boolean;
requireAddon: IBuildingAddonShort;
requiredTotalPop?: number;
limitSquad?:number;
limitTech?:number;
limitByBuilding ?: IRequirementLimitByBuildingDto;
requiredOwnership: string[];
}
export interface IRequirementLimitByBuildingDto{
building: IBuildingShort;
limit: number;
}
+2 -5
View File
@@ -1,6 +1,6 @@
import {IModifier} from "./IModifier";
import {IRequirement} from "./IRequirement";
import {IAffectedEntity, ISergeantUnitShortDto, IWeaponUnitShortDto} from "./IAffectedEntity";
import {IAffectedData} from "./IAffectedData";
export interface IResearch {
id: number
@@ -15,10 +15,7 @@ export interface IResearch {
costTime: number
icon: string
modId: number
affectedUnits: IAffectedEntity[]
affectedSergeants: ISergeantUnitShortDto[]
affectedBuildings: IAffectedEntity[]
affectedWeapons: IWeaponUnitShortDto[]
affectedData: IAffectedData
requirements: IRequirement
modifiers: IModifier[]
}
+6 -1
View File
@@ -1,6 +1,7 @@
import {Irace} from "./Irace";
import {IArmorType} from "./IArmorType";
import {IResearchShort} from "./IResearchShort";
import {IRequirement} from "./IRequirement";
export interface IUnitResponse {
race: string
@@ -52,6 +53,8 @@ export interface IUnit {
requisitionIncome?: number
icon: string
modId: number
requirements: IRequirement
haveReinforceMenu: boolean,
sergeants: ISergeant[]
weapons: WeaponHardpoint[]
affectedResearches: IResearchShort[],
@@ -82,6 +85,7 @@ export interface ISergeant {
sightRadius: number
detectRadius: number
icon: string
requirements: IRequirement,
weapons: WeaponHardpoint[]
affectedResearches: IResearchShort[],
}
@@ -117,9 +121,10 @@ export interface IWeapon {
isMeleeWeapon: boolean
canAttackAir: boolean
canAttackGround: boolean
haveEquipButton: boolean
showInReinforce: boolean
icon: string
modId: number
requirements: IRequirement
weaponArmorPiercing: IWeaponPiercing[]
affectedResearches: IResearchShort[],
}