Wiki full

This commit is contained in:
v.holodov 2025-01-08 02:44:30 +03:00
parent 27308c2c88
commit 2b4352ff21
23 changed files with 18876 additions and 27 deletions

8
.idea/.gitignore generated vendored Normal file
View File

@ -0,0 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml
# Editor-based HTTP Client requests
/httpRequests/

1
.idea/.name generated Normal file
View File

@ -0,0 +1 @@
dow-wiki-frontend

13
.idea/codeStyles/Project.xml generated Normal file
View File

@ -0,0 +1,13 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<JetCodeStyleSettings>
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</JetCodeStyleSettings>
<ScalaCodeStyleSettings>
<option name="MULTILINE_STRING_CLOSING_QUOTES_ON_NEW_LINE" value="true" />
</ScalaCodeStyleSettings>
<codeStyleSettings language="kotlin">
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</codeStyleSettings>
</code_scheme>
</component>

5
.idea/codeStyles/codeStyleConfig.xml generated Normal file
View File

@ -0,0 +1,5 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
</state>
</component>

9
.idea/dow-wiki-frontend.iml generated Normal file
View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View File

@ -0,0 +1,6 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
</profile>
</component>

6
.idea/misc.xml generated Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" languageLevel="JDK_16">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

8
.idea/modules.xml generated Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/dow-wiki-frontend.iml" filepath="$PROJECT_DIR$/.idea/dow-wiki-frontend.iml" />
</modules>
</component>
</project>

6
.idea/sbt.xml generated Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ScalaSbtSettings">
<option name="customVMPath" />
</component>
</project>

6
.idea/vcs.xml generated Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

11
index.html Normal file
View File

@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Webpack App</title>
</head>
<body>
<h1>Hello world!</h1>
<h2>Tip: Check your console</h2>
</body>
</html>

18412
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 B

View File

@ -0,0 +1,3 @@
#back-button{
margin-top: 10px;
}

View File

@ -1,15 +1,57 @@
import React from "react"; import React from "react";
interface IArmorType{ interface IArmorType{
value: string name: string,
withName: boolean
} }
class ArmorType extends React.Component<IArmorType, any> { class ArmorType extends React.Component<IArmorType, any> {
public static defaultProps = {
withName: false
};
renderArmorImage(armorType: String): string {
switch(armorType) {
case 'Infantry Low':
return '/images/ARM_Inf_Lo.webp';
case 'Infantry Medium':
return '/images/ARM_Inf_Mid.webp';
case 'Infantry High':
return '/images/ARM_Inf_Hi.webp';
case 'Infantry Heavy Medium':
return '/images/ARM_Hvy_Inf_Mid.webp';
case 'Infantry Heavy High':
return '/images/ARM_Hvy_Inf_Hi.webp';
case 'Commander':
return '/images/ARM_Cmdr.webp';
case 'Vehicle Low':
return '/images/ARM_Veh_Lo.webp';
case 'Vehicle Medium':
return '/images/ARM_Veh_Mid.webp';
case 'Vehicle High':
return '/images/ARM_Veh_Hi.webp';
case 'Air':
return '/images/ARM_Air.webp';
case 'Building Low':
return '/images/ARM_Bld_Lo.webp';
case 'Building Medium':
return '/images/ARM_Bld_Mid.webp';
case 'Building High':
return '/images/ARM_Bld_Hi.webp';
case 'Demon Medium':
return '/images/ARM_Dmn_Mid.webp';
case 'Demon High':
return '/images/ARM_Dmn_Hi.webp';
default:
return 'Unknown armor';
}
}
render() { render() {
return this.props.value;
return (<span> <img style={{verticalAlign: "top"}} src={this.renderArmorImage(this.props.name)}/> {this.props.withName && this.props.name} </span>);
} }
} }
export default ArmorType; export default ArmorType;

240
src/classes/Weapon.tsx Normal file
View File

