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

View File

@ -7,7 +7,7 @@
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="The word 'Caldiris' is slang for someone who drinks alcohol in large quantities. For example, the phrase 'he was caldiris all night' implies that the person drank heavily throughout the night and posted content on dawn-of-war-unification-mod.fandom.com."
content="Autogeneration wiki"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--

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>
)

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>)
}

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)

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>)
}

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
src/classes/WeaponFull.tsx Normal file
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>;
}

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 ,

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}/>

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;

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;

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';

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}>

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)

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,

View File

@ -1,5 +1,5 @@
export interface IResearchShort {
id: string;
id: number;
name: string;
icon: string;
buildingId: number;

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