Add teleport feature

This commit is contained in:
anibus 2026-08-01 21:43:14 +03:00
parent 57779cacbd
commit 405b562f8f
20 changed files with 351 additions and 32 deletions

BIN
public/images/Jump.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

BIN
public/images/NoImg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
public/images/Teleport.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

BIN
public/images/dowde.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

View File

@ -29,7 +29,7 @@ function Ability(props: IAbilityProps){
aria-controls="panel1-content"
>
<span style={{fontSize: 20, color: textColor, display: 'flex', alignItems: 'center'}}>
{ability.activationType === "Passive ability" ? <img className="abilityIcon" src="/images/PassiveIcon.gif"/> : <img className="abilityIcon" src={getIcon(ability.icon)}/>}
{ability.activationType === "Passive ability" ? <img className="abilityIcon" src="/images/PassiveIcon.png"/> : <img className="abilityIcon" src={getIcon(ability.icon)}/>}
&nbsp; {ability.name ? ability.name : ability.fileName.charAt(0).toUpperCase() + ability.fileName.slice(1).replaceAll('_', ' ').replace('.rgd', '')}
<i style={{fontSize: 12, color: 'rgba(255,255,255,0.5)'}}> ({ability.activationType})</i>
</span>

View File

@ -1,5 +1,6 @@
import React, {useEffect, useState} from "react";
import {useTheme} from "@mui/material/styles";
import useMediaQuery from "@mui/material/useMediaQuery";
import {AbilityUrl, AvailableBuildings, AvailableUnits, UserUrl, WeaponUrl} from "../core/api";
import {IWeapon} from "../types/IUnit";
import ArmorTypeNames from "../types/ArmorTypeValues";
@ -24,7 +25,6 @@ import AvTimerOutlinedIcon from "@mui/icons-material/AvTimer";
import ability from "./Ability";
import AbilitySpawnedObject from "./AbilitySpawnedObject";
import DpsTable from "./DpsTable";
import {DescriptionBox} from "../commons/DescriptionBox";
import {StyledPaper} from "../commons/StyledPaper";
import {StyledTableCell} from "../commons/StyledTableCell";
@ -34,6 +34,7 @@ interface IAbilityFullState {
export default function AbilityFull(props: { abilityId?: number, mod: IMod, race: Irace, isChild?: boolean, abilityFull?: IAbilityFill }) {
const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down('md'));
const isDark = theme.palette.mode === 'dark';
const textColor = isDark ? '#dee2e6' : 'rgba(0, 0, 0, 0.85)';
@ -181,7 +182,7 @@ export default function AbilityFull(props: { abilityId?: number, mod: IMod, race
</TableContainer><br/>
</Grid2>
<Grid2 size={8}>
{props.isChild !== true && <DescriptionBox>{ability.description}</DescriptionBox>}
{props.isChild !== true && <span style={{ whiteSpace: 'pre-wrap' }}>{ability.description}</span>}
</Grid2>
{showDpsTable ?
<Grid2 size={12}>
@ -191,8 +192,71 @@ export default function AbilityFull(props: { abilityId?: number, mod: IMod, race
</Grid2> : showTargetTable ?
<Grid2 size={12}>
<TableContainer>
{ props.mod !== undefined && props.mod.technicalName === 'UltimateApocalypse' ?
<Table sx={{ textAlign: "center"}} size="small" aria-label="a dense table">
{isMobile ? (() => {
const items = props.mod !== undefined && props.mod.technicalName === 'UltimateApocalypse' ? [
{ label: <ArmorType name={ArmorTypeNames.InfantryLow}/>, value: () => isAbilityApplyTo(ArmorTypeNames.InfantryLow) },
{ label: <ArmorType name={ArmorTypeNames.InfantryMedium}/>, value: () => isAbilityApplyTo(ArmorTypeNames.InfantryMedium) },
{ label: <ArmorType name={ArmorTypeNames.InfantryHigh}/>, value: () => isAbilityApplyTo(ArmorTypeNames.InfantryHigh) },
{ label: <ArmorType name={ArmorTypeNames.InfantryHeavyMedium}/>, value: () => isAbilityApplyTo(ArmorTypeNames.InfantryHeavyMedium) },
{ label: <ArmorType name={ArmorTypeNames.InfantryHeavyHigh}/>, value: () => isAbilityApplyTo(ArmorTypeNames.InfantryHeavyHigh) },
{ label: <ArmorType name={ArmorTypeNames.Commander}/>, value: () => isAbilityApplyTo(ArmorTypeNames.Commander) },
{ label: <ArmorType name={ArmorTypeNames.DemonLow}/>, value: () => isAbilityApplyTo(ArmorTypeNames.DemonLow) },
{ label: <ArmorType name={ArmorTypeNames.DemonMedium}/>, value: () => isAbilityApplyTo(ArmorTypeNames.DemonMedium) },
{ label: <ArmorType name={ArmorTypeNames.DemonHigh}/>, value: () => isAbilityApplyTo(ArmorTypeNames.DemonHigh) },
{ label: <ArmorType name={ArmorTypeNames.Air}/>, value: () => isAbilityApplyTo(ArmorTypeNames.Air) },
{ label: <ArmorType name={ArmorTypeNames.VehicleLow}/>, value: () => isAbilityApplyTo(ArmorTypeNames.VehicleLow) },
{ label: <ArmorType name={ArmorTypeNames.VehicleMedium}/>, value: () => isAbilityApplyTo(ArmorTypeNames.VehicleMedium) },
{ label: <ArmorType name={ArmorTypeNames.VehicleHigh}/>, value: () => isAbilityApplyTo(ArmorTypeNames.VehicleHigh) },
{ label: <ArmorType name={ArmorTypeNames.LivingMetal}/>, value: () => isAbilityApplyTo(ArmorTypeNames.LivingMetal) },
{ label: <ArmorType name={ArmorTypeNames.Titan}/>, value: () => isAbilityApplyTo(ArmorTypeNames.Titan) },
{ label: <ArmorType name={ArmorTypeNames.BuildingLow}/>, value: () => isAbilityApplyTo(ArmorTypeNames.BuildingLow) },
{ label: <ArmorType name={ArmorTypeNames.BuildingMedium}/>, value: () => isAbilityApplyTo(ArmorTypeNames.BuildingMedium) },
{ label: <ArmorType name={ArmorTypeNames.BuildingHigh}/>, value: () => isAbilityApplyTo(ArmorTypeNames.BuildingHigh) },
{ label: <ArmorType name={ArmorTypeNames.BuildingSuper}/>, value: () => isAbilityApplyTo(ArmorTypeNames.BuildingSuper) },
{ label: null, value: () => null },
] : [
{ label: <ArmorType name={ArmorTypeNames.InfantryLow}/>, value: () => isAbilityApplyTo(ArmorTypeNames.InfantryLow) },
{ label: <ArmorType name={ArmorTypeNames.InfantryMedium}/>, value: () => isAbilityApplyTo(ArmorTypeNames.InfantryMedium) },
{ label: <ArmorType name={ArmorTypeNames.InfantryHigh}/>, value: () => isAbilityApplyTo(ArmorTypeNames.InfantryHigh) },
{ label: <ArmorType name={ArmorTypeNames.InfantryHeavyMedium}/>, value: () => isAbilityApplyTo(ArmorTypeNames.InfantryHeavyMedium) },
{ label: <ArmorType name={ArmorTypeNames.InfantryHeavyHigh}/>, value: () => isAbilityApplyTo(ArmorTypeNames.InfantryHeavyHigh) },
{ label: <ArmorType name={ArmorTypeNames.Commander}/>, value: () => isAbilityApplyTo(ArmorTypeNames.Commander) },
{ label: <ArmorType name={ArmorTypeNames.DemonMedium}/>, value: () => isAbilityApplyTo(ArmorTypeNames.DemonMedium) },
{ label: <ArmorType name={ArmorTypeNames.DemonHigh}/>, value: () => isAbilityApplyTo(ArmorTypeNames.DemonHigh) },
{ label: <ArmorType name={ArmorTypeNames.Air}/>, value: () => isAbilityApplyTo(ArmorTypeNames.Air) },
{ label: <ArmorType name={ArmorTypeNames.VehicleLow}/>, value: () => isAbilityApplyTo(ArmorTypeNames.VehicleLow) },
{ label: <ArmorType name={ArmorTypeNames.VehicleMedium}/>, value: () => isAbilityApplyTo(ArmorTypeNames.VehicleMedium) },
{ label: <ArmorType name={ArmorTypeNames.VehicleHigh}/>, value: () => isAbilityApplyTo(ArmorTypeNames.VehicleHigh) },
{ label: <ArmorType name={ArmorTypeNames.BuildingLow}/>, value: () => isAbilityApplyTo(ArmorTypeNames.BuildingLow) },
{ label: <ArmorType name={ArmorTypeNames.BuildingMedium}/>, value: () => isAbilityApplyTo(ArmorTypeNames.BuildingMedium) },
{ label: <ArmorType name={ArmorTypeNames.BuildingHigh}/>, value: () => isAbilityApplyTo(ArmorTypeNames.BuildingHigh) },
];
const chunks = [];
for (let i = 0; i < items.length; i += 5) {
chunks.push(items.slice(i, i + 5));
}
return (<>{chunks.map((chunk, chunkIndex) => (
<Table key={chunkIndex} sx={{marginTop: "10px", textAlign: "center"}} size="small" aria-label="a dense table">
<TableHead>
<TableRow>
{chunk.map((item, i) => (
<StyledTableCell key={i}>{item.label}</StyledTableCell>
))}
</TableRow>
</TableHead>
<TableBody>
<TableRow sx={{'&:last-child td, &:last-child th': {border: 0}}}>
{chunk.map((item, i) => (
<StyledTableCell key={i}>{item.value()}</StyledTableCell>
))}
</TableRow>
</TableBody>
</Table>
))}</>)
})() : props.mod !== undefined && props.mod.technicalName === 'UltimateApocalypse' ?
<Table sx={{ textAlign: "center", minWidth: 600}} size="small" aria-label="a dense table">
<TableHead>
<TableRow>
<StyledTableCell><ArmorType
@ -268,7 +332,7 @@ export default function AbilityFull(props: { abilityId?: number, mod: IMod, race
</TableRow>
</TableBody>
</Table> :
<Table size="small" aria-label="a dense table">
<Table sx={{ minWidth: 800 }} size="small" aria-label="a dense table">
<TableHead>
<TableRow>
<StyledTableCell><ArmorType
@ -324,7 +388,8 @@ export default function AbilityFull(props: { abilityId?: number, mod: IMod, race
</Table>
}
</TableContainer>
</Grid2> : <span></span>}
</Grid2>
: <span></span>}
<ModifiersProvidesTable modifiers={ability.modifiers} affectedData={ability.affectedData} modId={props.mod.id} race={props.race}/>
</Grid2>
{(ability.abilityEnvironment != null) && <AbilitySpawnedObject abilityEnvironment={ability.abilityEnvironment} race={props.race} mod={props.mod}/>}

View File

@ -1,4 +1,6 @@
import React from "react";
import {useTheme} from "@mui/material/styles";
import useMediaQuery from "@mui/material/useMediaQuery";
import ArmorTypeNames from "../types/ArmorTypeValues";
import {
Table,
@ -14,6 +16,8 @@ import {IArmorType} from "../types/IArmorType";
import {StyledTableCell} from "../commons/StyledTableCell";
export default function DpsTable(props: { mod: IMod, minDamageValue?: number, minDamage?: number, maxDamage?: number, piercings: IPiercing[], targetFilter: IArmorType[], moraleDamage?: number }) {
const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down('md'));
function getPiercingK(armorType: string): number|null {
if(props.targetFilter.length > 0 && props.targetFilter.find(tf => tf.name == armorType) === undefined) return null
@ -72,6 +76,72 @@ export default function DpsTable(props: { mod: IMod, minDamageValue?: number, mi
if(props.moraleDamage !== undefined) return (props.moraleDamage / 2).toFixed(2)
}
if (isMobile) {
const items = props.mod !== undefined && props.mod.name.includes("Ultimate Apocalypse") ? [
{ label: <ArmorType name={ArmorTypeNames.InfantryLow}/>, value: () => getTotalDamage(infLowPiercing) },
{ label: <ArmorType name={ArmorTypeNames.InfantryMedium}/>, value: () => getTotalDamage(infMedPiercing) },
{ label: <ArmorType name={ArmorTypeNames.InfantryHigh}/>, value: () => getTotalDamage(infHighPiercing) },
{ label: <ArmorType name={ArmorTypeNames.InfantryHeavyMedium}/>, value: () => getTotalDamage(infHeavyMedPiercing) },
{ label: <ArmorType name={ArmorTypeNames.InfantryHeavyHigh}/>, value: () => getTotalDamage(infHeavyHighPiercing) },
{ label: <ArmorType name={ArmorTypeNames.Commander}/>, value: () => getTotalDamage(commanderPiercing) },
{ label: <ArmorType name={ArmorTypeNames.DemonLow}/>, value: () => getTotalDamage(demonLowPiercing) },
{ label: <ArmorType name={ArmorTypeNames.DemonMedium}/>, value: () => getTotalDamage(demonPiercing) },
{ label: <ArmorType name={ArmorTypeNames.DemonHigh}/>, value: () => getTotalDamage(demonHighPiercing) },
{ label: <ArmorType name={ArmorTypeNames.Air}/>, value: () => getTotalDamage(airPiercing) },
{ label: <ArmorType name={ArmorTypeNames.VehicleLow}/>, value: () => getTotalDamage(vehLowPiercing) },
{ label: <ArmorType name={ArmorTypeNames.VehicleMedium}/>, value: () => getTotalDamage(vehMedPiercing) },
{ label: <ArmorType name={ArmorTypeNames.VehicleHigh}/>, value: () => getTotalDamage(vehHighPiercing) },
{ label: <ArmorType name={ArmorTypeNames.LivingMetal}/>, value: () => getTotalDamage(livingMetalPiercing) },
{ label: <ArmorType name={ArmorTypeNames.Titan}/>, value: () => getTotalDamage(titanPiercing) },
{ label: <ArmorType name={ArmorTypeNames.BuildingLow}/>, value: () => getTotalDamage(buildingLowPiercing) },
{ label: <ArmorType name={ArmorTypeNames.BuildingMedium}/>, value: () => getTotalDamage(buildingMedPiercing) },
{ label: <ArmorType name={ArmorTypeNames.BuildingHigh}/>, value: () => getTotalDamage(buildingHighPiercing) },
{ label: <ArmorType name={ArmorTypeNames.BuildingSuper}/>, value: () => getTotalDamage(buildingSuperPiercing) },
{ label: <ArmorType name="Morale damage"/>, value: () => getMoraleDamage() },
] : [
{ label: <ArmorType name={ArmorTypeNames.InfantryLow}/>, value: () => getTotalDamage(infLowPiercing) },
{ label: <ArmorType name={ArmorTypeNames.InfantryMedium}/>, value: () => getTotalDamage(infMedPiercing) },
{ label: <ArmorType name={ArmorTypeNames.InfantryHigh}/>, value: () => getTotalDamage(infHighPiercing) },
{ label: <ArmorType name={ArmorTypeNames.InfantryHeavyMedium}/>, value: () => getTotalDamage(infHeavyMedPiercing) },
{ label: <ArmorType name={ArmorTypeNames.InfantryHeavyHigh}/>, value: () => getTotalDamage(infHeavyHighPiercing) },
{ label: <ArmorType name={ArmorTypeNames.Commander}/>, value: () => getTotalDamage(commanderPiercing) },
{ label: <ArmorType name={ArmorTypeNames.DemonMedium}/>, value: () => getTotalDamage(demonPiercing) },
{ label: <ArmorType name={ArmorTypeNames.DemonHigh}/>, value: () => getTotalDamage(demonHighPiercing) },
{ label: <ArmorType name={ArmorTypeNames.Air}/>, value: () => getTotalDamage(airPiercing) },
{ label: <ArmorType name={ArmorTypeNames.VehicleLow}/>, value: () => getTotalDamage(vehLowPiercing) },
{ label: <ArmorType name={ArmorTypeNames.VehicleMedium}/>, value: () => getTotalDamage(vehMedPiercing) },
{ label: <ArmorType name={ArmorTypeNames.VehicleHigh}/>, value: () => getTotalDamage(vehHighPiercing) },
{ label: <ArmorType name={ArmorTypeNames.BuildingLow}/>, value: () => getTotalDamage(buildingLowPiercing) },
{ label: <ArmorType name={ArmorTypeNames.BuildingMedium}/>, value: () => getTotalDamage(buildingMedPiercing) },
{ label: <ArmorType name={ArmorTypeNames.BuildingHigh}/>, value: () => getTotalDamage(buildingHighPiercing) },
{ label: <ArmorType name="Morale damage"/>, value: () => getMoraleDamage() },
];
const chunks = [];
for (let i = 0; i < items.length; i += 5) {
chunks.push(items.slice(i, i + 5));
}
return (<>{chunks.map((chunk, chunkIndex) => (
<Table key={chunkIndex} sx={{marginTop: "10px", textAlign: "center"}} size="small" aria-label="a dense table">
<TableHead>
<TableRow>
{chunk.map((item, i) => (
<StyledTableCell key={i}>{item.label}</StyledTableCell>
))}
</TableRow>
</TableHead>
<TableBody>
<TableRow sx={{'&:last-child td, &:last-child th': {border: 0}}}>
{chunk.map((item, i) => (
<StyledTableCell key={i}>{item.value()}</StyledTableCell>
))}
</TableRow>
</TableBody>
</Table>
))}</>)
}
return (
<TableContainer>
{ props.mod !== undefined && props.mod.name.includes("Ultimate Apocalypse") ?
@ -153,7 +223,7 @@ export default function DpsTable(props: { mod: IMod, minDamageValue?: number, mi
</TableRow>
</TableBody>
</Table> :
<Table size="small" aria-label="a dense table">
<Table sx={{ minWidth: 800 }} size="small" aria-label="a dense table">
<TableHead>
<TableRow>
<StyledTableCell><ArmorType

100
src/classes/Jump.tsx Normal file
View File

@ -0,0 +1,100 @@
import React from "react";
import {AccordionDetails, AccordionSummary, Grid2, Table, TableBody, TableCell, TableRow, TableContainer} from "@mui/material";
import {ExpandMore} from "@mui/icons-material";
import {useTheme} from "@mui/material/styles";
import {IJumps} from "../types/IUnit";
import {Irace} from "../types/Irace";
import {IMod} from "../types/Imod";
import {StyledAccordion} from "../commons/StyledAccordion";
import {StyledPaper} from "../commons/StyledPaper";
import Required from "./Required";
interface IJumpProps {
jumps: IJumps,
race: Irace,
mod: IMod,
}
function Jump(props: IJumpProps) {
const theme = useTheme();
const isDark = theme.palette.mode === 'dark';
const textColor = isDark ? '#dee2e6' : 'rgba(0, 0, 0, 0.85)';
const jumps = props.jumps;
const isTeleport = jumps.teleport;
const icon = isTeleport ? "/images/Teleport.png" : "/images/Jump.png";
const title = isTeleport ? "Teleport" : "Jumps";
return (
<div style={{marginBottom: 10}}>
<StyledAccordion TransitionProps={{unmountOnExit: true, timeout: 100}}>
<AccordionSummary
expandIcon={<ExpandMore sx={{color: textColor}}/>}
aria-controls="jump-content"
id="jump-header"
sx={{
color: textColor,
'& .MuiAccordionSummary-content': {margin: '12px 0'},
'& .MuiAccordionSummary-content.Mui-expanded': {margin: '12px 0'}
}}
>
<span style={{fontSize: 20, color: textColor, display: 'flex', alignItems: 'center'}}>
<img className="jumpIcon" src={icon}/>
&nbsp; {title}
</span>
</AccordionSummary>
<AccordionDetails sx={{color: textColor}}>
<Grid2 container spacing={2}>
{(() => {
const rows: { label: string; value: React.ReactNode }[] = [
{ label: "Charge cost", value: jumps.chargeJumpCostMin === jumps.chargeJumpCostMax
? jumps.chargeJumpCostMin.toFixed(0)
: `${jumps.chargeJumpCostMin.toFixed(0)} - ${jumps.chargeJumpCostMax.toFixed(0)}` },
{ label: "Charge max", value: jumps.chargeMax.toFixed(0) },
{ label: "Charge regeneration", value: `${jumps.chargeRegeneration}/s` },
{ label: "Distance", value: jumps.jumpDistanceMax.toFixed(0) },
{ label: "Setup time", value: <span><img style={{verticalAlign: "top"}} src="/images/Time_icon.webp"/>&nbsp;{jumps.setupTime}s</span> },
{ label: "Breakdown time", value: <span><img style={{verticalAlign: "top"}} src="/images/Time_icon.webp"/>&nbsp;{jumps.breakdownTime}s</span> },
];
if (!isTeleport) {
if (jumps.goUpTime !== undefined && jumps.goUpTime !== null) {
rows.push({ label: "Go up time", value: <span><img style={{verticalAlign: "top"}} src="/images/Time_icon.webp"/>&nbsp;{jumps.goUpTime}s</span> });
}
if (jumps.goDownTime !== undefined && jumps.goDownTime !== null) {
rows.push({ label: "Go down time", value: <span><img style={{verticalAlign: "top"}} src="/images/Time_icon.webp"/>&nbsp;{jumps.goDownTime}s</span> });
}
}
const chunks: { label: string; value: React.ReactNode }[][] = [];
for (let i = 0; i < rows.length; i += 3) {
chunks.push(rows.slice(i, i + 3));
}
return chunks.map((chunk, chunkIndex) => (
<Grid2 key={chunkIndex} size={{xs: 12, md: 4}}>
<TableContainer component={StyledPaper} elevation={0}>
<Table size="small" aria-label="jump stats table">
<TableBody>
{chunk.map((row, i) => (
<TableRow key={i} sx={{'&:last-child td, &:last-child th': {border: 0}}}>
<TableCell component="th" scope="row">{row.label}</TableCell>
<TableCell>{row.value}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
</Grid2>
));
})()}
</Grid2>
{jumps.requirements !== null &&
<Grid2 size={{xs: 12, md: 12}}>
<Required requirement={jumps.requirements} modId={props.mod.id}
raceId={props.race.id}/>
</Grid2>}
</AccordionDetails>
</StyledAccordion>
</div>
);
}
export default Jump;

View File

@ -265,7 +265,7 @@ export function ModifiersProvidesTable(props: IModifiersProvidesResearchTable) {
export function getIcon(icon?: string): string {
if (icon !== null && icon !== undefined) {
return IconUrl + icon.replaceAll('\\', '/')
} else return '/images/NoImg.gif'
} else return '/images/NoImg.png'
}

View File

@ -1,5 +1,6 @@
import React, {useEffect, useState} from "react";
import {useTheme} from "@mui/material/styles";
import useMediaQuery from "@mui/material/useMediaQuery";
import {AvailableBuildings, AvailableUnits, UserUrl, WeaponUrl} from "../core/api";
import {IWeapon} from "../types/IUnit";
import ArmorTypeNames from "../types/ArmorTypeValues";
@ -20,7 +21,6 @@ import {Irace} from "../types/Irace";
import {IRaceUnits} from "../types/IUnitShort";
import {IRaceBuildings} from "../types/IBuildingShort";
import {ModifiersProvidesTable} from "./ModifiersProvideTable";
import {DescriptionBox} from "../commons/DescriptionBox";
import {StyledPaper} from "../commons/StyledPaper";
import {StyledTableCell} from "../commons/StyledTableCell";
import AvTimerOutlinedIcon from "@mui/icons-material/AvTimer";
@ -32,6 +32,7 @@ interface IWeaponFull {
export default function WeaponFull(props: {weaponId: number, isDefault: Boolean, mod: IMod, race: Irace, haveReinforceMenu: Boolean}) {
const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down('md'));
const isDark = theme.palette.mode === 'dark';
const textColor = isDark ? '#dee2e6' : 'rgba(0, 0, 0, 0.85)';
@ -54,8 +55,6 @@ export default function WeaponFull(props: {weaponId: number, isDefault: Boolean,
}, []);
if(weaoponFull.weapon !== undefined){
const weapon = weaoponFull.weapon
@ -126,9 +125,7 @@ export default function WeaponFull(props: {weaponId: number, isDefault: Boolean,
<div style={{color: textColor}}>
<Grid2 container spacing={2}>
{!props.isDefault && <Grid2 size={12}>
<DescriptionBox>
<span className="weaponDescription">{weapon.description}</span>
</DescriptionBox>
<span className="weaponDescription" style={{ whiteSpace: 'pre-wrap' }}>{weapon.description}</span>
</Grid2>}
<Grid2 size={{xs: 12, md: 4}}>
@ -238,7 +235,71 @@ export default function WeaponFull(props: {weaponId: number, isDefault: Boolean,
<ToggleButton size="small" value="one hit" sx={{ color: textColor, '&.Mui-selected': {borderColor: textColor, color: textColor, backgroundColor: isDark ? 'rgba(255, 255, 255, 0.15)' : 'rgba(0, 0, 0, 0.15)' } }}>One hit average damage</ToggleButton>
</ToggleButtonGroup>
<TableContainer>
{ props.mod !== undefined && props.mod.technicalName === 'UltimateApocalypse' ?
{isMobile ? (() => {
const items = props.mod !== undefined && props.mod.technicalName === 'UltimateApocalypse' ? [
{ label: <ArmorType name={ArmorTypeNames.InfantryLow}/>, value: () => getTotalDamage(infLowPiercing) },
{ label: <ArmorType name={ArmorTypeNames.InfantryMedium}/>, value: () => getTotalDamage(infMedPiercing) },
{ label: <ArmorType name={ArmorTypeNames.InfantryHigh}/>, value: () => getTotalDamage(infHighPiercing) },
{ label: <ArmorType name={ArmorTypeNames.InfantryHeavyMedium}/>, value: () => getTotalDamage(infHeavyMedPiercing) },
{ label: <ArmorType name={ArmorTypeNames.InfantryHeavyHigh}/>, value: () => getTotalDamage(infHeavyHighPiercing) },
{ label: <ArmorType name={ArmorTypeNames.Commander}/>, value: () => getTotalDamage(commanderPiercing) },
{ label: <ArmorType name={ArmorTypeNames.DemonLow}/>, value: () => getTotalDamage(demonLowPiercing) },
{ label: <ArmorType name={ArmorTypeNames.DemonMedium}/>, value: () => getTotalDamage(demonPiercing) },
{ label: <ArmorType name={ArmorTypeNames.DemonHigh}/>, value: () => getTotalDamage(demonHighPiercing) },
{ label: <ArmorType name={ArmorTypeNames.Air}/>, value: () => getTotalDamage(airPiercing, true) },
{ label: <ArmorType name={ArmorTypeNames.VehicleLow}/>, value: () => getTotalDamage(vehLowPiercing) },
{ label: <ArmorType name={ArmorTypeNames.VehicleMedium}/>, value: () => getTotalDamage(vehMedPiercing) },
{ label: <ArmorType name={ArmorTypeNames.VehicleHigh}/>, value: () => getTotalDamage(vehHighPiercing) },
{ label: <ArmorType name={ArmorTypeNames.LivingMetal}/>, value: () => getTotalDamage(livingMetalPiercing) },
{ label: <ArmorType name={ArmorTypeNames.Titan}/>, value: () => getTotalDamage(titanPiercing) },
{ label: <ArmorType name={ArmorTypeNames.BuildingLow}/>, value: () => getTotalDamage(buildingLowPiercing) },
{ label: <ArmorType name={ArmorTypeNames.BuildingMedium}/>, value: () => getTotalDamage(buildingMedPiercing) },
{ label: <ArmorType name={ArmorTypeNames.BuildingHigh}/>, value: () => getTotalDamage(buildingHighPiercing) },
{ label: <ArmorType name={ArmorTypeNames.BuildingSuper}/>, value: () => getTotalDamage(buildingSuperPiercing) },
{ label: (<><img style={{verticalAlign: "top"}} src="/images/ARM_Morale.webp"/><div style={{width: 20, fontSize: 12, height: 50, color: textColor}}><i>Morale</i></div></>), value: () => getMoraleDamage() },
] : [
{ label: <ArmorType name={ArmorTypeNames.InfantryLow}/>, value: () => getTotalDamage(infLowPiercing) },
{ label: <ArmorType name={ArmorTypeNames.InfantryMedium}/>, value: () => getTotalDamage(infMedPiercing) },
{ label: <ArmorType name={ArmorTypeNames.InfantryHigh}/>, value: () => getTotalDamage(infHighPiercing) },
{ label: <ArmorType name={ArmorTypeNames.InfantryHeavyMedium}/>, value: () => getTotalDamage(infHeavyMedPiercing) },
{ label: <ArmorType name={ArmorTypeNames.InfantryHeavyHigh}/>, value: () => getTotalDamage(infHeavyHighPiercing) },
{ label: <ArmorType name={ArmorTypeNames.Commander}/>, value: () => getTotalDamage(commanderPiercing) },
{ label: <ArmorType name={ArmorTypeNames.DemonMedium}/>, value: () => getTotalDamage(demonPiercing) },
{ label: <ArmorType name={ArmorTypeNames.DemonHigh}/>, value: () => getTotalDamage(demonHighPiercing) },
{ label: <ArmorType name={ArmorTypeNames.Air}/>, value: () => getTotalDamage(airPiercing, true) },
{ label: <ArmorType name={ArmorTypeNames.VehicleLow}/>, value: () => getTotalDamage(vehLowPiercing) },
{ label: <ArmorType name={ArmorTypeNames.VehicleMedium}/>, value: () => getTotalDamage(vehMedPiercing) },
{ label: <ArmorType name={ArmorTypeNames.VehicleHigh}/>, value: () => getTotalDamage(vehHighPiercing) },
{ label: <ArmorType name={ArmorTypeNames.BuildingLow}/>, value: () => getTotalDamage(buildingLowPiercing) },
{ label: <ArmorType name={ArmorTypeNames.BuildingMedium}/>, value: () => getTotalDamage(buildingMedPiercing) },
{ label: <ArmorType name={ArmorTypeNames.BuildingHigh}/>, value: () => getTotalDamage(buildingHighPiercing) },
{ label: <ArmorType name="Morale damage"/>, value: () => getMoraleDamage() },
];
const chunks = [];
for (let i = 0; i < items.length; i += 5) {
chunks.push(items.slice(i, i + 5));
}
return chunks.map((chunk, chunkIndex) => (
<Table key={chunkIndex} sx={{marginTop: "10px", textAlign: "center"}} size="small" aria-label="a dense table">
<TableHead>
<TableRow>
{chunk.map((item, i) => (
<StyledTableCell key={i}>{item.label}</StyledTableCell>
))}
</TableRow>
</TableHead>
<TableBody>
<TableRow sx={{'&:last-child td, &:last-child th': {border: 0}}}>
{chunk.map((item, i) => (
<StyledTableCell key={i}>{item.value()}</StyledTableCell>
))}
</TableRow>
</TableBody>
</Table>
));
})() : props.mod !== undefined && props.mod.technicalName === 'UltimateApocalypse' ?
<Table sx={{marginTop: "10px", textAlign: "center"}} size="small" aria-label="a dense table">
<TableHead>
<TableRow>
@ -378,7 +439,6 @@ export default function WeaponFull(props: {weaponId: number, isDefault: Boolean,
</TableBody>
</Table>
}
</TableContainer>
</Grid2>

View File

@ -18,7 +18,6 @@ import {IMod} from "../../types/Imod";
import {getIcon, ModifiersProvidesTable,} from "../ModifiersProvideTable";
import Required from "../Required";
import {StyledAccordion} from "../../commons/StyledAccordion";
import {DescriptionBox} from "../../commons/DescriptionBox";
import {StyledPaper} from "../../commons/StyledPaper";
import {styled} from "@mui/material/styles";
@ -92,9 +91,7 @@ function BuildingAddon(props: IBuildingAddonProps){
</TableContainer><br/>
</Grid2>
<Grid2 size={{xs: 12, md: 8}}>
<DescriptionBox>
{addon.description}
</DescriptionBox>
<span style={{ whiteSpace: 'pre-wrap' }}>{addon.description}</span>
</Grid2>
<ModifiersProvidesTable modifiers={addon.addonModifiers} affectedData={addon.affectedData} modId={props.mod.id} race={building.race}/>
{addon.addonRequirement !== null &&

View File

@ -22,7 +22,6 @@ import {IResearchShort} from "../../types/IResearchShort";
import {ResearchUrl, WeaponUrl} from "../../core/api";
import {IWeapon} from "../../types/IUnit";
import {styled} from '@mui/material/styles';
import {DescriptionBox} from "../../commons/DescriptionBox";
import {StyledPaper} from "../../commons/StyledPaper";
@ -96,10 +95,7 @@ function ResearchFull(props: { id: number, building: IBuilding }){
</TableContainer><br/>
</Grid2>
<Grid2 size={{xs: 12, md: 8}}>
<DescriptionBox>
{research.description}
</DescriptionBox>
<span style={{ whiteSpace: 'pre-wrap' }}>{research.description}</span>
</Grid2>
<Grid2 size={{xs: 12, md: 12}}>
<ModifiersProvidesTable modifiers={research.modifiers} affectedData={research.affectedData}

View File

@ -17,6 +17,10 @@ export const StyledTableCell = styled(TableCell)(({theme}) => {
color: textColor,
paddingRight: 18,
paddingLeft: 10,
[theme.breakpoints.down('md')]: {
paddingRight: 4,
paddingLeft: 2,
},
},
};
});

View File

@ -21,6 +21,11 @@
width: 40px;
}
.jumpIcon{
padding: 0px;
width: 40px;
}
.unitHeader{
margin-left: 15px;
}

View File

@ -183,7 +183,6 @@ function Mods({ mods }: ModsProps) {
const sameMods = mapWithModVersions.get(modName) ?? [];
// The latest version is the one with the maximum id
const latest = sameMods.reduce((max, mod) => mod.id > max.id ? mod : max, sameMods[0]);
const nonBetaMods = sameMods.filter(m => !m.isBeta);
const betaMods = sameMods.filter(m => m.isBeta);
return {
latest: latest,
@ -329,7 +328,7 @@ class ModsPage extends React.Component<any, ModsPageState> {
const response = await fetch(AvailableMods);
const data: IMod[] = await response.json();
this.setState({
mods: data,
mods: data.filter(mod => !mod.isHide),
loading: false
});
} catch (error) {

View File

@ -33,6 +33,7 @@ import {AffectedResearches} from "../classes/building/Research";
import {ModifiersProvidesTable} from "../classes/ModifiersProvideTable";
import Ability from "../classes/Ability";
import DeathExplosion from "../classes/DeathExplosion";
import Jump from "../classes/Jump";
import {styled} from '@mui/material/styles';
import {withTheme} from "../core/api";
import {DescriptionBox} from "../commons/DescriptionBox";
@ -337,7 +338,7 @@ function Unit(unit: IUnit, mod: IMod, theme: Theme) {
unit.moraleBrakeModifiers.find(m => m.reference.includes("speed_maximum_modifier") && m.value === 1.2) !== undefined &&
unit.moraleBrakeModifiers.length === 2)
&& <Grid2 size={12}>
<SectionTitle><img style={{verticalAlign: "top"}} src="/images/ARM_Morale.webp"/> Morale broken unusual behavior <img style={{verticalAlign: "top"}} src="/images/ARM_Morale.webp"/></SectionTitle>
<SectionTitle><img style={{verticalAlign: "top"}} src="/images/ARM_Morale.webp"/> Morale broken <img style={{verticalAlign: "top"}} src="/images/ARM_Morale.webp"/></SectionTitle>
<ModifiersProvidesTable modifiers={unit.moraleBrakeModifiers} modId={unit.modId} race={unit.race} affectedData={unit.affectedData}/>
</Grid2>}
@ -353,6 +354,11 @@ function Unit(unit: IUnit, mod: IMod, theme: Theme) {
)}
</Grid2>}
{unit.jumps != null && <Grid2 size={12}>
<SectionTitle>Jumps</SectionTitle>
<Jump jumps={unit.jumps} race={unit.race} mod={mod}/>
</Grid2>}
<Grid2 size={12}>
{[...mapWithUnitWeapons.keys()].sort(function (a, b) {
return a - b;

View File

@ -61,8 +61,8 @@ export const lightTheme = createTheme({
paper: '#ffffff',
},
text: {
primary: 'rgba(0, 0, 0, 0.85)',
secondary: 'rgba(0, 0, 0, 0.6)',
primary: '#111111',
secondary: '#333333',
},
},
typography: {
@ -75,7 +75,7 @@ export const lightTheme = createTheme({
MuiTableCell: {
styleOverrides: {
root: {
color: 'rgba(0, 0, 0, 0.85)',
color: '#111111',
},
},
},

View File

@ -13,6 +13,21 @@ export interface IUnitResponse {
units: IUnit[]
}
export interface IJumps {
chargeRegeneration: number;
goUpTime?: number;
jumpDistanceMax: number;
breakdownTime: number;
chargeJumpCostMin: number;
chargeJumpCostMax: number;
jumpHeight: number;
chargeMax: number;
setupTime: number;
goDownTime?: number;
teleport: boolean;
requirements: IRequirement | null;
}
export interface IUnit {
id: number
race: Irace
@ -69,6 +84,7 @@ export interface IUnit {
affectedData: IAffectedData,
affectedResearches: IResearchShort[],
deathExplosions: IDeathExplosion[],
jumps?: IJumps | null,
hotkey?: string,
}

View File

@ -5,4 +5,5 @@ export interface IMod {
version: string;
technicalName: string;
isBeta: boolean;
isHide: boolean;
}