@ -0,0 +1,240 @@
import React from "react";
import {IWeapon, WeaponPiercing} from "../types/IUnit";
import {
Grid2,
Paper,
styled,
Table,
TableBody,
TableCell,
tableCellClasses,
TableContainer,
TableHead,
TableRow, ToggleButton, ToggleButtonGroup
} from "@mui/material";
import ArmorType from "./ArmorType";
import ArmorTypeNames from "../types/ArmorTypeValues";
interface IWeaponProps{
weapon: IWeapon,
}
interface IWeaponState{
currentTable: "dps" | "one hit",
}
class Weapon extends React.Component<IWeaponProps, any> {
humanReadableName(unit: string) {
const firstUpper = String(unit).charAt(0).toUpperCase() + String(unit).slice(1);
return firstUpper.replaceAll('_', ' ').replace('.rgd', '');
}
constructor(props: any) {
super(props);
this.state = ({
currentTable: 'dps'
});
}
getPiercingK(armorType: string): number {
const weaponPiercing = this.props.weapon.weaponPiercings.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.InfantryHeavyMedium)
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)
const getTotalDamage = (damagePiercing: number, isAir: boolean = false) => {
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(234, 234, 234)",
color: theme.palette.common.white,
},
[`&.${tableCellClasses.body}`]: {
fontSize: 14,
},
}));
return (
<div>
<h4>{this.humanReadableName(weapon.name)}</h4>
<Grid2 container spacing={2}>
<Grid2 size={3}>
<TableContainer component={Paper}>
<Table size="small" aria-label="a dense table">
<TableBody>
<TableRow
sx={{'&:last-child td, &:last-child th': {border: 0}}}
>
<TableCell component="th" scope="row">Base damage</TableCell>
<TableCell component="th"
scope="row">{weapon.minDamage} {weapon.maxDamage !== weapon.minDamage && -weapon.maxDamage}
</TableCell>
</TableRow>
<TableRow
sx={{'&:last-child td, &:last-child th': {border: 0}}}
>
<TableCell component="th" scope="row">Accuracy</TableCell>
<TableCell component="th" scope="row">{weapon.accuracy.toFixed(2)}
</TableCell>
</TableRow>
<TableRow
sx={{'&:last-child td, &:last-child th': {border: 0}}}
>
<TableCell component="th" scope="row">Accuracy moving</TableCell>
<TableCell component="th"
scope="row">{weapon.setupTime === 0 ? (weapon.accuracy - weapon.accuracyReductionMoving).toFixed(2) : "-"}
</TableCell>
</TableRow>
<TableRow
sx={{'&:last-child td, &:last-child th': {border: 0}}}
>
<TableCell component="th" scope="row">Reload time</TableCell>
<TableCell component="th"
scope="row">{weapon.reloadTime}
</TableCell>
</TableRow>
<TableRow
sx={{'&:last-child td, &:last-child th': {border: 0}}}
>
<TableCell component="th" scope="row">Max range</TableCell>
<TableCell component="th"
scope="row">{weapon.maxRange ? (weapon.maxRange) : "-"}
</TableCell>
</TableRow>
<TableRow
sx={{'&:last-child td, &:last-child th': {border: 0}}}
>
<TableCell component="th" scope="row">Setup time</TableCell>
<TableCell component="th"
scope="row">{weapon.setupTime != 0 ? (weapon.setupTime).toFixed(2) : "-"}
</TableCell>
</TableRow>
</TableBody>
</Table>
</TableContainer>
</Grid2>
<Grid2 size={9}>
<ToggleButtonGroup
color="primary"
exclusive
value={this.state.currentTable}
onChange={this.handleChange}
aria-label="Platform"
>
<ToggleButton size="small" value="dps">Dps</ToggleButton>
<ToggleButton size="small" value="one hit">One hit average damage</ToggleButton>
</ToggleButtonGroup>
<TableContainer>
<Table sx={{minWidth: 600, 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.DemonMedium}/></StyledTableCell>
<StyledTableCell><ArmorType name={ArmorTypeNames.DemonHigh}/></StyledTableCell>
<StyledTableCell><ArmorType name={ArmorTypeNames.Commander}/></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(demonPiercing)}</StyledTableCell>
<StyledTableCell>{getTotalDamage(demonHighPiercing)}</StyledTableCell>
<StyledTableCell>{getTotalDamage(commanderPiercing)}</StyledTableCell>
</TableRow>
</TableBody>
<TableHead>
<TableRow >
<StyledTableCell><ArmorType name={ArmorTypeNames.Air}/></StyledTableCell>
<StyledTableCell><ArmorType name={ArmorTypeNames.VehicleLow}/></StyledTableCell>
<StyledTableCell><ArmorType name={ArmorTypeNames.VehicleMedium}/></StyledTableCell>
<StyledTableCell><ArmorType name={ArmorTypeNames.VehicleHigh}/></StyledTableCell>
<StyledTableCell><ArmorType name={ArmorTypeNames.BuildingLow}/></StyledTableCell>
<StyledTableCell><ArmorType name={ArmorTypeNames.BuildingMedium}/></StyledTableCell>
<StyledTableCell><ArmorType name={ArmorTypeNames.BuildingHigh}/></StyledTableCell>
<StyledTableCell><img style={{verticalAlign: "top"}} src="/images/ARM_Morale.webp"/></StyledTableCell>
</TableRow>
</TableHead>
<TableBody>
<TableRow>
<StyledTableCell>{getTotalDamage(airPiercing)}</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>
</Grid2>
</Grid2>
</div>
);
}
}
export default Weapon;

