- Add unit, buildings, sergeants income resources

- Add repair speed
- Add UA support
- Rework race page
This commit is contained in:
Anibus 2025-06-25 16:02:41 +03:00
parent 00963ddc56
commit f94553bb9a
17 changed files with 364 additions and 117 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 186 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 196 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 486 B

View File

@ -26,6 +26,8 @@ class ArmorType extends React.Component<IArmorType, any> {
return '/images/ARM_Hvy_Inf_Hi.webp'; return '/images/ARM_Hvy_Inf_Hi.webp';
case 'Commander': case 'Commander':
return '/images/ARM_Cmdr.webp'; return '/images/ARM_Cmdr.webp';
case 'Living Metal':
return '/images/ARM_living_metal.webp';
case 'Vehicle Low': case 'Vehicle Low':
return '/images/ARM_Veh_Lo.webp'; return '/images/ARM_Veh_Lo.webp';
case 'Vehicle Medium': case 'Vehicle Medium':
@ -40,10 +42,16 @@ class ArmorType extends React.Component<IArmorType, any> {
return '/images/ARM_Bld_Mid.webp'; return '/images/ARM_Bld_Mid.webp';
case 'Building High': case 'Building High':
return '/images/ARM_Bld_Hi.webp'; return '/images/ARM_Bld_Hi.webp';
case 'Building Super':
return '/images/ARM_building_super.webp';
case 'Demon Low':
return '/images/ARM_Dmn_Lo.webp';
case 'Demon Medium': case 'Demon Medium':
return '/images/ARM_Dmn_Mid.webp'; return '/images/ARM_Dmn_Mid.webp';
case 'Demon High': case 'Demon High':
return '/images/ARM_Dmn_Hi.webp'; return '/images/ARM_Dmn_Hi.webp';
case 'Titan':
return '/images/ARM_titan.webp';
default: default:
return armorTypeId; return armorTypeId;
} }

View File

