- Add building page

- Rework weapon ui (hide to spoiler and adapt to mobile)
- Add additional weapon params (throw force, min radius, damage radius etc)
- Add squad start/max sise, unit mass, souls and faith to cost
This commit is contained in:
Anibus
2025-04-18 14:06:26 +03:00
parent 3e78fa3807
commit 5751cec4de
15 changed files with 576 additions and 211 deletions
+214 -159
View File
@@ -1,6 +1,9 @@
import React from "react";
import {IWeapon, IWeaponPiercing} from "../types/IUnit";
import {
Accordion,
AccordionDetails,
AccordionSummary,
Grid2,
Paper,
styled,
@@ -15,13 +18,15 @@ import {
import ArmorType from "./ArmorType";
import ArmorTypeNames from "../types/ArmorTypeValues";
import {IconUrl} from "../core/api";
import {ExpandMore} from "@mui/icons-material";
import Sergeant from "./Sergeant";
interface IWeaponProps{
interface IWeaponProps {
weapon: IWeapon,
isDefault: Boolean,
}
interface IWeaponState{
interface IWeaponState {
currentTable: "dps" | "one hit",
}
@@ -45,24 +50,23 @@ class Weapon extends React.Component<IWeaponProps, any> {
getPiercingK(armorType: string): number {
const weaponPiercing = this.props.weapon.weaponPiercings.find((p) => p.armorType.name === armorType)
return (typeof weaponPiercing !== "undefined" ? weaponPiercing.piercingValue : 10 ) / 100
const weaponPiercing = this.props.weapon.weaponArmorPiercing.find((p) => p.armorType.name === armorType)
return (typeof weaponPiercing !== "undefined" ? weaponPiercing.piercingValue : 10) / 100
}
handleChange = (e: React.MouseEvent, selectedDamageTable: String | null) => {
handleChange = (e: React.MouseEvent, selectedDamageTable: String | null) => {
this.setState({
currentTable: selectedDamageTable
});
}
render() {
const weapon = this.props.weapon
const dpsK = (this.state.currentTable == "dps") ? weapon.accuracy * (1/(weapon.reloadTime - (weapon.reloadTime % 0.125))) : 1
const dpsK = (this.state.currentTable == "dps") ? weapon.accuracy * (1 / (weapon.reloadTime - (weapon.reloadTime % 0.125))) : 1
const infLowPiercing = this.getPiercingK(ArmorTypeNames.InfantryLow)
const infMedPiercing = this.getPiercingK(ArmorTypeNames.InfantryMedium)
@@ -83,16 +87,16 @@ class Weapon extends React.Component<IWeaponProps, any> {
const getTotalDamage = (damagePiercing: number, isAir: boolean = false) => {
if(!isAir && !weapon.canAttackGround && !weapon.isMeleeWeapon) return ""
if(isAir && !weapon.canAttackAir) return ""
if (!isAir && !weapon.canAttackGround && !weapon.isMeleeWeapon) return ""
if (isAir && !weapon.canAttackAir) return ""
var minDamage = damagePiercing * weapon.minDamage
var maxDamage = damagePiercing * weapon.maxDamage
if(minDamage < weapon.minDamageValue){
if (minDamage < weapon.minDamageValue) {
minDamage = weapon.minDamageValue
}
if(maxDamage < weapon.minDamageValue){
if (maxDamage < weapon.minDamageValue) {
maxDamage = weapon.minDamageValue
}
@@ -106,7 +110,7 @@ class Weapon extends React.Component<IWeaponProps, any> {
}
const StyledTableCell = styled(TableCell)(({ theme }) => ({
const StyledTableCell = styled(TableCell)(({theme}) => ({
[`&.${tableCellClasses.head}`]: {
backgroundColor: "rgb(234, 234, 234)",
color: theme.palette.common.white,
@@ -119,162 +123,213 @@ class Weapon extends React.Component<IWeaponProps, any> {
}));
return (
<div>
<h4>{ weapon.name ? weapon.name : this.humanReadableName(weapon.filename)}</h4>
<Grid2 container spacing={2}>
<div style={{marginBottom: 10}}><Accordion>
<AccordionSummary
expandIcon={<ExpandMore/>}
aria-controls="panel1-content"
id="panel1-header"
>
<span style={{fontSize: 18}}> {!this.props.isDefault && weapon.icon && weapon.haveEquipButton ?
<img className="weaponIcon" src={IconUrl + weapon.icon.replaceAll('\\', '/')}/> : (
weapon.isMeleeWeapon ?
<img className="weaponIcon" src="/images/MeleeStance_icon_bw.jpg"/> :
<img className="weaponIcon" src="/images/RangedStance_icon_bw.jpg"/>
)} {weapon.name ? weapon.name : this.humanReadableName(weapon.filename)} </span>
</AccordionSummary>
<AccordionDetails>
<Grid2 container spacing={2}>
<Grid2 size={12}>
<div style={{whiteSpace: "pre-wrap"}}>
<span
className="weaponDescription">{this.props.isDefault ? "Default weapon" : weapon.description}</span>
</div>
</Grid2>
<Grid2 size= {{xs: 12, md: 3}}>
<TableContainer component={Paper}>
<Table size="small" aria-label="a dense table">
<TableBody>
<TableRow
sx={{'&:last-child td, &:last-child th': {border: 0}}}
>
<TableCell component="th" scope="row">Base damage</TableCell>
<TableCell component="th"
scope="row">{weapon.minDamage} {weapon.maxDamage !== weapon.minDamage && -weapon.maxDamage}
</TableCell>
</TableRow>
<TableRow
sx={{'&:last-child td, &:last-child th': {border: 0}}}
>
<TableCell component="th" scope="row">Accuracy</TableCell>
<TableCell component="th" scope="row">{weapon.accuracy.toFixed(2)}
</TableCell>
</TableRow>
<TableRow
sx={{'&:last-child td, &:last-child th': {border: 0}}}
>
<TableCell component="th" scope="row">Accuracy moving</TableCell>
<TableCell component="th"
scope="row">{weapon.setupTime === 0 ? (weapon.accuracy - weapon.accuracyReductionMoving).toFixed(2) : "-"}
</TableCell>
</TableRow>
<TableRow
sx={{'&:last-child td, &:last-child th': {border: 0}}}
>
<TableCell component="th" scope="row">Reload time</TableCell>
<TableCell component="th"
scope="row">{weapon.reloadTime}
</TableCell>
</TableRow>
<TableRow
sx={{'&:last-child td, &:last-child th': {border: 0}}}
>
<TableCell component="th" scope="row">Max range</TableCell>
<TableCell component="th"
scope="row">{weapon.maxRange ? (weapon.maxRange) : "-"}
</TableCell>
</TableRow>
<TableRow
sx={{'&:last-child td, &:last-child th': {border: 0}}}
>
<TableCell component="th" scope="row">Setup time</TableCell>
<TableCell component="th"
scope="row">{weapon.setupTime != 0 ? (weapon.setupTime).toFixed(2) : "-"}
</TableCell>
</TableRow>
{(weapon.costRequisition > 0 || weapon.costPower > 0) &&
<Grid2 size={{xs: 12, md: 4}}>
<TableContainer component={Paper}>
<Table size="small" aria-label="a dense table">
{/*<TableHead><TableRow><TableCell component="th" scope="row"><b>Damage</b></TableCell><TableCell/></TableRow></TableHead>*/}
<TableBody>
<TableRow
sx={{'&:last-child td, &:last-child th': {border: 0}}}
>
<TableCell component="th" scope="row">Base damage</TableCell>
<TableCell component="th"
scope="row">{weapon.minDamage} {weapon.maxDamage !== weapon.minDamage && -weapon.maxDamage}
</TableCell>
</TableRow>
<TableRow
sx={{'&:last-child td, &:last-child th': {border: 0}}}
>
<TableCell component="th" scope="row">Accuracy</TableCell>
<TableCell component="th"
scope="row">{weapon.accuracy.toFixed(3).replace(/[,.]?0+$/, '')}
</TableCell>
</TableRow>
<TableRow
sx={{'&:last-child td, &:last-child th': {border: 0}}}
>
<TableCell component="th" scope="row">Accuracy moving</TableCell>
<TableCell component="th"
scope="row">{weapon.setupTime === 0 && weapon.accuracy - weapon.accuracyReductionMoving > 0 ? (weapon.accuracy - weapon.accuracyReductionMoving).toFixed(3).replace(/[,.]?0+$/, '') : "-"}
</TableCell>
</TableRow>
</TableBody>
</Table>
</TableContainer>
</Grid2>
<Grid2 size={{xs: 12, md: 4}}>
<TableContainer component={Paper}>
<Table size="small" aria-label="a dense table">
<TableBody>
<TableRow
sx={{'&:last-child td, &:last-child th': {border: 0}}}
>
<TableCell component="th" scope="row">Reload time</TableCell>
<TableCell component="th"
scope="row">{weapon.reloadTime}
</TableCell>
</TableRow>
<TableRow
sx={{'&:last-child td, &:last-child th': {border: 0}}}
>
<TableCell component="th" scope="row">Range</TableCell>
<TableCell component="th"
scope="row">{weapon.maxRange ? (weapon.maxRange) : "-"} {weapon.minRange ? "(min " + (weapon.minRange) + ")" : ""}
</TableCell>
</TableRow>
<TableRow
sx={{'&:last-child td, &:last-child th': {border: 0}}}
>
<TableCell component="th" scope="row">Damage radius</TableCell>
<TableCell component="th"
scope="row">{weapon.damageRadius ? (weapon.damageRadius) : "-"}
</TableCell>
</TableRow>
</TableBody>
</Table>
</TableContainer>
</Grid2>
<Grid2 size={{xs: 12, md: 4}}>
<TableContainer component={Paper}>
<Table size="small" aria-label="a dense table">
<TableBody>
<TableRow
sx={{'&:last-child td, &:last-child th': {border: 0}}}
>
<TableCell component="th" scope="row">Cost</TableCell>
{(weapon.costRequisition > 0 || weapon.costPower > 0) ?
<TableCell component="th"
scope="row">
{weapon.costRequisition > 0 &&
<span>&nbsp;<img style={{verticalAlign: "top"}}
src="/images/Resource_requisition.gif"/>&nbsp;
{weapon.costRequisition.toFixed(0)}</span>}
{weapon.costPower > 0 &&
<span>&nbsp;<img style={{verticalAlign: "top"}}
src="/images/Resource_power.gif"/>&nbsp;
{weapon.costPower.toFixed(0)}</span>}
</TableCell> : <TableCell component="th" scope="row"> - </TableCell>
}
</TableRow>
<TableRow
sx={{'&:last-child td, &:last-child th': {border: 0}}}
>
<TableCell component="th" scope="row">Setup time</TableCell>
<TableCell component="th"
scope="row">
{weapon.costRequisition > 0 && <span>&nbsp;<img style={{verticalAlign: "top"}}
src="/images/Resource_requisition.gif"/>&nbsp;
{weapon.costRequisition}</span>}
{weapon.costPower > 0 && <span>&nbsp;<img style={{verticalAlign: "top"}}
src="/images/Resource_power.gif"/>&nbsp;
{weapon.costPower}</span>}
scope="row">{weapon.setupTime != 0 ? (weapon.setupTime).toFixed(3).replace(/[,.]?0+$/, '') : "-"}
</TableCell>
</TableRow>
}
</TableBody>
</Table>
</TableContainer>
<TableRow
sx={{'&:last-child td, &:last-child th': {border: 0}}}
>
<TableCell component="th" scope="row">Throw force</TableCell>
<TableCell component="th"
scope="row">{weapon.throwForceMin != 0 ? weapon.throwForceMin + " - " + weapon.throwForceMax : "-"}
</TableCell>
</TableRow>
</TableBody>
</Table>
</TableContainer>
</Grid2>
<Grid2 size={12}>
<ToggleButtonGroup
color="primary"
exclusive
value={this.state.currentTable}
onChange={this.handleChange}
aria-label="Platform"
>
<ToggleButton size="small" value="dps">Dps</ToggleButton>
<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>
</TableContainer>
<i className="rgdFrom">{weapon.filename}</i>
</Grid2>
</Grid2>
<Grid2 size={9}>
<div style={{whiteSpace: "pre-wrap"}}>
{!this.props.isDefault && weapon.icon ? <img className="unitIcon" src={IconUrl + weapon.icon.replaceAll('\\', '/')}/> : (
weapon.isMeleeWeapon ?
<img src="/images/MeleeStance_icon_bw.jpg"/> :
<img src="/images/RangedStance_icon_bw.jpg"/>
) } <br/>
{this.props.isDefault ? "Default weapon" : weapon.description}
</div>
</Grid2>
<Grid2 size={12}>
<ToggleButtonGroup
color="primary"
exclusive
value={this.state.currentTable}
onChange={this.handleChange}
aria-label="Platform"
>
<ToggleButton size="small" value="dps">Dps</ToggleButton>
<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>
</TableContainer>
<i className="rgdFrom">{weapon.filename}</i>
</Grid2>
</Grid2>
</div>
</AccordionDetails>
</Accordion></div>
);
}
}