View File

@ -4,6 +4,8 @@ import {NavLink, useLocation, useParams} from "react-router-dom";
import {withRouter} from "../core/withrouter"; import {withRouter} from "../core/withrouter";
import {IMod} from "../types/Imod"; import {IMod} from "../types/Imod";
import {Irace} from "../types/Irace"; import {Irace} from "../types/Irace";
import {Button} from "@mui/material";
import {ArrowBack} from "@mui/icons-material";
interface ModPageState { interface ModPageState {
@ -42,8 +44,14 @@ class ModPage extends React.Component<any, ModPageState> {
render() { render() {
if(this.state != null && this.state.races != null ){ if(this.state != null && this.state.races != null ){
return <div><h1>{this.state.mod.name} ({this.state.mod.version})</h1>
{this.state.races.map(race => <ul><NavLink state={race.id} to= {"/mod/" + this.state.mod.id + "/race/" + race.id} >{race.name}</NavLink></ul>)} return <div>
<a href="/"><Button id="back-button" variant="contained"
startIcon={<ArrowBack/>}> Back</Button></a>
<h1>{this.state.mod.name} ({this.state.mod.version})</h1>
{this.state.races.map(race => <ul><NavLink state={race.id}
to={"/mod/" + this.state.mod.id + "/race/" + race.id}>{race.name}</NavLink>
</ul>)}
</div>; </div>;
} else { } else {
return ""; return "";

View File

@ -29,9 +29,6 @@ function Mods (mods: IMod[]) {
) )
} }
mapWithModVersions.keys()
return( return(
[...mapWithModVersions.keys()].map(m => Mod(m)) [...mapWithModVersions.keys()].map(m => Mod(m))
) )

View File

@ -3,20 +3,33 @@ import React, {useState} from "react";
import {withRouter} from "../core/withrouter"; import {withRouter} from "../core/withrouter";
import {IMod} from "../types/Imod"; import {IMod} from "../types/Imod";
import {Irace} from "../types/Irace"; import {Irace} from "../types/Irace";
import {IUnit, IUnitResponse} from "../types/IUnit"; import {IUnit, IUnitResponse, IWeapon} from "../types/IUnit";
import '../css/Unit.css' import '../css/Unit.css'
import { import {
Accordion, AccordionDetails, Accordion,
AccordionDetails,
AccordionSummary, AccordionSummary,
Button, Button,
ButtonGroup, Grid2, Paper, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, ButtonGroup,
Grid2,
List,
ListItem,
ListSubheader,
Paper,
Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
ToggleButton, ToggleButton,
ToggleButtonClassKey, ToggleButtonClassKey,
ToggleButtonGroup, Typography ToggleButtonGroup,
Typography
} from "@mui/material"; } from "@mui/material";
import {ArrowDropDown} from "@mui/icons-material"; import {ArrowBack, ArrowDropDown} from "@mui/icons-material";
import ArmorType from "../classes/ArmorType"; import ArmorType from "../classes/ArmorType";
import Item from "../classes/Item"; import Weapon from "../classes/Weapon";
interface RacePageState { interface RacePageState {
mod: IMod, mod: IMod,
@ -26,12 +39,6 @@ interface RacePageState {
} }
function humanReadableUnitName(unit: string) {
const firstUpper = String(unit).charAt(0).toUpperCase() + String(unit).slice(1);
return firstUpper.replaceAll('_', ' ').replace('.rgd', '');
}
function Unit (unit: IUnit) { function Unit (unit: IUnit) {
const morale = (unit.moraleMax !== null) ? <span> const morale = (unit.moraleMax !== null) ? <span>
@ -46,6 +53,31 @@ function Unit (unit: IUnit) {
<span>&nbsp;<img style={{verticalAlign: "top"}} <span>&nbsp;<img style={{verticalAlign: "top"}}
src="/images/DETECT_NO.webp"/></span> src="/images/DETECT_NO.webp"/></span>
let mapWithUnitWeapons: Map<number, IWeapon[]> = new Map();
unit.weapons.forEach( weapon => {
const weaponList = mapWithUnitWeapons.get(weapon.hardpoint)
if(weaponList == null){
mapWithUnitWeapons.set(weapon.hardpoint, new Array(weapon.weapon))
} else {
weaponList.push(weapon.weapon)
}
})
function WeaponSlot (hardpoint: number) {
let weaponThisSlot = mapWithUnitWeapons.get(hardpoint) ?? []
let header = "Weapon slot " + hardpoint
return(
<div>
<h3>{header}</h3>
{weaponThisSlot.map(weapon => <div><Weapon weapon={weapon}/>
</div>)}
</div>
)
}
return ( return (
<Accordion> <Accordion>
@ -90,7 +122,7 @@ function Unit (unit: IUnit) {
> >
<TableCell component="th" scope="row">armor type</TableCell> <TableCell component="th" scope="row">armor type</TableCell>
<TableCell component="th" scope="row"> <TableCell component="th" scope="row">
<ArmorType value={unit.armorType.name}/> <ArmorType name={unit.armorType.name} withName={true}/>
</TableCell> </TableCell>
</TableRow> </TableRow>
<TableRow <TableRow
@ -137,6 +169,11 @@ function Unit (unit: IUnit) {
{unit.description} {unit.description}
</div> </div>
</Grid2> </Grid2>
<Grid2 size={12}>
{[...mapWithUnitWeapons.keys()].sort(function(a, b) {
return a - b;
}).map(h => WeaponSlot(h))}
</Grid2>
</Grid2> </Grid2>
<i>{unit.filename}</i> <i>{unit.filename}</i>
</AccordionDetails> </AccordionDetails>
@ -162,7 +199,6 @@ class Units extends React.Component<IUnitResponse, UnitsState> {
this.setState({ this.setState({
selectedUnits: newUnitType selectedUnits: newUnitType
}); });
{this.props.units.map(unit => Unit(unit))}
} }
render () { render () {
@ -173,8 +209,10 @@ class Units extends React.Component<IUnitResponse, UnitsState> {
unitsToRender = this.props.units.filter(unit => unit.capInfantry > 0) unitsToRender = this.props.units.filter(unit => unit.capInfantry > 0)
} else if (this.state.selectedUnits === 'tech'){ } else if (this.state.selectedUnits === 'tech'){
unitsToRender = this.props.units.filter(unit => unit.capSupport > 0) unitsToRender = this.props.units.filter(unit => unit.capSupport > 0)
} else { } else if (this.state.selectedUnits === 'support'){
unitsToRender = this.props.units.filter(unit => unit.capSupport == 0 && unit.capInfantry == 0) unitsToRender = this.props.units.filter(unit => unit.capSupport == 0 && unit.capInfantry == 0)
} else {
unitsToRender = this.props.units
} }
@ -239,12 +277,18 @@ class RacePage extends React.Component<any, RacePageState> {
} }
render() { render() {
if(this.state != null && this.state.units != null ){ if(this.state != null && this.state.units != null ){
return <div><h1>{this.state.mod.name} ({this.state.mod.version}) - {this.state.race.name}</h1> const backRef = "/mod/" + this.state.mod.id
return <div><a href={backRef}><Button id="back-button" variant="contained" startIcon={<ArrowBack/>}> Back</Button></a>
<h1>{this.state.mod.name} ({this.state.mod.version}) - {this.state.race.name}</h1>
<Units race={this.state.race.name} units={this.state.units.units}/> <Units race={this.state.race.name} units={this.state.units.units}/>
</div>; </div>;
} else { } else {
return ""; return "loading...";
} }
} }
} }

View File

@ -0,0 +1,19 @@
enum ArmorTypeValues {
InfantryLow = 'Infantry Low',
InfantryMedium = 'Infantry Medium',
InfantryHigh = 'Infantry High',
InfantryHeavyMedium = 'Infantry Heavy Medium',
InfantryHeavyHigh = 'Infantry Heavy High',
Commander = 'Commander',
VehicleLow = 'Vehicle Low',
VehicleMedium = 'Vehicle Medium',
VehicleHigh = 'Vehicle High',
Air = 'Air',
BuildingLow = 'Building Low',
BuildingMedium = 'Building Medium',
BuildingHigh = 'Building High',
DemonMedium = 'Demon Medium',
DemonHigh = 'Demon High',
}
export default ArmorTypeValues;

View File

@ -42,11 +42,11 @@ export interface IUnit {
export interface WeaponHardpoint { export interface WeaponHardpoint {
weapon: Weapon weapon: IWeapon
hardpoint: number hardpoint: number
} }
export interface Weapon { export interface IWeapon {
id: number id: number
name: string name: string
costRequisition: number costRequisition: number
@ -56,9 +56,14 @@ export interface Weapon {
reloadTime: number reloadTime: number
setupTime: number setupTime: number
accuracyReductionMoving: number accuracyReductionMoving: number
maxRange: number
minDamage: number minDamage: number
maxDamage: number maxDamage: number
minDamageValue: number
moraleDamage: number moraleDamage: number
isMeleeWeapon: boolean
canAttackAir: boolean
canAttackGround: boolean
modId: number modId: number
weaponPiercings: WeaponPiercing[] weaponPiercings: WeaponPiercing[]
} }