@ -17,11 +17,13 @@ import ArmorType from "./ArmorType";
import {ExpandMore} from "@mui/icons-material"; import {ExpandMore} from "@mui/icons-material";
import WeaponSlot from "./WeaponSlot"; import WeaponSlot from "./WeaponSlot";
import Vision from "./Vision"; import Vision from "./Vision";
import {IMod} from "../types/Imod";
export interface SergeantProps { export interface SergeantProps {
sergeant: ISergeant sergeant: ISergeant,
mod: IMod
} }
const Sergeant = (props: SergeantProps) => { const Sergeant = (props: SergeantProps) => {
@ -51,7 +53,7 @@ const Sergeant = (props: SergeantProps) => {
<TableRow <TableRow
sx={{'&:last-child td, &:last-child th': {border: 0}}} sx={{'&:last-child td, &:last-child th': {border: 0}}}
> >
<TableCell component="th" scope="row">cost</TableCell> <TableCell component="th" scope="row">Cost</TableCell>
<TableCell component="th" scope="row"> <TableCell component="th" scope="row">
{sergeant.buildCostRequisition > 0 && {sergeant.buildCostRequisition > 0 &&
<span>&nbsp;<img style={{verticalAlign: "top"}} <span>&nbsp;<img style={{verticalAlign: "top"}}
@ -74,10 +76,29 @@ const Sergeant = (props: SergeantProps) => {
{sergeant.buildCostTime}s</span>} {sergeant.buildCostTime}s</span>}
</TableCell> </TableCell>
</TableRow> </TableRow>
{(sergeant.requisitionIncome !== undefined && sergeant.requisitionIncome !== null || sergeant.powerIncome !== undefined && sergeant.powerIncome !== null || sergeant.faithIncome !== undefined && sergeant.faithIncome !== null) &&
<TableRow>
<TableCell>Resource income</TableCell>
<TableCell>
{sergeant.requisitionIncome !== undefined && sergeant.requisitionIncome > 0 &&
<span>&nbsp;<img style={{verticalAlign: "top"}}
src="/images/Resource_requisition.gif"/>&nbsp;
{sergeant.requisitionIncome}</span>}
{sergeant.powerIncome !== undefined && sergeant.powerIncome > 0 &&
<span>&nbsp;<img style={{verticalAlign: "top"}}
src="/images/Resource_power.gif"/>&nbsp;
{sergeant.powerIncome}</span>}
{sergeant.faithIncome !== undefined && sergeant.faithIncome > 0 &&
<span>&nbsp;<img style={{verticalAlign: "top"}}
src="/images/Resource_faith.gif"/>&nbsp;
{sergeant.faithIncome}</span>}
</TableCell>
</TableRow>
}
<TableRow <TableRow
sx={{'&:last-child td, &:last-child th': {border: 0}}} sx={{'&:last-child td, &:last-child th': {border: 0}}}
> >
<TableCell component="th" scope="row">armor type</TableCell> <TableCell component="th" scope="row">Armor type</TableCell>
<TableCell component="th" scope="row"> <TableCell component="th" scope="row">
<ArmorType name={sergeant.armorType.name}/> <ArmorType name={sergeant.armorType.name}/>
</TableCell> </TableCell>
@ -85,7 +106,7 @@ const Sergeant = (props: SergeantProps) => {
<TableRow <TableRow
sx={{'&:last-child td, &:last-child th': {border: 0}}} sx={{'&:last-child td, &:last-child th': {border: 0}}}
> >
<TableCell component="th" scope="row">health</TableCell> <TableCell component="th" scope="row">Health</TableCell>
<TableCell component="th" scope="row"> <TableCell component="th" scope="row">
<span><img style={{verticalAlign: "top"}} <span><img style={{verticalAlign: "top"}}
src="/images/Health_icon.webp"/>&nbsp; src="/images/Health_icon.webp"/>&nbsp;
@ -96,7 +117,7 @@ const Sergeant = (props: SergeantProps) => {
<TableRow <TableRow
sx={{'&:last-child td, &:last-child th': {border: 0}}} sx={{'&:last-child td, &:last-child th': {border: 0}}}
> >
<TableCell component="th" scope="row">morale</TableCell> <TableCell component="th" scope="row">Morale</TableCell>
<TableCell component="th" scope="row"> <TableCell component="th" scope="row">
<img style={{verticalAlign: "top"}} <img style={{verticalAlign: "top"}}
src="/images/Kills_icon.webp"/> -{sergeant.moraleDeathPenalty} src="/images/Kills_icon.webp"/> -{sergeant.moraleDeathPenalty}
@ -105,7 +126,7 @@ const Sergeant = (props: SergeantProps) => {
<TableRow <TableRow
sx={{'&:last-child td, &:last-child th': {border: 0}}} sx={{'&:last-child td, &:last-child th': {border: 0}}}
> >
<TableCell component="th" scope="row">detect</TableCell> <TableCell component="th" scope="row">Detect</TableCell>
<TableCell component="th" scope="row"> <TableCell component="th" scope="row">
<Vision sight={sergeant.sightRadius} detect={sergeant.detectRadius}/> <Vision sight={sergeant.sightRadius} detect={sergeant.detectRadius}/>
</TableCell> </TableCell>
@ -123,7 +144,7 @@ const Sergeant = (props: SergeantProps) => {
<Grid2 size={12}> <Grid2 size={12}>
{[...mapWithUnitWeapons.keys()].sort(function (a, b) { {[...mapWithUnitWeapons.keys()].sort(function (a, b) {
return a - b; return a - b;
}).map(h => <WeaponSlot unitWeapons={mapWithUnitWeapons.get(h)} hardpoint={h}/>)} }).map(h => <WeaponSlot mod={props.mod} unitWeapons={mapWithUnitWeapons.get(h)} hardpoint={h}/>)}
</Grid2> </Grid2>
</Grid2> </Grid2>
<i className="rgdFrom">{sergeant.filename}</i> <i className="rgdFrom">{sergeant.filename}</i>

View File

@ -20,10 +20,12 @@ import ArmorTypeNames from "../types/ArmorTypeValues";
import {IconUrl} from "../core/api"; import {IconUrl} from "../core/api";
import {ExpandMore} from "@mui/icons-material"; import {ExpandMore} from "@mui/icons-material";
import Sergeant from "./Sergeant"; import Sergeant from "./Sergeant";
import {IMod} from "../types/Imod";
interface IWeaponProps { interface IWeaponProps {
weapon: IWeapon, weapon: IWeapon,
isDefault: Boolean, isDefault: Boolean,
mod: IMod,
} }
interface IWeaponState { interface IWeaponState {
@ -85,6 +87,12 @@ class Weapon extends React.Component<IWeaponProps, any> {
const buildingMedPiercing = this.getPiercingK(ArmorTypeNames.BuildingMedium) const buildingMedPiercing = this.getPiercingK(ArmorTypeNames.BuildingMedium)
const buildingHighPiercing = this.getPiercingK(ArmorTypeNames.BuildingHigh) const buildingHighPiercing = this.getPiercingK(ArmorTypeNames.BuildingHigh)
// UA mod
const demonLowPiercing = this.getPiercingK(ArmorTypeNames.DemonLow)
const buildingSuperPiercing = this.getPiercingK(ArmorTypeNames.BuildingSuper)
const livingMetalPiercing = this.getPiercingK(ArmorTypeNames.LivingMetal)
const titanPiercing = this.getPiercingK(ArmorTypeNames.Titan)
const getTotalDamage = (damagePiercing: number, isAir: boolean = false) => { const getTotalDamage = (damagePiercing: number, isAir: boolean = false) => {
if (!isAir && !weapon.canAttackGround && !weapon.isMeleeWeapon) return "" if (!isAir && !weapon.canAttackGround && !weapon.isMeleeWeapon) return ""
@ -155,7 +163,7 @@ class Weapon extends React.Component<IWeaponProps, any> {
> >
<TableCell component="th" scope="row">Base damage</TableCell> <TableCell component="th" scope="row">Base damage</TableCell>
<TableCell component="th" <TableCell component="th"
scope="row">{weapon.minDamage} {weapon.maxDamage !== weapon.minDamage && -weapon.maxDamage} scope="row">{weapon.minDamage} {weapon.maxDamage !== weapon.minDamage && "- " + weapon.maxDamage}
</TableCell> </TableCell>
</TableRow> </TableRow>
<TableRow <TableRow
@ -267,63 +275,143 @@ class Weapon extends React.Component<IWeaponProps, any> {
<ToggleButton size="small" value="one hit">One hit average damage</ToggleButton> <ToggleButton size="small" value="one hit">One hit average damage</ToggleButton>
</ToggleButtonGroup> </ToggleButtonGroup>
<TableContainer> <TableContainer>
<Table sx={{marginTop: "10px"}} size="small" aria-label="a dense table"> { this.props.mod !== undefined && this.props.mod.name.includes("Ultimate Apocalypse") ?
<TableHead> <Table sx={{marginTop: "10px"}} size="small" aria-label="a dense table">
<TableRow> <TableHead>
<StyledTableCell><ArmorType <TableRow>
name={ArmorTypeNames.InfantryLow}/></StyledTableCell> <StyledTableCell><ArmorType
<StyledTableCell><ArmorType name={ArmorTypeNames.InfantryLow}/></StyledTableCell>
name={ArmorTypeNames.InfantryMedium}/></StyledTableCell> <StyledTableCell><ArmorType
<StyledTableCell><ArmorType name={ArmorTypeNames.InfantryMedium}/></StyledTableCell>
name={ArmorTypeNames.InfantryHigh}/></StyledTableCell> <StyledTableCell><ArmorType
<StyledTableCell><ArmorType name={ArmorTypeNames.InfantryHigh}/></StyledTableCell>
name={ArmorTypeNames.InfantryHeavyMedium}/></StyledTableCell> <StyledTableCell><ArmorType
<StyledTableCell><ArmorType name={ArmorTypeNames.InfantryHeavyMedium}/></StyledTableCell>
name={ArmorTypeNames.InfantryHeavyHigh}/></StyledTableCell> <StyledTableCell><ArmorType
<StyledTableCell><ArmorType name={ArmorTypeNames.InfantryHeavyHigh}/></StyledTableCell>
name={ArmorTypeNames.Commander}/></StyledTableCell> <StyledTableCell><ArmorType
<StyledTableCell><ArmorType name={ArmorTypeNames.Commander}/></StyledTableCell>
name={ArmorTypeNames.DemonMedium}/></StyledTableCell> <StyledTableCell><ArmorType
<StyledTableCell><ArmorType name={ArmorTypeNames.DemonLow}/></StyledTableCell>
name={ArmorTypeNames.DemonHigh}/></StyledTableCell> <StyledTableCell><ArmorType
<StyledTableCell><ArmorType name={ArmorTypeNames.Air}/></StyledTableCell> name={ArmorTypeNames.DemonMedium}/></StyledTableCell>
<StyledTableCell><ArmorType <StyledTableCell><ArmorType
name={ArmorTypeNames.VehicleLow}/></StyledTableCell> name={ArmorTypeNames.DemonHigh}/></StyledTableCell>
<StyledTableCell><ArmorType <StyledTableCell><ArmorType name={ArmorTypeNames.Air}/></StyledTableCell>
name={ArmorTypeNames.VehicleMedium}/></StyledTableCell> </TableRow>
<StyledTableCell><ArmorType </TableHead>
name={ArmorTypeNames.VehicleHigh}/></StyledTableCell> <TableBody>
<StyledTableCell><ArmorType <TableRow>
name={ArmorTypeNames.BuildingLow}/></StyledTableCell> <StyledTableCell>{getTotalDamage(infLowPiercing)}</StyledTableCell>
<StyledTableCell><ArmorType <StyledTableCell>{getTotalDamage(infMedPiercing)}</StyledTableCell>
name={ArmorTypeNames.BuildingMedium}/></StyledTableCell> <StyledTableCell>{getTotalDamage(infHighPiercing)}</StyledTableCell>
<StyledTableCell><ArmorType <StyledTableCell>{getTotalDamage(infHeavyMedPiercing)}</StyledTableCell>
name={ArmorTypeNames.BuildingHigh}/></StyledTableCell> <StyledTableCell>{getTotalDamage(infHeavyHighPiercing)}</StyledTableCell>
<StyledTableCell><img style={{verticalAlign: "top"}} <StyledTableCell>{getTotalDamage(commanderPiercing)}</StyledTableCell>
src="/images/ARM_Morale.webp"/></StyledTableCell> <StyledTableCell>{getTotalDamage(demonLowPiercing)}</StyledTableCell>
</TableRow> <StyledTableCell>{getTotalDamage(demonPiercing)}</StyledTableCell>
</TableHead> <StyledTableCell>{getTotalDamage(demonHighPiercing)}</StyledTableCell>
<TableBody> <StyledTableCell>{getTotalDamage(airPiercing, true)}</StyledTableCell>
<TableRow> </TableRow>
<StyledTableCell>{getTotalDamage(infLowPiercing)}</StyledTableCell> </TableBody>
<StyledTableCell>{getTotalDamage(infMedPiercing)}</StyledTableCell> <TableHead>
<StyledTableCell>{getTotalDamage(infHighPiercing)}</StyledTableCell> <TableRow>
<StyledTableCell>{getTotalDamage(infHeavyMedPiercing)}</StyledTableCell> <StyledTableCell><ArmorType
<StyledTableCell>{getTotalDamage(infHeavyHighPiercing)}</StyledTableCell> name={ArmorTypeNames.VehicleLow}/></StyledTableCell>
<StyledTableCell>{getTotalDamage(commanderPiercing)}</StyledTableCell> <StyledTableCell><ArmorType
<StyledTableCell>{getTotalDamage(demonPiercing)}</StyledTableCell> name={ArmorTypeNames.VehicleMedium}/></StyledTableCell>
<StyledTableCell>{getTotalDamage(demonHighPiercing)}</StyledTableCell> <StyledTableCell><ArmorType
<StyledTableCell>{getTotalDamage(airPiercing, true)}</StyledTableCell> name={ArmorTypeNames.VehicleHigh}/></StyledTableCell>
<StyledTableCell>{getTotalDamage(vehLowPiercing)}</StyledTableCell> <StyledTableCell><ArmorType
<StyledTableCell>{getTotalDamage(vehMedPiercing)}</StyledTableCell> name={ArmorTypeNames.LivingMetal}/></StyledTableCell>
<StyledTableCell>{getTotalDamage(vehHighPiercing)}</StyledTableCell> <StyledTableCell><ArmorType
<StyledTableCell>{getTotalDamage(buildingLowPiercing)}</StyledTableCell> name={ArmorTypeNames.Titan}/></StyledTableCell>
<StyledTableCell>{getTotalDamage(buildingMedPiercing)}</StyledTableCell> <StyledTableCell><ArmorType
<StyledTableCell>{getTotalDamage(buildingHighPiercing)}</StyledTableCell> name={ArmorTypeNames.BuildingLow}/></StyledTableCell>
<StyledTableCell>{getMoraleDamage()}</StyledTableCell> <StyledTableCell><ArmorType
</TableRow> name={ArmorTypeNames.BuildingMedium}/></StyledTableCell>
</TableBody> <StyledTableCell><ArmorType
</Table> name={ArmorTypeNames.BuildingHigh}/></StyledTableCell>
<StyledTableCell><ArmorType
name={ArmorTypeNames.BuildingSuper}/></StyledTableCell>
<StyledTableCell><img style={{verticalAlign: "top"}}
src="/images/ARM_Morale.webp"/></StyledTableCell>
</TableRow>
</TableHead>
<TableBody>
<TableRow>
<StyledTableCell>{getTotalDamage(vehLowPiercing)}</StyledTableCell>
<StyledTableCell>{getTotalDamage(vehMedPiercing)}</StyledTableCell>
<StyledTableCell>{getTotalDamage(vehHighPiercing)}</StyledTableCell>
<StyledTableCell>{getTotalDamage(livingMetalPiercing)}</StyledTableCell>
<StyledTableCell>{getTotalDamage(titanPiercing)}</StyledTableCell>
<StyledTableCell>{getTotalDamage(buildingLowPiercing)}</StyledTableCell>
<StyledTableCell>{getTotalDamage(buildingMedPiercing)}</StyledTableCell>
<StyledTableCell>{getTotalDamage(buildingHighPiercing)}</StyledTableCell>
<StyledTableCell>{getTotalDamage(buildingSuperPiercing)}</StyledTableCell>
<StyledTableCell>{getMoraleDamage()}</StyledTableCell>
</TableRow>
</TableBody>
</Table> :
<Table sx={{marginTop: "10px"}} size="small" aria-label="a dense table">
<TableHead>
<TableRow>
<StyledTableCell><ArmorType
name={ArmorTypeNames.InfantryLow}/></StyledTableCell>
<StyledTableCell><ArmorType
name={ArmorTypeNames.InfantryMedium}/></StyledTableCell>
<StyledTableCell><ArmorType
name={ArmorTypeNames.InfantryHigh}/></StyledTableCell>
<StyledTableCell><ArmorType
name={ArmorTypeNames.InfantryHeavyMedium}/></StyledTableCell>
<StyledTableCell><ArmorType
name={ArmorTypeNames.InfantryHeavyHigh}/></StyledTableCell>
<StyledTableCell><ArmorType
name={ArmorTypeNames.Commander}/></StyledTableCell>
<StyledTableCell><ArmorType
name={ArmorTypeNames.DemonMedium}/></StyledTableCell>
<StyledTableCell><ArmorType
name={ArmorTypeNames.DemonHigh}/></StyledTableCell>
<StyledTableCell><ArmorType name={ArmorTypeNames.Air}/></StyledTableCell>
<StyledTableCell><ArmorType
name={ArmorTypeNames.VehicleLow}/></StyledTableCell>
<StyledTableCell><ArmorType
name={ArmorTypeNames.VehicleMedium}/></StyledTableCell>
<StyledTableCell><ArmorType
name={ArmorTypeNames.VehicleHigh}/></StyledTableCell>
<StyledTableCell><ArmorType
name={ArmorTypeNames.BuildingLow}/></StyledTableCell>
<StyledTableCell><ArmorType
name={ArmorTypeNames.BuildingMedium}/></StyledTableCell>
<StyledTableCell><ArmorType
name={ArmorTypeNames.BuildingHigh}/></StyledTableCell>
<StyledTableCell><img style={{verticalAlign: "top"}}
src="/images/ARM_Morale.webp"/></StyledTableCell>
</TableRow>
</TableHead>
<TableBody>
<TableRow>
<StyledTableCell>{getTotalDamage(infLowPiercing)}</StyledTableCell>
<StyledTableCell>{getTotalDamage(infMedPiercing)}</StyledTableCell>
<StyledTableCell>{getTotalDamage(infHighPiercing)}</StyledTableCell>
<StyledTableCell>{getTotalDamage(infHeavyMedPiercing)}</StyledTableCell>
<StyledTableCell>{getTotalDamage(infHeavyHighPiercing)}</StyledTableCell>
<StyledTableCell>{getTotalDamage(commanderPiercing)}</StyledTableCell>
<StyledTableCell>{getTotalDamage(demonPiercing)}</StyledTableCell>
<StyledTableCell>{getTotalDamage(demonHighPiercing)}</StyledTableCell>
<StyledTableCell>{getTotalDamage(airPiercing, true)}</StyledTableCell>
<StyledTableCell>{getTotalDamage(vehLowPiercing)}</StyledTableCell>
<StyledTableCell>{getTotalDamage(vehMedPiercing)}</StyledTableCell>
<StyledTableCell>{getTotalDamage(vehHighPiercing)}</StyledTableCell>
<StyledTableCell>{getTotalDamage(buildingLowPiercing)}</StyledTableCell>
<StyledTableCell>{getTotalDamage(buildingMedPiercing)}</StyledTableCell>
<StyledTableCell>{getTotalDamage(buildingHighPiercing)}</StyledTableCell>
<StyledTableCell>{getMoraleDamage()}</StyledTableCell>
</TableRow>
</TableBody>
</Table>
}
</TableContainer> </TableContainer>
<i className="rgdFrom">{weapon.filename}</i> <i className="rgdFrom">{weapon.filename}</i>
</Grid2> </Grid2>

View File

@ -1,10 +1,12 @@
import Weapon from "./Weapon"; import Weapon from "./Weapon";
import React from "react"; import React from "react";
import {IWeapon} from "../types/IUnit"; import {IWeapon} from "../types/IUnit";
import {IMod} from "../types/Imod";
export interface WeaponSlotProps { export interface WeaponSlotProps {
hardpoint: number, hardpoint: number,
unitWeapons: Map<number, IWeapon> | undefined, unitWeapons: Map<number, IWeapon> | undefined,
mod: IMod,
showOnlyDefault?: boolean , showOnlyDefault?: boolean ,
} }
@ -31,7 +33,7 @@ const WeaponSlot= (props: WeaponSlotProps) => {
return ( return (
<div> <div>
<h3>{header}</h3> <h3>{header}</h3>
<div style={{marginLeft: 20}}><Weapon isDefault={true} weapon={firstWeapon}/></div> <div style={{marginLeft: 20}}><Weapon mod={props.mod} isDefault={true} weapon={firstWeapon}/></div>
</div> </div>
) )
} }
@ -41,7 +43,7 @@ const WeaponSlot= (props: WeaponSlotProps) => {
<h3>{header}</h3> <h3>{header}</h3>
{weaponsSorted.filter(wp => !wp[1].filename.includes("dummy")).map(wp => {weaponsSorted.filter(wp => !wp[1].filename.includes("dummy")).map(wp =>
<div style={{marginLeft: 20}}><Weapon isDefault={!haveDummy || wp[1].filename === firstWeapon.filename} weapon={wp[1]}/></div> <div style={{marginLeft: 20}}><Weapon mod={props.mod} isDefault={!haveDummy || wp[1].filename === firstWeapon.filename} weapon={wp[1]}/></div>
)} )}
</div> </div>
) )

View File

@ -19,6 +19,7 @@ import {ExpandMore} from "@mui/icons-material";
import {IconUrl} from "../../core/api"; import {IconUrl} from "../../core/api";
import {IBuildingShort} from "../../types/IBuildingShort"; import {IBuildingShort} from "../../types/IBuildingShort";
import {goToAddon} from "../../pages/BuildingPage"; import {goToAddon} from "../../pages/BuildingPage";
import {IMod} from "../../types/Imod";
interface IAddonModifiersProvidesTable{ interface IAddonModifiersProvidesTable{
modifiers: IAddonModifier[], modifiers: IAddonModifier[],
@ -34,10 +35,22 @@ function AddonModifiersProvidesTable (props: IAddonModifiersProvidesTable) {
return 'Health'; return 'Health';
case 'modifiers\\keen_sight_radius_modifier.lua': case 'modifiers\\keen_sight_radius_modifier.lua':
return 'Detect'; return 'Detect';
case 'modifiers\\sight_radius_modifier.lua':
return 'Sight';
case 'modifiers\\garrison_requisition_modifier.lua': case 'modifiers\\garrison_requisition_modifier.lua':
return 'Requisition'; return 'Requisition';
case 'modifiers\\faith_max_modifier.lua':
return 'Faith max';
case 'modifiers\\holy_icon_cost_power.lua':
return <span>Next icon cost <img style={{verticalAlign: "top"}} src='/images/Resource_power.gif'/></span>;
case 'modifiers\\holy_icon_cost_requisition.lua':
return <span>Next icon cost <img style={{verticalAlign: "top"}} src='/images/Resource_requisition.gif'/></span>;
case 'modifiers\\enable_hardpoint_02.lua':
return 'Weapon slot 2';
case 'modifiers\\cost_requisition_modifier.lua':
return 'Requisition cost';
default: default:
return mt; return mt.replace("modifiers\\", "").replace("modifier.lua", "").replace(".lua", "").replaceAll("_", " ");
} }
} }
@ -49,23 +62,35 @@ function AddonModifiersProvidesTable (props: IAddonModifiersProvidesTable) {
return <img style={{verticalAlign: "top"}} src='/images/Health_icon.webp'/>; return <img style={{verticalAlign: "top"}} src='/images/Health_icon.webp'/>;
case 'modifiers\\keen_sight_radius_modifier.lua': case 'modifiers\\keen_sight_radius_modifier.lua':
return <img style={{verticalAlign: "top"}} src='/images/DETECT_YES.webp'/>; return <img style={{verticalAlign: "top"}} src='/images/DETECT_YES.webp'/>;
case 'modifiers\\sight_radius_modifier.lua':
return <img style={{verticalAlign: "top"}} src='/images/DETECT_NO.webp'/>;
case 'modifiers\\faith_max_modifier.lua':
return <img style={{verticalAlign: "top"}} src='/images/Resource_faith.gif'/>;
case 'modifiers\\garrison_requisition_modifier.lua': case 'modifiers\\garrison_requisition_modifier.lua':
return <img style={{verticalAlign: "top"}} src='/images/Resource_requisition.gif'/>; return <img style={{verticalAlign: "top"}} src='/images/Resource_requisition.gif'/>;
case 'modifiers\\cost_requisition_modifier.lua':
return <img style={{verticalAlign: "top"}} src='/images/Resource_requisition.gif'/>;
default: default:
return mt; return "";
} }
} }
function getChangeDescription(ref: String) { function getChangeDescription(ref: String, v: number) {
switch(ref) { switch(ref) {
case 'type_modifierusagetype\\tp_mod_usage_addition.lua': case 'type_modifierusagetype\\tp_mod_usage_addition.lua':
return '+'; if(v < 0){
return v
}else return '+' + v;
case 'type_modifierusagetype\\tp_mod_usage_multiplication.lua': case 'type_modifierusagetype\\tp_mod_usage_multiplication.lua':
return 'x'; return 'x' + v;
case 'type_modifierusagetype\\tp_mod_usage_percentage.lua': case 'type_modifierusagetype\\tp_mod_usage_percentage.lua':
return '%'; return '%' + v;
case 'type_modifierusagetype\\tp_mod_usage_enable.lua': case 'type_modifierusagetype\\tp_mod_usage_enable.lua':
return ' enable '; if(v === 1){
return ' enable ';
} else {
return ' disable ';
}
default: default:
return ref; return ref;
} }
@ -81,7 +106,7 @@ function AddonModifiersProvidesTable (props: IAddonModifiersProvidesTable) {
sx={{'&:last-child td, &:last-child th': {border: 0}}} sx={{'&:last-child td, &:last-child th': {border: 0}}}
> >
<TableCell component="th" scope="row">{getModName(m.reference)}</TableCell> <TableCell component="th" scope="row">{getModName(m.reference)}</TableCell>
<TableCell component="th" scope="row">{getModIcon(m.reference)} {getChangeDescription(m.usageType)}{m.value}</TableCell> <TableCell component="th" scope="row">{getModIcon(m.reference)} {getChangeDescription(m.usageType, m.value)}</TableCell>
</TableRow> </TableRow>
)} )}
</Table> </Table>
@ -92,7 +117,8 @@ function AddonModifiersProvidesTable (props: IAddonModifiersProvidesTable) {
interface IAddonModifiersProvidesWeapons{ interface IAddonModifiersProvidesWeapons{
modifiers: IAddonModifier[], modifiers: IAddonModifier[],
replacedAddonModifiers: IAddonModifier[], replacedAddonModifiers: IAddonModifier[],
weapons: WeaponHardpoint[] weapons: WeaponHardpoint[],
mod: IMod,
} }
function AddonModifiersProvidesWeapons (props: IAddonModifiersProvidesWeapons) { function AddonModifiersProvidesWeapons (props: IAddonModifiersProvidesWeapons) {
@ -107,7 +133,7 @@ function AddonModifiersProvidesWeapons (props: IAddonModifiersProvidesWeapons) {
let weapon = props.weapons.find(w => w.hardpoint === hardpoint && w.hardpointOrder === 1 + value )?.weapon let weapon = props.weapons.find(w => w.hardpoint === hardpoint && w.hardpointOrder === 1 + value )?.weapon
if (weapon != null){ if (weapon != null){
return <WeaponSlot unitWeapons={new Map().set(hardpoint, weapon)} hardpoint={hardpoint}/>; return <WeaponSlot mod={props.mod} unitWeapons={new Map().set(hardpoint, weapon)} hardpoint={hardpoint}/>;
}else{ }else{
return "Can't find weapon, replaced by addon:" + value + " - " return "Can't find weapon, replaced by addon:" + value + " - "
} }
@ -126,7 +152,8 @@ function AddonModifiersProvidesWeapons (props: IAddonModifiersProvidesWeapons) {
interface IBuildingAddonProps { interface IBuildingAddonProps {
addon: IBuildingAddon, addon: IBuildingAddon,
building: IBuilding building: IBuilding,
mod: IMod,
} }
function BuildingAddon(props: IBuildingAddonProps){ function BuildingAddon(props: IBuildingAddonProps){
@ -145,12 +172,12 @@ function BuildingAddon(props: IBuildingAddonProps){
return requirementBuildings.length > 0 && ( return requirementBuildings.length > 0 && (
<div>&nbsp; Buildings: <img style={{verticalAlign: "top", height: 25}} <div>&nbsp; Buildings: <img style={{verticalAlign: "top", height: 25}}
src={IconUrl + requirementBuildings[0].icon.replaceAll('\\', '/')}/> src={IconUrl + requirementBuildings[0].icon.replaceAll('\\', '/')}/>
&nbsp;{" "}<a href={`/mod/${building.modId}/race/${building.race.id}/building/${requirementBuildings[0].id}`}> &nbsp;{" "}<a href={`/mod/${props.mod.id}/race/${building.race.id}/building/${requirementBuildings[0].id}`}>
{requirementBuildings[0].name} {requirementBuildings[0].name}
</a> or &nbsp;<img style={{verticalAlign: "top", height: 25}} </a> {requirementBuildings[1] !== undefined && <span>or &nbsp;<img style={{verticalAlign: "top", height: 25}}
src={IconUrl + requirementBuildings[1].icon.replaceAll('\\', '/')}/> src={IconUrl + requirementBuildings[1].icon.replaceAll('\\', '/')}/>
&nbsp;{" "}<a href={`/mod/${building.modId}/race/${building.race.id}/building/${requirementBuildings[1].id}`}> &nbsp;{" "}<a href={`/mod/${props.mod.id}/race/${building.race.id}/building/${requirementBuildings[1].id}`}>
{requirementBuildings[1].name}</a></div> {requirementBuildings[1].name}</a></span> } </div>
); );
} }
@ -158,7 +185,7 @@ function BuildingAddon(props: IBuildingAddonProps){
return <div> {rgas.map(rga => return <div> {rgas.map(rga =>
<span>&nbsp; Global addon: <img style={{verticalAlign: "top", height: 25}} <span>&nbsp; Global addon: <img style={{verticalAlign: "top", height: 25}}
src={IconUrl + rga.icon.replaceAll('\\', '/')}/> src={IconUrl + rga.icon.replaceAll('\\', '/')}/>
&nbsp;<a href={`/mod/${building.modId}/race/${building.race.id}/building/${rga.buildingId}#addon-${rga.id}`}> &nbsp;<a href={`/mod/${props.mod.id}/race/${building.race.id}/building/${rga.buildingId}#addon-${rga.id}`}>
{rga.name} {rga.name}
</a></span> </a></span>
)}</div> )}</div>
@ -224,7 +251,7 @@ function BuildingAddon(props: IBuildingAddonProps){
</Grid2> </Grid2>
{addon.addonModifiers !== null && {addon.addonModifiers !== null &&
<Grid2 size={{xs: 12, md: 12}}> <Grid2 size={{xs: 12, md: 12}}>
<AddonModifiersProvidesWeapons modifiers={addon.addonModifiers} replacedAddonModifiers={prevAddonModifiers} weapons={building.weapons}/> <AddonModifiersProvidesWeapons mod={props.mod} modifiers={addon.addonModifiers} replacedAddonModifiers={prevAddonModifiers} weapons={building.weapons}/>
</Grid2>} </Grid2>}
{addon.addonRequirement !== null && {addon.addonRequirement !== null &&
<Grid2 size={{xs: 12, md: 12}}> <Grid2 size={{xs: 12, md: 12}}>
@ -244,7 +271,7 @@ function BuildingAddon(props: IBuildingAddonProps){
{addon.addonRequirement.requirementBuildings.map(b => {addon.addonRequirement.requirementBuildings.map(b =>
<div>&nbsp; Building: <img style={{verticalAlign: "top", height: 25}} <div>&nbsp; Building: <img style={{verticalAlign: "top", height: 25}}
src={IconUrl + b.icon.replaceAll('\\', '/')}/>&nbsp; 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.mod.id +'/race/'+ building.race.id +'/building/' + b.id + "/"}>{b.name}</a></div>)
} }
{addon.addonRequirement.requirementBuildingsEither.length !== 0 && {addon.addonRequirement.requirementBuildingsEither.length !== 0 &&
renderRequirementBuilding(addon.addonRequirement.requirementBuildingsEither, building)} renderRequirementBuilding(addon.addonRequirement.requirementBuildingsEither, building)}

View File

@ -2,6 +2,11 @@
width: 50px; width: 50px;
} }
.unitIconSmall{
width: 35px;
margin-left: 30px;
}
.weaponIcon{ .weaponIcon{
width: 40px; width: 40px;
} }

View File

@ -79,6 +79,25 @@ function Building(building: IBuilding, mod: IMod) {
{building.buildCostTime}s</span>} {building.buildCostTime}s</span>}
</TableCell> </TableCell>
</TableRow> </TableRow>
{(building.requisitionIncome !== undefined && building.requisitionIncome > 0 || building.powerIncome !== undefined && building.powerIncome !== null || building.faithIncome !== undefined && building.faithIncome !== null) &&
<TableRow>
<TableCell>Resource income</TableCell>
<TableCell>
{building.requisitionIncome !== undefined && building.requisitionIncome != null &&
<span>&nbsp;<img style={{verticalAlign: "top"}}
src="/images/Resource_requisition.gif"/>&nbsp;
{building.requisitionIncome}</span>}
{building.powerIncome !== undefined && building.powerIncome > 0 &&
<span>&nbsp;<img style={{verticalAlign: "top"}}
src="/images/Resource_power.gif"/>&nbsp;
{building.powerIncome}</span>}
{building.faithIncome !== undefined && building.faithIncome > 0 &&
<span>&nbsp;<img style={{verticalAlign: "top"}}
src="/images/Resource_faith.gif"/>&nbsp;
{building.faithIncome}</span>}
</TableCell>
</TableRow>
}
<TableRow <TableRow
sx={{'&:last-child td, &:last-child th': {border: 0}}} sx={{'&:last-child td, &:last-child th': {border: 0}}}
> >
@ -126,13 +145,13 @@ function Building(building: IBuilding, mod: IMod) {
{building.addons.length > 0 && <Grid2 size={12}> {building.addons.length > 0 && <Grid2 size={12}>
<h3>Addons</h3> <h3>Addons</h3>
{building.addons.map(b => {building.addons.map(b =>
<BuildingAddon addon={b} building={building}/> <BuildingAddon mod={mod} addon={b} building={building}/>
)} )}
</Grid2> } </Grid2> }
<Grid2 size={12}> <Grid2 size={12}>
{[...mapBuildingWeapons.keys()].sort(function (a, b) { {[...mapBuildingWeapons.keys()].sort(function (a, b) {
return a - b; return a - b;
}).map(h => <WeaponSlot showOnlyDefault={true} unitWeapons={mapBuildingWeapons.get(h)} hardpoint={h}/>)} }).map(h => <WeaponSlot mod={mod} showOnlyDefault={true} unitWeapons={mapBuildingWeapons.get(h)} hardpoint={h}/>)}
</Grid2> </Grid2>
</Grid2> </Grid2>
<i className="rgdFrom">{building.filename}</i> <i className="rgdFrom">{building.filename}</i>

View File

@ -26,7 +26,7 @@ import {
ToggleButtonGroup, ToggleButtonGroup,
Typography Typography
} from "@mui/material"; } from "@mui/material";
import {ArrowBack, ArrowDropDown} from "@mui/icons-material"; import {ArrowBack, ArrowDropDown, ExpandMore} from "@mui/icons-material";
import ArmorType from "../classes/ArmorType"; import ArmorType from "../classes/ArmorType";
import Weapon from "../classes/Weapon"; import Weapon from "../classes/Weapon";
import {IRaceUnits, IUnitShort} from "../types/IUnitShort"; import {IRaceUnits, IUnitShort} from "../types/IUnitShort";
@ -50,15 +50,34 @@ function Unit (unit: IUnitShort, modId: number, raceId: String) {
</ListItem></Link>) </ListItem></Link>)
} }
function UnitSmall (unit: IUnitShort, modId: number, raceId: String) {
var unitName = ""
if (unit.name.length > 21) {
unitName = unit.name.substring(0, 20) + "...";
} else {
unitName = unit.name;
}
return (<Link href={"/mod/" + modId + "/race/" + raceId + "/unit/" + unit.id}>
{unit.icon && <img className="unitIconSmall" src={IconUrl + unit.icon.replaceAll('\\', '/')}/>}
<span style={{fontSize: 12}}>{unitName}</span>
{unit.canDetect && <span>&nbsp;<img style={{verticalAlign: "top"}}
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 (<Link href={"/mod/" + modId + "/race/" + raceId + "/building/" + building.id}><ListItem> 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('\\', '/')}/>} {building.icon && <img className="unitIcon" src={IconUrl + building.icon.replaceAll('\\', '/')}/>}
{building.name} {building.name}
{building.canDetect && <span>&nbsp;<img style={{verticalAlign: "top"}} {building.canDetect && <span>&nbsp;<img style={{verticalAlign: "top"}}
src="/images/DETECT_YES.webp"/></span>} src="/images/DETECT_YES.webp"/></span>}
</ListItem></Link>)
</ListItem></Link>
{building.units.map(unit => UnitSmall(unit, modId, raceId))}
</Grid2>)
} }
interface UnitsProps{ interface UnitsProps{
@ -108,28 +127,38 @@ class Units extends React.Component<UnitsProps, UnitsState> {
{this.state.units != null && this.state.buildings != null ? {this.state.units != null && this.state.buildings != null ?
<Grid2 container spacing={2}> <Grid2 container spacing={2}>
<Grid2 size= {{xs: 12, md: 4}}>
<h3>Infantry</h3>
{this.state.units.infantry.map(unit => Unit(unit, this.props.modId, this.props.raceId))} {this.state.buildings.buildings.map(building => Building(building, this.props.modId, this.props.raceId))}
</Grid2> <Grid2 size={{xs: 12, md: 12}}>
<Grid2 size={{xs: 12, md: 4}}> <Accordion>
<h3>Tech</h3> <AccordionSummary
<List sx={{width: '100%', maxWidth: 360, bgcolor: 'background.paper'}}> expandIcon={<ExpandMore/>}
{this.state.units.tech.map(unit => Unit(unit, this.props.modId, this.props.raceId))} aria-controls="units-accordion"
</List> id="units-accordion"
</Grid2> >
<Grid2 size={{xs: 12, md: 4}}> <h3>All units</h3>
<h3>Support</h3> </AccordionSummary>
<List sx={{width: '100%', maxWidth: 360, bgcolor: 'background.paper'}}> <AccordionDetails>
{this.state.units.support.map(unit => Unit(unit, this.props.modId, this.props.raceId))} <Grid2 size= {{xs: 12, md: 4}}>
</List> <h3>Infantry</h3>
</Grid2> {this.state.units.infantry.map(unit => Unit(unit, this.props.modId, this.props.raceId))}
<Grid2 size={{xs: 12, md: 4}}> </Grid2>
<h3>Buildings</h3> <Grid2 size={{xs: 12, md: 4}}>
<List sx={{width: '100%', maxWidth: 360, bgcolor: 'background.paper'}}> <h3>Tech</h3>
{this.state.buildings.buildings.map(building => Building(building, this.props.modId, this.props.raceId))} <List sx={{width: '100%', maxWidth: 360, bgcolor: 'background.paper'}}>
</List> {this.state.units.tech.map(unit => Unit(unit, this.props.modId, this.props.raceId))}
</Grid2> </List>
</Grid2>
<Grid2 size={{xs: 12, md: 4}}>
<h3>Support</h3>
<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>
</AccordionDetails>
</Accordion>
</Grid2><br/>
</Grid2> </Grid2>
: "Loading"} : "Loading"}

View File

@ -156,6 +156,25 @@ function Unit(unit: IUnit, mod: IMod) {
</TableCell> </TableCell>
</TableRow> </TableRow>
} }
{(unit.requisitionIncome !== undefined && unit.requisitionIncome !== null || unit.powerIncome !== undefined && unit.powerIncome !== null || unit.faithIncome !== undefined && unit.faithIncome !== null) &&
<TableRow>
<TableCell>Resource income</TableCell>
<TableCell>
{unit.requisitionIncome !== undefined && unit.requisitionIncome > 0 &&
<span>&nbsp;<img style={{verticalAlign: "top"}}
src="/images/Resource_requisition.gif"/>&nbsp;
{unit.requisitionIncome}</span>}
{unit.powerIncome !== undefined && unit.powerIncome > 0 &&
<span>&nbsp;<img style={{verticalAlign: "top"}}
src="/images/Resource_power.gif"/>&nbsp;
{unit.powerIncome}</span>}
{unit.faithIncome !== undefined && unit.faithIncome > 0 &&
<span>&nbsp;<img style={{verticalAlign: "top"}}
src="/images/Resource_faith.gif"/>&nbsp;
{unit.faithIncome}</span>}
</TableCell>
</TableRow>
}
{unit.squadMaxSize > 1 && {unit.squadMaxSize > 1 &&
<TableRow> <TableRow>
<TableCell>Squad size</TableCell> <TableCell>Squad size</TableCell>
@ -177,10 +196,11 @@ function Unit(unit: IUnit, mod: IMod) {
> >
<TableCell component="th" scope="row">Health</TableCell> <TableCell component="th" scope="row">Health</TableCell>
<TableCell component="th" scope="row"> <TableCell component="th" scope="row">
<span><img style={{verticalAlign: "top"}} <img style={{verticalAlign: "top"}}
src="/images/Health_icon.webp"/>&nbsp; src="/images/Health_icon.webp"/>&nbsp;
{unit.health} {unit.healthRegeneration > 0 && {unit.health} {unit.healthRegeneration > 0 &&
<span>+{unit.healthRegeneration}/s</span>} </span> <span>+{unit.healthRegeneration}/s</span>}
{unit.armour !== undefined && unit.armour > 0 && <span><img style={{height: 20, verticalAlign: "top"}} src="/images/defence.png"/>{unit.armour} </span> }
</TableCell> </TableCell>
</TableRow> </TableRow>
<TableRow <TableRow
@ -223,6 +243,14 @@ function Unit(unit: IUnit, mod: IMod) {
</TableCell> </TableCell>
</TableRow> </TableRow>
} }
{unit.repairSpeed !== undefined && unit.repairSpeed !== null &&
<TableRow>
<TableCell>Repair speed</TableCell>
<TableCell>
{unit.repairSpeed}
</TableCell>
</TableRow>
}
</TableBody> </TableBody>
</Table> </Table>
</TableContainer> </TableContainer>
@ -243,7 +271,7 @@ function Unit(unit: IUnit, mod: IMod) {
<SergeantShort name={s.name} icon={s.icon} canDetect={s.detectRadius > 0}/> <SergeantShort name={s.name} icon={s.icon} canDetect={s.detectRadius > 0}/>
</AccordionSummary> </AccordionSummary>
<AccordionDetails> <AccordionDetails>
<Sergeant sergeant={s}/> <Sergeant mod={mod} sergeant={s}/>
<hr/> <hr/>
</AccordionDetails> </AccordionDetails>
@ -252,7 +280,7 @@ function Unit(unit: IUnit, mod: IMod) {
<Grid2 size={12}> <Grid2 size={12}>
{[...mapWithUnitWeapons.keys()].sort(function (a, b) { {[...mapWithUnitWeapons.keys()].sort(function (a, b) {
return a - b; return a - b;
}).map(h => <WeaponSlot unitWeapons={mapWithUnitWeapons.get(h)} hardpoint={h}/>)} }).map(h => <WeaponSlot mod={mod} unitWeapons={mapWithUnitWeapons.get(h)} hardpoint={h}/>)}
</Grid2> </Grid2>
</Grid2> </Grid2>
<i className="rgdFrom">{unit.filename}</i> <i className="rgdFrom">{unit.filename}</i>

View File

@ -8,10 +8,14 @@ enum ArmorTypeValues {
VehicleLow = 'Vehicle Low', VehicleLow = 'Vehicle Low',
VehicleMedium = 'Vehicle Medium', VehicleMedium = 'Vehicle Medium',
VehicleHigh = 'Vehicle High', VehicleHigh = 'Vehicle High',
LivingMetal = 'Living Metal',
Titan = 'Titan',
Air = 'Air', Air = 'Air',
BuildingLow = 'Building Low', BuildingLow = 'Building Low',
BuildingMedium = 'Building Medium', BuildingMedium = 'Building Medium',
BuildingHigh = 'Building High', BuildingHigh = 'Building High',
BuildingSuper = 'Building Super',
DemonLow = 'Demon Low',
DemonMedium = 'Demon Medium', DemonMedium = 'Demon Medium',
DemonHigh = 'Demon High', DemonHigh = 'Demon High',
} }

View File

@ -17,7 +17,11 @@ export interface IBuilding {
buildCostFaith: number buildCostFaith: number
buildCostSouls: number buildCostSouls: number
buildCostTime: number buildCostTime: number
faithIncome?: number
powerIncome?: number
requisitionIncome?: number
health: number health: number
armour?: number
healthRegeneration: number healthRegeneration: number
sightRadius: number sightRadius: number
detectRadius: number detectRadius: number

View File

@ -1,4 +1,5 @@
import {Irace} from "./Irace"; import {Irace} from "./Irace";
import {IUnitShort} from "./IUnitShort";
export interface IRaceBuildings { export interface IRaceBuildings {
race: Irace race: Irace
@ -9,6 +10,7 @@ export interface IBuildingShort {
name: string name: string
icon: string icon: string
id: number id: number
units: IUnitShort[]
canDetect: boolean canDetect: boolean
armorTypeName: string armorTypeName: string
} }

View File

@ -26,6 +26,7 @@ export interface IUnit {
squadMaxSize: number squadMaxSize: number
squadLimit?: number squadLimit?: number
health: number health: number
armour?: number
healthRegeneration: number healthRegeneration: number
moraleDeathPenalty: number moraleDeathPenalty: number
repairMax: number repairMax: number
@ -43,6 +44,11 @@ export interface IUnit {
reinforceCostFaith ?: number reinforceCostFaith ?: number
reinforceCostSouls ?: number reinforceCostSouls ?: number
reinforceTime?: number reinforceTime?: number
repairSpeed? : number
repairCostPercent?: number
faithIncome?: number
powerIncome?: number
requisitionIncome?: number
icon: string icon: string
modId: number modId: number
sergeants: ISergeant[] sergeants: ISergeant[]
@ -62,7 +68,11 @@ export interface ISergeant {
buildCostFaith: number buildCostFaith: number
buildCostSouls: number buildCostSouls: number
buildCostTime: number buildCostTime: number
faithIncome?: number
powerIncome?: number
requisitionIncome?: number
health: number health: number
armour?: number
healthRegeneration: number healthRegeneration: number
moraleDeathPenalty: number moraleDeathPenalty: number
mass: number mass: number