- 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';
case 'Commander':
return '/images/ARM_Cmdr.webp';
case 'Living Metal':
return '/images/ARM_living_metal.webp';
case 'Vehicle Low':
return '/images/ARM_Veh_Lo.webp';
case 'Vehicle Medium':
@ -40,10 +42,16 @@ class ArmorType extends React.Component<IArmorType, any> {
return '/images/ARM_Bld_Mid.webp';
case 'Building High':
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':
return '/images/ARM_Dmn_Mid.webp';
case 'Demon High':
return '/images/ARM_Dmn_Hi.webp';
case 'Titan':
return '/images/ARM_titan.webp';
default:
return armorTypeId;
}

View File

@ -17,11 +17,13 @@ import ArmorType from "./ArmorType";
import {ExpandMore} from "@mui/icons-material";
import WeaponSlot from "./WeaponSlot";
import Vision from "./Vision";
import {IMod} from "../types/Imod";
export interface SergeantProps {
sergeant: ISergeant
sergeant: ISergeant,
mod: IMod
}
const Sergeant = (props: SergeantProps) => {
@ -51,7 +53,7 @@ const Sergeant = (props: SergeantProps) => {
<TableRow
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">
{sergeant.buildCostRequisition > 0 &&
<span>&nbsp;<img style={{verticalAlign: "top"}}
@ -74,10 +76,29 @@ const Sergeant = (props: SergeantProps) => {
{sergeant.buildCostTime}s</span>}
</TableCell>
</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
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">
<ArmorType name={sergeant.armorType.name}/>
</TableCell>
@ -85,7 +106,7 @@ const Sergeant = (props: SergeantProps) => {
<TableRow
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">
<span><img style={{verticalAlign: "top"}}
src="/images/Health_icon.webp"/>&nbsp;
@ -96,7 +117,7 @@ const Sergeant = (props: SergeantProps) => {
<TableRow
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">
<img style={{verticalAlign: "top"}}
src="/images/Kills_icon.webp"/> -{sergeant.moraleDeathPenalty}
@ -105,7 +126,7 @@ const Sergeant = (props: SergeantProps) => {
<TableRow
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">
<Vision sight={sergeant.sightRadius} detect={sergeant.detectRadius}/>
</TableCell>
@ -123,7 +144,7 @@ const Sergeant = (props: SergeantProps) => {
<Grid2 size={12}>
{[...mapWithUnitWeapons.keys()].sort(function (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>
<i className="rgdFrom">{sergeant.filename}</i>

View File

@ -20,10 +20,12 @@ import ArmorTypeNames from "../types/ArmorTypeValues";
import {IconUrl} from "../core/api";
import {ExpandMore} from "@mui/icons-material";
import Sergeant from "./Sergeant";
import {IMod} from "../types/Imod";
interface IWeaponProps {
weapon: IWeapon,
isDefault: Boolean,
mod: IMod,
}
interface IWeaponState {
@ -85,6 +87,12 @@ class Weapon extends React.Component<IWeaponProps, any> {
const buildingMedPiercing = this.getPiercingK(ArmorTypeNames.BuildingMedium)
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) => {
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">{weapon.minDamage} {weapon.maxDamage !== weapon.minDamage && -weapon.maxDamage}
scope="row">{weapon.minDamage} {weapon.maxDamage !== weapon.minDamage && "- " + weapon.maxDamage}
</TableCell>
</TableRow>
<TableRow
@ -267,63 +275,143 @@ class Weapon extends React.Component<IWeaponProps, any> {
<ToggleButton size="small" value="one hit">One hit average damage</ToggleButton>
</ToggleButtonGroup>
<TableContainer>
<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>
{ this.props.mod !== undefined && this.props.mod.name.includes("Ultimate Apocalypse") ?
<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.DemonLow}/></StyledTableCell>
<StyledTableCell><ArmorType
name={ArmorTypeNames.DemonMedium}/></StyledTableCell>
<StyledTableCell><ArmorType
name={ArmorTypeNames.DemonHigh}/></StyledTableCell>
<StyledTableCell><ArmorType name={ArmorTypeNames.Air}/></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(demonLowPiercing)}</StyledTableCell>
<StyledTableCell>{getTotalDamage(demonPiercing)}</StyledTableCell>
<StyledTableCell>{getTotalDamage(demonHighPiercing)}</StyledTableCell>
<StyledTableCell>{getTotalDamage(airPiercing, true)}</StyledTableCell>
</TableRow>
</TableBody>
<TableHead>
<TableRow>
<StyledTableCell><ArmorType
name={ArmorTypeNames.VehicleLow}/></StyledTableCell>
<StyledTableCell><ArmorType
name={ArmorTypeNames.VehicleMedium}/></StyledTableCell>
<StyledTableCell><ArmorType
name={ArmorTypeNames.VehicleHigh}/></StyledTableCell>
<StyledTableCell><ArmorType
name={ArmorTypeNames.LivingMetal}/></StyledTableCell>
<StyledTableCell><ArmorType
name={ArmorTypeNames.Titan}/></StyledTableCell>
<StyledTableCell><ArmorType
name={ArmorTypeNames.BuildingLow}/></StyledTableCell>
<StyledTableCell><ArmorType
name={ArmorTypeNames.BuildingMedium}/></StyledTableCell>
<StyledTableCell><ArmorType
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>
<i className="rgdFrom">{weapon.filename}</i>
</Grid2>

View File

@ -1,10 +1,12 @@
import Weapon from "./Weapon";
import React from "react";
import {IWeapon} from "../types/IUnit";
import {IMod} from "../types/Imod";
export interface WeaponSlotProps {
hardpoint: number,
unitWeapons: Map<number, IWeapon> | undefined,
mod: IMod,
showOnlyDefault?: boolean ,
}
@ -31,7 +33,7 @@ const WeaponSlot= (props: WeaponSlotProps) => {
return (
<div>
<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>
)
}
@ -41,7 +43,7 @@ const WeaponSlot= (props: WeaponSlotProps) => {
<h3>{header}</h3>
{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>
)

View File

@ -19,6 +19,7 @@ import {ExpandMore} from "@mui/icons-material";
import {IconUrl} from "../../core/api";
import {IBuildingShort} from "../../types/IBuildingShort";
import {goToAddon} from "../../pages/BuildingPage";
import {IMod} from "../../types/Imod";
interface IAddonModifiersProvidesTable{
modifiers: IAddonModifier[],
@ -34,10 +35,22 @@ function AddonModifiersProvidesTable (props: IAddonModifiersProvidesTable) {
return 'Health';
case 'modifiers\\keen_sight_radius_modifier.lua':
return 'Detect';
case 'modifiers\\sight_radius_modifier.lua':
return 'Sight';
case 'modifiers\\garrison_requisition_modifier.lua':
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:
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'/>;
case 'modifiers\\keen_sight_radius_modifier.lua':
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':
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:
return mt;
return "";
}
}
function getChangeDescription(ref: String) {
function getChangeDescription(ref: String, v: number) {
switch(ref) {
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':
return 'x';
return 'x' + v;
case 'type_modifierusagetype\\tp_mod_usage_percentage.lua':
return '%';
return '%' + v;
case 'type_modifierusagetype\\tp_mod_usage_enable.lua':
return ' enable ';
if(v === 1){
return ' enable ';
} else {
return ' disable ';
}
default:
return ref;
}
@ -81,7 +106,7 @@ function AddonModifiersProvidesTable (props: IAddonModifiersProvidesTable) {
sx={{'&:last-child td, &:last-child th': {border: 0}}}
>
<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>
)}
</Table>
@ -92,7 +117,8 @@ function AddonModifiersProvidesTable (props: IAddonModifiersProvidesTable) {
interface IAddonModifiersProvidesWeapons{
modifiers: IAddonModifier[],
replacedAddonModifiers: IAddonModifier[],
weapons: WeaponHardpoint[]
weapons: WeaponHardpoint[],
mod: IMod,
}
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
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{
return "Can't find weapon, replaced by addon:" + value + " - "
}
@ -126,7 +152,8 @@ function AddonModifiersProvidesWeapons (props: IAddonModifiersProvidesWeapons) {
interface IBuildingAddonProps {
addon: IBuildingAddon,
building: IBuilding
building: IBuilding,
mod: IMod,
}
function BuildingAddon(props: IBuildingAddonProps){
@ -145,12 +172,12 @@ function BuildingAddon(props: IBuildingAddonProps){
return requirementBuildings.length > 0 && (
<div>&nbsp; Buildings: <img style={{verticalAlign: "top", height: 25}}
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}
</a> or &nbsp;<img style={{verticalAlign: "top", height: 25}}
src={IconUrl + requirementBuildings[1].icon.replaceAll('\\', '/')}/>
&nbsp;{" "}<a href={`/mod/${building.modId}/race/${building.race.id}/building/${requirementBuildings[1].id}`}>
{requirementBuildings[1].name}</a></div>
</a> {requirementBuildings[1] !== undefined && <span>or &nbsp;<img style={{verticalAlign: "top", height: 25}}
src={IconUrl + requirementBuildings[1].icon.replaceAll('\\', '/')}/>
&nbsp;{" "}<a href={`/mod/${props.mod.id}/race/${building.race.id}/building/${requirementBuildings[1].id}`}>
{requirementBuildings[1].name}</a></span> } </div>
);
}
@ -158,7 +185,7 @@ function BuildingAddon(props: IBuildingAddonProps){
return <div> {rgas.map(rga =>
<span>&nbsp; Global addon: <img style={{verticalAlign: "top", height: 25}}
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}
</a></span>
)}</div>
@ -224,7 +251,7 @@ function BuildingAddon(props: IBuildingAddonProps){
</Grid2>
{addon.addonModifiers !== null &&
<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>}
{addon.addonRequirement !== null &&
<Grid2 size={{xs: 12, md: 12}}>
@ -244,7 +271,7 @@ function BuildingAddon(props: IBuildingAddonProps){
{addon.addonRequirement.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.mod.id +'/race/'+ building.race.id +'/building/' + b.id + "/"}>{b.name}</a></div>)
}
{addon.addonRequirement.requirementBuildingsEither.length !== 0 &&
renderRequirementBuilding(addon.addonRequirement.requirementBuildingsEither, building)}

View File

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

View File

@ -79,6 +79,25 @@ function Building(building: IBuilding, mod: IMod) {
{building.buildCostTime}s</span>}
</TableCell>
</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
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}>
<h3>Addons</h3>
{building.addons.map(b =>
<BuildingAddon addon={b} building={building}/>
<BuildingAddon mod={mod} addon={b} building={building}/>
)}
</Grid2> }
<Grid2 size={12}>
{[...mapBuildingWeapons.keys()].sort(function (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>
<i className="rgdFrom">{building.filename}</i>

View File

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

View File

@ -156,6 +156,25 @@ function Unit(unit: IUnit, mod: IMod) {
</TableCell>
</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 &&
<TableRow>
<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">
<span><img style={{verticalAlign: "top"}}
<img style={{verticalAlign: "top"}}
src="/images/Health_icon.webp"/>&nbsp;
{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>
</TableRow>
<TableRow
@ -223,6 +243,14 @@ function Unit(unit: IUnit, mod: IMod) {
</TableCell>
</TableRow>
}
{unit.repairSpeed !== undefined && unit.repairSpeed !== null &&
<TableRow>
<TableCell>Repair speed</TableCell>
<TableCell>
{unit.repairSpeed}
</TableCell>
</TableRow>
}
</TableBody>
</Table>
</TableContainer>
@ -243,7 +271,7 @@ function Unit(unit: IUnit, mod: IMod) {
<SergeantShort name={s.name} icon={s.icon} canDetect={s.detectRadius > 0}/>
</AccordionSummary>
<AccordionDetails>
<Sergeant sergeant={s}/>
<Sergeant mod={mod} sergeant={s}/>
<hr/>
</AccordionDetails>
@ -252,7 +280,7 @@ function Unit(unit: IUnit, mod: IMod) {
<Grid2 size={12}>
{[...mapWithUnitWeapons.keys()].sort(function (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>
<i className="rgdFrom">{unit.filename}</i>

View File

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

View File

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

View File

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

View File

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