Optimization update

- request weapons and researches only when user open accordion
- add building affects data
This commit is contained in:
Anibus
2025-12-07 00:54:42 +03:00
parent f0fde93d7b
commit 318b5c47e9
17 changed files with 638 additions and 502 deletions
+23 -7
View File
@@ -5,6 +5,7 @@ import {IconUrl} from "../core/api";
import {Irace} from "../types/Irace";
import {IAffectedData} from "../types/IAffectedData";
import {IMod} from "../types/Imod";
import AvTimerOutlinedIcon from "@mui/icons-material/AvTimer";
interface IModifiersProvidesTable{
modifiers: IModifier[],
@@ -13,7 +14,7 @@ interface IModifiersProvidesTable{
interface IModifiersProvidesResearchTable{
modifiers: IModifier[],
affectedData: IAffectedData,
mod: IMod,
modId: number,
race: Irace,
}
@@ -63,6 +64,10 @@ function getModName(mt: String){
return 'Change armor type';
case 'modifiers\\cost_requisition_modifier.lua':
return 'Requisition cost';
case 'modifiers\\cost_power_modifier.lua':
return 'Power cost';
case 'modifiers\\cost_time_modifier.lua':
return 'Cost time';
default:
return mt.replace("modifiers\\", "").replace("modifier.lua", "").replace(".lua", "").replaceAll("_", " ");
}
@@ -84,8 +89,19 @@ function getModIcon(mt: String){
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'/>;
case 'modifiers\\cost_power_modifier.lua':
return <img style={{verticalAlign: "top"}} src='/images/Resource_power.gif'/>;
case 'modifiers\\morale_maximum_squad_modifier.lua':
return <img style={{verticalAlign: "top"}} src='/images/ARM_Morale.webp'/>;
case 'modifiers\\support_cap_player_modifier.lua':
return <img style={{verticalAlign: "top"}} src='/images/Resource_cap_vehicle.gif'/>;
case 'modifiers\\squad_cap_player_modifier.lua':
return <img style={{verticalAlign: "top"}} src='/images/Resource_cap_infantry.gif'/>;
case 'modifiers\\population_cap_player_modifier.lua':
return <img style={{verticalAlign: "top"}} src='/images/Resource_orksquadcap.gif'/>;
case 'modifiers\\cost_time_modifier.lua':
return <AvTimerOutlinedIcon
style={{verticalAlign: "top", fontSize: "18px"}}/>;
default:
return "";
}
@@ -142,7 +158,7 @@ export function ModifiersProvidesTable (props: IModifiersProvidesResearchTable)
if(affectedUnit != null) {
return <span><img style={{verticalAlign: "top", height: 25}}
src={getIcon(affectedUnit.icon)}/><a
href={`/mod/${props.mod.id}/race/${props.race.id}/unit/${affectedUnit.id}`}>
href={`/mod/${props.modId}/race/${props.race.id}/unit/${affectedUnit.id}`}>
{affectedUnit.name}
</a></span>
}
@@ -151,14 +167,14 @@ export function ModifiersProvidesTable (props: IModifiersProvidesResearchTable)
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/${props.mod.id}/race/${props.race.id}/unit/${affectedSergeant.unit.id}`}>
href={`/mod/${props.modId}/race/${props.race.id}/unit/${affectedSergeant.unit.id}`}>
{affectedSergeant.unit.name}
</a></span>
}
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/${props.mod.id}/race/${props.race.id}/building/${affectedBuilding.id}`}>
href={`/mod/${props.modId}/race/${props.race.id}/building/${affectedBuilding.id}`}>
{affectedBuilding.name}
</a> </span>
}
@@ -168,7 +184,7 @@ export function ModifiersProvidesTable (props: IModifiersProvidesResearchTable)
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/${props.mod.id}/race/${props.race.id}/unit/${unit.id}`}>
href={`/mod/${props.modId}/race/${props.race.id}/unit/${unit.id}`}>
{unit.name}
</a> </span>
)
@@ -179,7 +195,7 @@ export function ModifiersProvidesTable (props: IModifiersProvidesResearchTable)
src={getIcon(affectedSergeant.icon)}/>{affectedSergeant.name} at <img
style={{verticalAlign: "top", height: 25}}
src={getIcon(affectedSergeant.unit.icon)}/><a
href={`/mod//${props.mod.id}/race/${props.race.id}/unit/${affectedSergeant.unit.id}`}>
href={`/mod//${props.modId}/race/${props.race.id}/unit/${affectedSergeant.unit.id}`}>
{affectedSergeant.unit.name}
</a> </span>
)
@@ -187,7 +203,7 @@ export function ModifiersProvidesTable (props: IModifiersProvidesResearchTable)
{
affectedWeapon.buildings.map(affectedBuilding =>
<span><img style={{verticalAlign: "top", height: 25}} src={getIcon(affectedBuilding.icon)}/><a
href={`/mod//${props.mod.id}/race/${props.race.id}/building/${affectedBuilding.id}`}>
href={`/mod//${props.modId}/race/${props.race.id}/building/${affectedBuilding.id}`}>
{affectedBuilding.name}
</a> </span>
)
+1 -1
View File
@@ -89,7 +89,7 @@ function Required (props: IRequiredProps) {
</div>
}
{requirement.requirementBuildings.map(b =>
<div>&nbsp; Building: <img style={{verticalAlign: "top", height: 25}}
<div key={b.id}>&nbsp; Building: <img style={{verticalAlign: "top", height: 25}}
src={IconUrl + b.icon.replaceAll('\\', '/')}/>&nbsp;
<a href= {'/mod/'+ props.modId +'/race/'+ props.raceId +'/building/' + b.id + "/"}>{b.name}</a></div>)
}
+4 -17
View File
@@ -1,20 +1,8 @@
import React, {useState} from "react";
import {IconUrl, UserUrl} from "../core/api";
import {ISergeant, IWeapon} from "../types/IUnit";
import {
Accordion, AccordionDetails,
AccordionSummary,
Grid2,
Paper,
Table,
TableBody,
TableCell,
TableContainer,
TableRow, Tooltip
} from "@mui/material";
import React from "react";
import {ISergeant, IShortWeapon} from "../types/IUnit";
import {Grid2, Paper, Table, TableBody, TableCell, TableContainer, TableRow, Tooltip} from "@mui/material";
import AvTimerOutlinedIcon from "@mui/icons-material/AvTimer";
import ArmorType from "./ArmorType";
import {ExpandMore} from "@mui/icons-material";
import WeaponSlot from "./WeaponSlot";
import Vision from "./Vision";
import {IMod} from "../types/Imod";
@@ -23,7 +11,6 @@ import {Irace} from "../types/Irace";
import Required from "./Required";
export interface SergeantProps {
sergeant: ISergeant,
mod: IMod,
@@ -34,7 +21,7 @@ const Sergeant = (props: SergeantProps) => {
const sergeant = props.sergeant
let mapWithUnitWeapons: Map<number, Map<number, IWeapon>> = new Map();
let mapWithUnitWeapons: Map<number, Map<number, IShortWeapon>> = new Map();
sergeant.weapons.forEach(weapon => {
const weaponMap = mapWithUnitWeapons.get(weapon.hardpoint)
+18 -11
View File
@@ -32,17 +32,24 @@ export default function UnitsTable(prop: {modId: number}) {
useEffect(() => {
fetch(AvailableBuildings + "/mod/" + prop.modId)
.then(res => res.json())
.then((resBuildings: IRaceBuildings[]) => {
fetch(AvailableUnits + "/mod/" + prop.modId)
.then(res => res.json())
.then((res: IRaceUnits[]) => {
setUnitsTable({
racesUnits : res,
racesBuildings: resBuildings
fetch(AvailableUnits + "/mod/" + prop.modId)
.then(resUnits => resUnits.json())
.then((resUnits: IRaceUnits[]) => {
if(resUnits.length <= 10){
fetch(AvailableBuildings + "/mod/" + prop.modId)
.then(resBuildings => resBuildings.json())
.then((resBuildings: IRaceBuildings[]) => {
setUnitsTable({
racesUnits : resUnits,
racesBuildings: resBuildings
});
});
})
}else {
setUnitsTable({
racesUnits : resUnits,
racesBuildings: []
});
}
})
}, []);
@@ -149,7 +156,7 @@ export default function UnitsTable(prop: {modId: number}) {
function generateRaceList(unitsTable: IUnitsTable){
return unitsTable.racesUnits.sort(function (a, b) {
return a.race.name.localeCompare(b.race.name);
}).map(raceUnits => <h5><Link color="inherit"
}).map(raceUnits => <h5 key={raceUnits.race.id}><Link color="inherit"
href={"/mod/" + prop.modId + "/race/" + raceUnits.race.id}>{raceUnits.race.name}</Link><br/>
</h5>)
}
+6 -392
View File
@@ -1,32 +1,14 @@
import React from "react";
import {IWeapon, IWeaponPiercing} from "../types/IUnit";
import {
Accordion,
AccordionDetails,
AccordionSummary,
Grid2,
Paper,
styled,
Table,
TableBody,
TableCell,
tableCellClasses,
TableContainer,
TableHead,
TableRow, ToggleButton, ToggleButtonGroup
} from "@mui/material";
import ArmorType from "./ArmorType";
import ArmorTypeNames from "../types/ArmorTypeValues";
import {IShortWeapon, IWeapon} from "../types/IUnit";
import {Accordion, AccordionDetails, AccordionSummary} from "@mui/material";
import {IconUrl} from "../core/api";
import {ExpandMore} from "@mui/icons-material";
import Sergeant from "./Sergeant";
import {IMod} from "../types/Imod";
import {Irace} from "../types/Irace";
import {renderAffectedResearches} from "./building/Research";
import Required from "./Required";
import WeaponFull from "./WeaponFull";
interface IWeaponProps {
weapon: IWeapon,
weapon: IShortWeapon,
isDefault: Boolean,
renderRequired: Boolean,
mod: IMod,
@@ -46,98 +28,13 @@ class Weapon extends React.Component<IWeaponProps, any> {
return firstUpper.replaceAll('_', ' ').replace('.rgd', '');
}
constructor(props: any) {
super(props);
this.state = ({
currentTable: 'dps'
});
}
getPiercingK(armorType: string): number {
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) => {
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 infLowPiercing = this.getPiercingK(ArmorTypeNames.InfantryLow)
const infMedPiercing = this.getPiercingK(ArmorTypeNames.InfantryMedium)
const infHighPiercing = this.getPiercingK(ArmorTypeNames.InfantryHigh)
const infHeavyMedPiercing = this.getPiercingK(ArmorTypeNames.InfantryHeavyMedium)
const infHeavyHighPiercing = this.getPiercingK(ArmorTypeNames.InfantryHeavyHigh)
const demonPiercing = this.getPiercingK(ArmorTypeNames.DemonMedium)
const demonHighPiercing = this.getPiercingK(ArmorTypeNames.DemonHigh)
const commanderPiercing = this.getPiercingK(ArmorTypeNames.Commander)
const airPiercing = this.getPiercingK(ArmorTypeNames.Air)
const vehLowPiercing = this.getPiercingK(ArmorTypeNames.VehicleLow)
const vehMedPiercing = this.getPiercingK(ArmorTypeNames.VehicleMedium)
const vehHighPiercing = this.getPiercingK(ArmorTypeNames.VehicleHigh)
const buildingLowPiercing = this.getPiercingK(ArmorTypeNames.BuildingLow)
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 ""
if (isAir && !weapon.canAttackAir) return ""
var minDamage = damagePiercing * weapon.minDamage
var maxDamage = damagePiercing * weapon.maxDamage
if (minDamage < weapon.minDamageValue) {
minDamage = weapon.minDamageValue
}
if (maxDamage < weapon.minDamageValue) {
maxDamage = weapon.minDamageValue
}
const averageDmg = (minDamage + maxDamage) / 2
return (averageDmg * dpsK).toFixed(2)
}
const getMoraleDamage = () => {
return (weapon.moraleDamage * dpsK / 2).toFixed(2)
}
const StyledTableCell = styled(TableCell)(({theme}) => ({
[`&.${tableCellClasses.head}`]: {
backgroundColor: "rgb(244,244,244)",
marginRight: 'auto',
marginLeft: 'auto',
paddingLeft: 10
},
[`&.${tableCellClasses.body}`]: {
fontSize: 12,
textAlign: 'center',
paddingRight: 18,
paddingLeft: 10
},
}));
return (
<div style={{marginBottom: 10}}><Accordion>
<div style={{marginBottom: 10}}><Accordion TransitionProps={{ unmountOnExit: true, timeout: 100 }}>
<AccordionSummary
expandIcon={<ExpandMore/>}
aria-controls="panel1-content"
@@ -151,290 +48,7 @@ class Weapon extends React.Component<IWeaponProps, any> {
)} {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: 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.setupTime != 0 ? (weapon.setupTime).toFixed(3).replace(/[,.]?0+$/, '') : "-"}
</TableCell>
</TableRow>
<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>
{ this.props.mod !== undefined && this.props.mod.name.includes("Ultimate Apocalypse") ?
<Table sx={{marginTop: "10px", textAlign: "center"}} 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"/>
<div style={{width: 20, fontSize: 12, height: 50}}>
<i>Morale</i></div>
</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"/>
<div style={{width: 20, fontSize: 12, height: 50}}>
<i>Morale</i></div>
</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>
{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>
</Grid2>
<WeaponFull weaponId={weapon.id} isDefault={this.props.isDefault} mod={this.props.mod} race={this.props.race} haveReinforceMenu={this.props.haveReinforceMenu}/>
</AccordionDetails>
</Accordion></div>
);
+430
View File
@@ -0,0 +1,430 @@
import React, {useEffect, useState} from "react";
import {AvailableBuildings, AvailableUnits, UserUrl, WeaponUrl} from "../core/api";
import {IWeapon} from "../types/IUnit";
import ArmorTypeNames from "../types/ArmorTypeValues";
import {
AccordionDetails,
Grid2,
Paper,
styled,
Table,
TableBody,
TableCell,
tableCellClasses,
TableContainer, TableHead,
TableRow, ToggleButton, ToggleButtonGroup
} from "@mui/material";
import ArmorType from "./ArmorType";
import Required from "./Required";
import {renderAffectedResearches} from "./building/Research";
import {IMod} from "../types/Imod";
import {Irace} from "../types/Irace";
import {IRaceUnits} from "../types/IUnitShort";
import {IRaceBuildings} from "../types/IBuildingShort";
interface IWeaponFull {
currentTable: string;
weapon?: IWeapon;
}
export default function WeaponFull(props: {weaponId: number, isDefault: Boolean, mod: IMod, race: Irace, haveReinforceMenu: Boolean}) {
const [weaoponFull, setWeaponFull] = useState<IWeaponFull>({
currentTable: "dps",
weapon: undefined,
});
const StyledTableCell = styled(TableCell)(({theme}) => ({
[`&.${tableCellClasses.head}`]: {
backgroundColor: "rgb(244,244,244)",
marginRight: 'auto',
marginLeft: 'auto',
paddingLeft: 10
},
[`&.${tableCellClasses.body}`]: {
fontSize: 12,
textAlign: 'center',
paddingRight: 18,
paddingLeft: 10
},
}));
useEffect(() => {
fetch(WeaponUrl + "/" + props.mod.id + "/" + props.weaponId)
.then(res => res.json())
.then((weapon: IWeapon) => {
setWeaponFull({
currentTable: "dps",
weapon: weapon,
})
});
}, []);
if(weaoponFull.weapon !== undefined){
const weapon = weaoponFull.weapon
function getPiercingK(armorType: string): number {
const weaponPiercing = weapon.weaponArmorPiercing.find((p) => p.armorType.name === armorType)
return (typeof weaponPiercing !== "undefined" ? weaponPiercing.piercingValue : 10) / 100
}
function handleChange (e: React.MouseEvent, selectedDamageTable: string) {
setWeaponFull({
currentTable: selectedDamageTable,
weapon: weaoponFull.weapon,
});
}
const dpsK = (weaoponFull.currentTable == "dps") ? weapon.accuracy * (1 / (weapon.reloadTime - (weapon.reloadTime % 0.125))) : 1
const infLowPiercing = getPiercingK(ArmorTypeNames.InfantryLow)
const infMedPiercing = getPiercingK(ArmorTypeNames.InfantryMedium)
const infHighPiercing = getPiercingK(ArmorTypeNames.InfantryHigh)
const infHeavyMedPiercing = getPiercingK(ArmorTypeNames.InfantryHeavyMedium)
const infHeavyHighPiercing = getPiercingK(ArmorTypeNames.InfantryHeavyHigh)
const demonPiercing = getPiercingK(ArmorTypeNames.DemonMedium)
const demonHighPiercing = getPiercingK(ArmorTypeNames.DemonHigh)
const commanderPiercing = getPiercingK(ArmorTypeNames.Commander)
const airPiercing = getPiercingK(ArmorTypeNames.Air)
const vehLowPiercing = getPiercingK(ArmorTypeNames.VehicleLow)
const vehMedPiercing = getPiercingK(ArmorTypeNames.VehicleMedium)
const vehHighPiercing = getPiercingK(ArmorTypeNames.VehicleHigh)
const buildingLowPiercing = getPiercingK(ArmorTypeNames.BuildingLow)
const buildingMedPiercing = getPiercingK(ArmorTypeNames.BuildingMedium)
const buildingHighPiercing = getPiercingK(ArmorTypeNames.BuildingHigh)
// UA mod
const demonLowPiercing = getPiercingK(ArmorTypeNames.DemonLow)
const buildingSuperPiercing = getPiercingK(ArmorTypeNames.BuildingSuper)
const livingMetalPiercing = getPiercingK(ArmorTypeNames.LivingMetal)
const titanPiercing = getPiercingK(ArmorTypeNames.Titan)
const getTotalDamage = (damagePiercing: number, isAir: boolean = false) => {
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) {
minDamage = weapon.minDamageValue
}
if (maxDamage < weapon.minDamageValue) {
maxDamage = weapon.minDamageValue
}
const averageDmg = (minDamage + maxDamage) / 2
return (averageDmg * dpsK).toFixed(2)
}
const getMoraleDamage = () => {
return (weapon.moraleDamage * dpsK / 2).toFixed(2)
}
return (
<div>
<Grid2 container spacing={2}>
<Grid2 size={12}>
<div style={{whiteSpace: "pre-wrap"}}>
<span
className="weaponDescription">{props.isDefault ? "Default weapon" : weapon.description}</span>
</div>
</Grid2>
<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.setupTime != 0 ? (weapon.setupTime).toFixed(3).replace(/[,.]?0+$/, '') : "-"}
</TableCell>
</TableRow>
<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
aria-label="Platform"
value={weaoponFull.currentTable}
onChange={handleChange}
>
<ToggleButton size="small" value="dps">Dps</ToggleButton>
<ToggleButton size="small" value="one hit">One hit average damage</ToggleButton>
</ToggleButtonGroup>
<TableContainer>
{ props.mod !== undefined && props.mod.name.includes("Ultimate Apocalypse") ?
<Table sx={{marginTop: "10px", textAlign: "center"}} 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"/>
<div style={{width: 20, fontSize: 12, height: 50}}>
<i>Morale</i></div>
</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"/>
<div style={{width: 20, fontSize: 12, height: 50}}>
<i>Morale</i></div>
</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>
{weapon.requirements !== null && !props.isDefault && props.haveReinforceMenu &&
<Grid2 size={{xs: 12, md: 12}}>
<Required requirement={weapon.requirements} modId={props.mod.id} raceId={props.race.id}/>
</Grid2>}
<Grid2 size={12}>
{renderAffectedResearches(weapon.affectedResearches, props.mod.id, props.race.id)}
</Grid2>
</Grid2>
</div>
)
} else return <div>loading...</div>;
}
+3 -2
View File
@@ -1,12 +1,13 @@
import Weapon from "./Weapon";
import React from "react";
import {IWeapon} from "../types/IUnit";
import {IShortWeapon, IWeapon} from "../types/IUnit";
import {IMod} from "../types/Imod";
import {Irace} from "../types/Irace";
import WeaponShort from "./WeaponFull";
export interface WeaponSlotProps {
hardpoint: number,
unitWeapons: Map<number, IWeapon> | undefined,
unitWeapons: Map<number, IShortWeapon> | undefined,
mod: IMod,
race: Irace,
haveReinforceMenu?: boolean ,
+1 -1
View File
@@ -87,7 +87,7 @@ function BuildingAddon(props: IBuildingAddonProps){
{addon.description}
</div>
</Grid2>
<ModifiersProvidesTable modifiers={addon.addonModifiers} affectedData={addon.affectedData} mod={props.mod} race={building.race}/>
<ModifiersProvidesTable modifiers={addon.addonModifiers} affectedData={addon.affectedData} modId={props.mod.id} race={building.race}/>
{addon.addonRequirement !== null &&
<Grid2 size={{xs: 12, md: 12}}>
<Required requirement={addon.addonRequirement} modId={building.modId} raceId={building.race.id}/>
+6 -59
View File
@@ -19,20 +19,21 @@ import Required from "../Required";
import React from "react";
import {IBuilding} from "../../types/IBuilding";
import {IResearchShort} from "../../types/IResearchShort";
import ResearchFull from "./ResearchFull";
interface IResearchProps {
research: IResearch,
research: IResearchShort,
building: IBuilding,
mod: IMod,
}
function ResearchFull(props: IResearchProps){
function Research(props: IResearchProps){
const research = props.research
const building = props.building
return <div className='addon-research-accordion' id={"research-" + research.id}><Accordion>
return <div className='addon-research-accordion' id={"research-" + research.id}><Accordion TransitionProps={{ unmountOnExit: true, timeout: 100 }}>
<AccordionSummary
expandIcon={<ExpandMore/>}
aria-controls="panel1-content"
@@ -43,61 +44,7 @@ function ResearchFull(props: IResearchProps){
</span>
</AccordionSummary>
<AccordionDetails>
<Grid2 container spacing={2}>
<Grid2 size={{xs: 12, md: 4}}>
<TableContainer component={Paper}>
<Table size="small" aria-label="a dense table">
<TableBody id="unit-stats-table">
<TableRow
sx={{'&:last-child td, &:last-child th': {border: 0}}}
>
<TableCell component="th" scope="row">Cost</TableCell>
<TableCell component="th" scope="row">
{research.costRequisition > 0 &&
<span>&nbsp;<img style={{verticalAlign: "top"}}
src="/images/Resource_requisition.gif"/>&nbsp;
{research.costRequisition.toFixed(0)}</span>}
{research.costPower > 0 && <span>&nbsp;<img style={{verticalAlign: "top"}}
src="/images/Resource_power.gif"/>&nbsp;
{research.costPower.toFixed(0)}</span>}
{(research.costPopulation !== undefined && research.costPopulation > 0) &&
<span>&nbsp;<img style={{verticalAlign: "top"}}
src="/images/Resource_orksquadcap.gif"/>&nbsp;
{research.costPopulation.toFixed(0)}</span>}
{(research.costFaith !== undefined && research.costFaith > 0) &&
<span>&nbsp;<img style={{verticalAlign: "top"}}
src="/images/Resource_faith.gif"/>&nbsp;
{research.costFaith}</span>}
{(research.costSouls !== undefined && research.costSouls > 0) &&
<span>&nbsp;<img style={{verticalAlign: "top"}}
src="/images/Resource_souls.gif"/>&nbsp;
{research.costSouls.toFixed(0)}</span>}
{(research.costTime !== undefined && research.costTime > 0) &&
<span>&nbsp;<AvTimerOutlinedIcon
style={{verticalAlign: "top", fontSize: "18px"}}/>&nbsp;
{research.costTime}s</span>}
</TableCell>
</TableRow>
</TableBody>
</Table>
</TableContainer><br/>
</Grid2>
<Grid2 size={{xs: 12, md: 8}}>
<div style={{whiteSpace: "pre-wrap"}}>
{research.description}
</div>
</Grid2>
<Grid2 size={{xs: 12, md: 12}}>
<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} modId={building.modId} raceId={building.race.id}/>
</Grid2>}
</Grid2>
<i className="rgdFrom">{research.filename}</i>
<ResearchFull id={research.id} building={building}/>
</AccordionDetails>
</Accordion></div>
@@ -120,5 +67,5 @@ export function renderAffectedResearches(researches: IResearchShort[], modId: nu
}
export default ResearchFull;
export default Research;
+115
View File
@@ -0,0 +1,115 @@
import {IMod} from "../../types/Imod";
import {IResearch} from "../../types/IResearch";
import {
Accordion,
AccordionDetails,
AccordionSummary,
Grid2,
Paper,
Table,
TableBody,
TableCell,
TableContainer,
TableRow
} from "@mui/material";
import {ExpandMore} from "@mui/icons-material";
import {getIcon, ModifiersProvidesTable} from "../ModifiersProvideTable";
import AvTimerOutlinedIcon from "@mui/icons-material/AvTimer";
import Required from "../Required";
import React, {useEffect, useState} from "react";
import {IBuilding} from "../../types/IBuilding";
import {IResearchShort} from "../../types/IResearchShort";
import {ResearchUrl, WeaponUrl} from "../../core/api";
import {IWeapon} from "../../types/IUnit";
interface IResearchFull {
research: IResearch | undefined,
}
function ResearchFull(props: { id: number, building: IBuilding }){
const [researchFull, setResearchFull] = useState<IResearchFull>({
research: undefined,
});
const building = props.building
const research= researchFull.research!!
useEffect(() => {
fetch(ResearchUrl + "/" + building.modId + "/" + props.id)
.then(res => res.json())
.then((research: IResearch) => {
setResearchFull({
research:research,
})
});
}, []);
if(researchFull.research !== undefined) {
return <div className='addon-research-accordion' id={"research-" + research.id}>
<Grid2 container spacing={2}>
<Grid2 size={{xs: 12, md: 4}}>
<TableContainer component={Paper}>
<Table size="small" aria-label="a dense table">
<TableBody id="unit-stats-table">
<TableRow
sx={{'&:last-child td, &:last-child th': {border: 0}}}
>
<TableCell component="th" scope="row">Cost</TableCell>
<TableCell component="th" scope="row">
{research.costRequisition > 0 &&
<span>&nbsp;<img style={{verticalAlign: "top"}}
src="/images/Resource_requisition.gif"/>&nbsp;
{research.costRequisition.toFixed(0)}</span>}
{research.costPower > 0 && <span>&nbsp;<img style={{verticalAlign: "top"}}
src="/images/Resource_power.gif"/>&nbsp;
{research.costPower.toFixed(0)}</span>}
{(research.costPopulation !== undefined && research.costPopulation > 0) &&
<span>&nbsp;<img style={{verticalAlign: "top"}}
src="/images/Resource_orksquadcap.gif"/>&nbsp;
{research.costPopulation.toFixed(0)}</span>}
{(research.costFaith !== undefined && research.costFaith > 0) &&
<span>&nbsp;<img style={{verticalAlign: "top"}}
src="/images/Resource_faith.gif"/>&nbsp;
{research.costFaith}</span>}
{(research.costSouls !== undefined && research.costSouls > 0) &&
<span>&nbsp;<img style={{verticalAlign: "top"}}
src="/images/Resource_souls.gif"/>&nbsp;
{research.costSouls.toFixed(0)}</span>}
{(research.costTime !== undefined && research.costTime > 0) &&
<span>&nbsp;<AvTimerOutlinedIcon
style={{verticalAlign: "top", fontSize: "18px"}}/>&nbsp;
{research.costTime}s</span>}
</TableCell>
</TableRow>
</TableBody>
</Table>
</TableContainer><br/>
</Grid2>
<Grid2 size={{xs: 12, md: 8}}>
<div style={{whiteSpace: "pre-wrap"}}>
{research.description}
</div>
</Grid2>
<Grid2 size={{xs: 12, md: 12}}>
<ModifiersProvidesTable modifiers={research.modifiers} affectedData={research.affectedData}
modId={building.modId} race={building.race}/>
</Grid2>
{research.requirements !== null &&
<Grid2 size={{xs: 12, md: 12}}>
<Required requirement={research.requirements} modId={building.modId} raceId={building.race.id}/>
</Grid2>}
</Grid2>
</div>
} else return <div>loading...</div>
}
export default ResearchFull;
+2
View File
@@ -1,5 +1,7 @@
export const UserUrl = process.env.REACT_APP_HOST_URL + '/api/v1/user';
export const WeaponUrl = process.env.REACT_APP_HOST_URL + '/api/v1/weapon';
export const ResearchUrl = process.env.REACT_APP_HOST_URL + '/api/v1/research';
export const AvailableMods = process.env.REACT_APP_HOST_URL + '/api/v1/mods';
export const AvailableRacesPart = process.env.REACT_APP_HOST_URL + '/api/v1/races';
export const AvailableUnits = process.env.REACT_APP_HOST_URL + '/api/v1/units';
+13 -6
View File
@@ -1,7 +1,7 @@
import {AvailableBuildings, AvailableMods, IconUrl} from "../core/api";
import React, {useEffect} from "react";
import React from "react";
import {withRouter} from "../core/withrouter";
import {IWeapon} from "../types/IUnit";
import {IShortWeapon} from "../types/IUnit";
import '../css/Building.css'
import {
Button,
@@ -13,7 +13,8 @@ import {
TableBody,
TableCell,
TableContainer,
TableRow, Tooltip
TableRow,
Tooltip
} from "@mui/material";
import {ArrowBack} from "@mui/icons-material";
import ArmorType from "../classes/ArmorType";
@@ -27,6 +28,7 @@ import BuildingAddon from "../classes/building/BuildingAddon";
import {IUnitShort} from "../types/IUnitShort";
import Research, {renderAffectedResearches} from "../classes/building/Research";
import Required from "../classes/Required";
import {ModifiersProvidesTable} from "../classes/ModifiersProvideTable";
interface UintPageState {
building: IBuilding,
@@ -35,7 +37,7 @@ interface UintPageState {
function Unit (unit: IUnitShort, modId: number, raceId: String) {
return (<Grid2 size={{xs: 12, md: 3}}><Link href={"/mod/" + modId + "/race/" + raceId + "/unit/" + unit.id}><ListItem>
return (<Grid2 key={unit.id} 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"}}
@@ -49,7 +51,7 @@ function Building(building: IBuilding, mod: IMod) {
document.title = building.name
let mapBuildingWeapons: Map<number, Map<number, IWeapon>> = new Map();
let mapBuildingWeapons: Map<number, Map<number, IShortWeapon>> = new Map();
building.weapons.forEach(weapon => {
const weaponMap = mapBuildingWeapons.get(weapon.hardpoint)
@@ -176,6 +178,11 @@ function Building(building: IBuilding, mod: IMod) {
<Grid2 size={{xs: 12, md: 12}}>
<Required requirement={building.requirements} modId={building.modId} raceId={building.race.id}/>
</Grid2>}
{building.modifiers.length > 0 &&
<Grid2 size={{xs: 12, md: 12}} >
<h3>Affected on</h3>
<ModifiersProvidesTable modifiers={building.modifiers} race={building.race} modId={building.modId} affectedData={building.affectedData} />
</Grid2>}
{building.units.length > 0 && <Grid2 size={12}>
<h3>Unit production</h3>
<Grid2 container spacing={2}>
@@ -193,7 +200,7 @@ function Building(building: IBuilding, mod: IMod) {
{building.researches.length > 0 && <Grid2 size={12}>
<h3>Researches</h3>
{building.researches.map(r =>
<Research mod={mod} research={r} building={building}/>
<Research key={r.id} research={r} building={building} mod={mod}/>
)}
</Grid2> }
<Grid2 size={12}>
+2 -2
View File
@@ -1,7 +1,7 @@
import {AvailableMods, AvailableUnits, IconUrl} from "../core/api";
import React from "react";
import {withRouter} from "../core/withrouter";
import {IUnit, IWeapon} from "../types/IUnit";
import {IShortWeapon, IUnit} from "../types/IUnit";
import '../css/Unit.css'
import {
Accordion,
@@ -46,7 +46,7 @@ function Unit(unit: IUnit, mod: IMod) {
</span> : "-"
let mapWithUnitWeapons: Map<number, Map<number, IWeapon>> = new Map();
let mapWithUnitWeapons: Map<number, Map<number, IShortWeapon>> = new Map();
unit.weapons.forEach(weapon => {
const weaponMap = mapWithUnitWeapons.get(weapon.hardpoint)
+3 -1
View File
@@ -33,9 +33,11 @@ export interface IBuilding {
repairMax: number
icon: string
modId: number
modifiers: IModifier[]
affectedData: IAffectedData,
weapons: WeaponHardpoint[]
addons: IBuildingAddon[]
researches: IResearch[]
researches: IResearchShort[]
units: IUnitShort[]
affectedResearches: IResearchShort[],
requirements: IRequirement,
+1 -1
View File
@@ -1,5 +1,5 @@
export interface IResearchShort {
id: string;
id: number;
name: string;
icon: string;
buildingId: number;
+9 -1
View File
@@ -92,11 +92,19 @@ export interface ISergeant {
export interface WeaponHardpoint {
weapon: IWeapon
weapon: IShortWeapon
hardpoint: number
hardpointOrder: number
}
export interface IShortWeapon {
id: number
name: string
filename: string
icon: string
isMeleeWeapon: boolean
}
export interface IWeapon {
id: number
name: string