Add research support
This commit is contained in:
parent
f94553bb9a
commit
851bb71601
1
.idea/inspectionProfiles/Project_Default.xml
generated
1
.idea/inspectionProfiles/Project_Default.xml
generated
@ -2,5 +2,6 @@
|
|||||||
<profile version="1.0">
|
<profile version="1.0">
|
||||||
<option name="myName" value="Project Default" />
|
<option name="myName" value="Project Default" />
|
||||||
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
|
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="UsePropertyAccessSyntax" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
|
||||||
</profile>
|
</profile>
|
||||||
</component>
|
</component>
|
||||||
@ -1,15 +1,16 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import {Tooltip} from "@mui/material";
|
import {Tooltip} from "@mui/material";
|
||||||
|
import ArmorTypeValues from "../types/ArmorTypeValues";
|
||||||
|
|
||||||
interface IArmorType{
|
interface IArmorType{
|
||||||
name: string,
|
name: string,
|
||||||
withName: boolean
|
compact: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
class ArmorType extends React.Component<IArmorType, any> {
|
class ArmorType extends React.Component<IArmorType, any> {
|
||||||
|
|
||||||
public static defaultProps = {
|
public static defaultProps = {
|
||||||
withName: false
|
compact: true
|
||||||
};
|
};
|
||||||
|
|
||||||
renderArmorImage(armorTypeId: string): string {
|
renderArmorImage(armorTypeId: string): string {
|
||||||
@ -20,11 +21,11 @@ class ArmorType extends React.Component<IArmorType, any> {
|
|||||||
return '/images/ARM_Inf_Mid.webp';
|
return '/images/ARM_Inf_Mid.webp';
|
||||||
case 'Infantry High':
|
case 'Infantry High':
|
||||||
return '/images/ARM_Inf_Hi.webp';
|
return '/images/ARM_Inf_Hi.webp';
|
||||||
case 'Infantry Heavy Medium':
|
case ArmorTypeValues.InfantryHeavyMedium:
|
||||||
return '/images/ARM_Hvy_Inf_Mid.webp';
|
return '/images/ARM_Hvy_Inf_Mid.webp';
|
||||||
case 'Infantry Heavy High':
|
case ArmorTypeValues.InfantryHeavyHigh:
|
||||||
return '/images/ARM_Hvy_Inf_Hi.webp';
|
return '/images/ARM_Hvy_Inf_Hi.webp';
|
||||||
case 'Commander':
|
case ArmorTypeValues.Commander:
|
||||||
return '/images/ARM_Cmdr.webp';
|
return '/images/ARM_Cmdr.webp';
|
||||||
case 'Living Metal':
|
case 'Living Metal':
|
||||||
return '/images/ARM_living_metal.webp';
|
return '/images/ARM_living_metal.webp';
|
||||||
@ -34,7 +35,7 @@ class ArmorType extends React.Component<IArmorType, any> {
|
|||||||
return '/images/ARM_Veh_Mid.webp';
|
return '/images/ARM_Veh_Mid.webp';
|
||||||
case 'Vehicle High':
|
case 'Vehicle High':
|
||||||
return '/images/ARM_Veh_Hi.webp';
|
return '/images/ARM_Veh_Hi.webp';
|
||||||
case 'Air':
|
case 'Vehicle Air':
|
||||||
return '/images/ARM_Air.webp';
|
return '/images/ARM_Air.webp';
|
||||||
case 'Building Low':
|
case 'Building Low':
|
||||||
return '/images/ARM_Bld_Lo.webp';
|
return '/images/ARM_Bld_Lo.webp';
|
||||||
@ -58,9 +59,14 @@ class ArmorType extends React.Component<IArmorType, any> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
if(this.props.compact){
|
||||||
|
return (<span> {this.props.compact ?
|
||||||
|
<div style={{ fontSize: 12, width: 40, textAlign: "center"}}><img src={this.renderArmorImage(this.props.name)}/><i>{this.props.name}</i>
|
||||||
|
</div> :
|
||||||
|
<span><img src={this.renderArmorImage(this.props.name)}/> {this.props.name}</span>} </span>);
|
||||||
|
} else
|
||||||
|
return <span ><img style={{verticalAlign: "top"}} src={this.renderArmorImage(this.props.name)}/> {this.props.name} </span>;
|
||||||
|
|
||||||
return (<Tooltip title={this.props.name}><span> <img style={{verticalAlign: "top"}}
|
|
||||||
src={this.renderArmorImage(this.props.name)}/> {this.props.withName && this.props.name}</span></Tooltip>);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,27 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
|
|
||||||
export default class LobbyList extends React.Component {
|
|
||||||
constructor(props){
|
|
||||||
super(props);
|
|
||||||
this.state = { messages : [] }
|
|
||||||
}
|
|
||||||
componentDidMount(){
|
|
||||||
// this is an "echo" websocket service
|
|
||||||
this.connection = new WebSocket('wss://echo.websocket.org');
|
|
||||||
// listen to onmessage event
|
|
||||||
this.connection.onmessage = evt => {
|
|
||||||
// add the new message to state
|
|
||||||
this.setState({
|
|
||||||
messages : this.state.messages.concat([ evt.data ])
|
|
||||||
})
|
|
||||||
};
|
|
||||||
// for testing purposes: sending to the echo service which will send it back back
|
|
||||||
setInterval( _ => {
|
|
||||||
this.connection.send( Math.random() )
|
|
||||||
}, 2000 )
|
|
||||||
}
|
|
||||||
render() {
|
|
||||||
// slice(-5) gives us the five most recent messages
|
|
||||||
return <ul>{ this.state.messages.slice(-5).map( (msg, idx) => <li key={'msg-' + idx }>{ msg }</li> )}</ul>;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
223
src/classes/ModifiersProvideTable.tsx
Normal file
223
src/classes/ModifiersProvideTable.tsx
Normal file
@ -0,0 +1,223 @@
|
|||||||
|
import {Paper, Table, TableCell, TableContainer, TableRow} from "@mui/material";
|
||||||
|
import React from "react";
|
||||||
|
import {IModifier} from "../types/IModifier";
|
||||||
|
import {IconUrl} from "../core/api";
|
||||||
|
import {IResearch} from "../types/IResearch";
|
||||||
|
import {Irace} from "../types/Irace";
|
||||||
|
|
||||||
|
interface IModifiersProvidesTable{
|
||||||
|
modifiers: IModifier[],
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IModifiersProvidesResearchTable{
|
||||||
|
modifiers: IModifier[],
|
||||||
|
research: IResearch,
|
||||||
|
race: Irace,
|
||||||
|
}
|
||||||
|
|
||||||
|
function getModName(mt: String){
|
||||||
|
switch(mt) {
|
||||||
|
case 'modifiers\\armour_modifier.lua':
|
||||||
|
return 'Armor';
|
||||||
|
case 'modifiers\\health_maximum_modifier.lua':
|
||||||
|
return 'Health';
|
||||||
|
case 'modifiers\\keen_sight_radius_modifier.lua':
|
||||||
|
return 'Detect';
|
||||||
|
case 'modifiers\\sight_radius_modifier.lua':
|
||||||
|
return 'Sight';
|
||||||
|
case 'modifiers\\garrison_requisition_modifier.lua':
|
||||||
|
return 'Requisition';
|
||||||
|
case 'modifiers\\faith_max_modifier.lua':
|
||||||
|
return 'Faith max';
|
||||||
|
case 'modifiers\\holy_icon_cost_power.lua':
|
||||||
|
return <span>Next icon cost <img style={{verticalAlign: "top"}} src='/images/Resource_power.gif'/></span>;
|
||||||
|
case 'modifiers\\holy_icon_cost_requisition.lua':
|
||||||
|
return <span>Next icon cost <img style={{verticalAlign: "top"}} src='/images/Resource_requisition.gif'/></span>;
|
||||||
|
case 'modifiers\\enable_hardpoint_02.lua':
|
||||||
|
return 'Weapon slot 2';
|
||||||
|
case 'modifiers\\default_weapon_modifier_hardpoint1.lua':
|
||||||
|
return 'Weapon slot 1 change weapon';
|
||||||
|
case 'modifiers\\default_weapon_modifier_hardpoint2.lua':
|
||||||
|
return 'Weapon slot 2 change weapon';
|
||||||
|
case 'modifiers\\default_weapon_modifier_hardpoint3.lua':
|
||||||
|
return 'Weapon slot 3 change weapon';
|
||||||
|
case 'modifiers\\default_weapon_modifier_hardpoint4.lua':
|
||||||
|
return 'Weapon slot 4 change weapon';
|
||||||
|
case 'modifiers\\default_weapon_modifier_hardpoint5.lua':
|
||||||
|
return 'Weapon slot 5 change weapon';
|
||||||
|
case 'modifiers\\default_weapon_modifier_hardpoint6.lua':
|
||||||
|
return 'Weapon slot 6 change weapon';
|
||||||
|
case 'modifiers\\morale_maximum_squad_modifier.lua':
|
||||||
|
return 'Morale';
|
||||||
|
case 'modifiers\\max_upgrades_squad_modifier.lua':
|
||||||
|
return 'Max equip weapons';
|
||||||
|
case 'modifiers\\speed_maximum_modifier.lua':
|
||||||
|
return 'Max speed';
|
||||||
|
case 'modifiers\\min_damage_weapon_modifier.lua':
|
||||||
|
return 'Min damage';
|
||||||
|
case 'modifiers\\max_damage_weapon_modifier.lua':
|
||||||
|
return 'Max damage';
|
||||||
|
case 'modifiers\\enable_armour_2.lua':
|
||||||
|
return 'Change armor type';
|
||||||
|
case 'modifiers\\cost_requisition_modifier.lua':
|
||||||
|
return 'Requisition cost';
|
||||||
|
default:
|
||||||
|
return mt.replace("modifiers\\", "").replace("modifier.lua", "").replace(".lua", "").replaceAll("_", " ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getModIcon(mt: String){
|
||||||
|
switch(mt) {
|
||||||
|
case 'modifiers\\armour_modifier.lua':
|
||||||
|
return <img style={{height: 20, verticalAlign: "top"}} src='/images/defence.png'/>;
|
||||||
|
case 'modifiers\\health_maximum_modifier.lua':
|
||||||
|
return <img style={{verticalAlign: "top"}} src='/images/Health_icon.webp'/>;
|
||||||
|
case 'modifiers\\keen_sight_radius_modifier.lua':
|
||||||
|
return <img style={{verticalAlign: "top"}} src='/images/DETECT_YES.webp'/>;
|
||||||
|
case 'modifiers\\sight_radius_modifier.lua':
|
||||||
|
return <img style={{verticalAlign: "top"}} src='/images/DETECT_NO.webp'/>;
|
||||||
|
case 'modifiers\\faith_max_modifier.lua':
|
||||||
|
return <img style={{verticalAlign: "top"}} src='/images/Resource_faith.gif'/>;
|
||||||
|
case 'modifiers\\garrison_requisition_modifier.lua':
|
||||||
|
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\\morale_maximum_squad_modifier.lua':
|
||||||
|
return <img style={{verticalAlign: "top"}} src='/images/ARM_Morale.webp'/>;
|
||||||
|
default:
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getChangeDescription(ref: String, v: number) {
|
||||||
|
switch(ref) {
|
||||||
|
case 'type_modifierusagetype\\tp_mod_usage_addition.lua':
|
||||||
|
if(v < 0){
|
||||||
|
return v
|
||||||
|
}else return '+' + v;
|
||||||
|
case 'type_modifierusagetype\\tp_mod_usage_multiplication.lua':
|
||||||
|
return 'x' + v;
|
||||||
|
case 'type_modifierusagetype\\tp_mod_usage_percentage.lua':
|
||||||
|
return '%' + v;
|
||||||
|
case 'type_modifierusagetype\\tp_mod_usage_enable.lua':
|
||||||
|
if(v === 1){
|
||||||
|
return ' enable ';
|
||||||
|
} else {
|
||||||
|
return ' disable ';
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return ref;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ModifiersProvidesAddonTable (props: IModifiersProvidesTable) {
|
||||||
|
|
||||||
|
let noWeaponMods = props.modifiers.filter(m => !m.reference.includes("default_weapon_modifier_hardpoint"))
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TableContainer component={Paper}>
|
||||||
|
<Table size="small" aria-label="a dense table">
|
||||||
|
{noWeaponMods.map(m =>
|
||||||
|
<TableRow
|
||||||
|
sx={{'&:last-child td, &:last-child th': {border: 0}}}
|
||||||
|
>
|
||||||
|
<TableCell component="th" scope="row">{getModName(m.reference)}</TableCell>
|
||||||
|
<TableCell component="th" scope="row">{getModIcon(m.reference)} {getChangeDescription(m.usageType, m.value)}</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
)}
|
||||||
|
</Table>
|
||||||
|
</TableContainer>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ModifiersProvidesResearchTable (props: IModifiersProvidesResearchTable) {
|
||||||
|
|
||||||
|
let mods = props.modifiers
|
||||||
|
|
||||||
|
function getTarget(target: String, research: IResearch, race: Irace) {
|
||||||
|
console.log(research.affectedUnits.map(au => au.filename))
|
||||||
|
var affectedUnit = research.affectedUnits.find(au => au.filename.replaceAll('.rgd', '').split(";")[0] === target || au.filename.replaceAll('.rgd', '').split(";")[1] === target);
|
||||||
|
if(affectedUnit != null) {
|
||||||
|
return <span><img style={{verticalAlign: "top", height: 25}}
|
||||||
|
src={getIcon(affectedUnit.icon)}/><a
|
||||||
|
href={`/mod/${research.modId}/race/${race.id}/unit/${affectedUnit.id}`}>
|
||||||
|
{affectedUnit.name}
|
||||||
|
</a></span>
|
||||||
|
}
|
||||||
|
var affectedSergeant = research.affectedSergeants.find(as => as.filename.replaceAll('.rgd', '') === target);
|
||||||
|
if(affectedSergeant != null) {
|
||||||
|
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/${research.modId}/race/${race.id}/unit/${affectedSergeant.unit.id}`}>
|
||||||
|
{affectedSergeant.unit.name}
|
||||||
|
</a></span>
|
||||||
|
}
|
||||||
|
var affectedBuilding = research.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/${research.modId}/race/${race.id}/building/${affectedBuilding.id}`}>
|
||||||
|
{affectedBuilding.name}
|
||||||
|
</a> </span>
|
||||||
|
}
|
||||||
|
|
||||||
|
var affectedWeapon = research.affectedWeapons.find(aw => aw.filename.replaceAll('.rgd', '') === target);
|
||||||
|
if (affectedWeapon != null) {
|
||||||
|
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/${research.modId}/race/${race.id}/unit/${unit.id}`}>
|
||||||
|
{unit.name}
|
||||||
|
</a> </span>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
affectedWeapon.sergeants.map(affectedSergeant =>
|
||||||
|
<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/${research.modId}/race/${race.id}/unit/${affectedSergeant.unit.id}`}>
|
||||||
|
{affectedSergeant.unit.name}
|
||||||
|
</a> </span>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
affectedWeapon.buildings.map(affectedBuilding =>
|
||||||
|
<span><img style={{verticalAlign: "top", height: 25}} src={getIcon(affectedBuilding.icon)}/><a
|
||||||
|
href={`/mod/${research.modId}/race/${race.id}/building/${affectedBuilding.id}`}>
|
||||||
|
{affectedBuilding.name}
|
||||||
|
</a> </span>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return <span>Unknown target <i>{target}</i></span>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TableContainer component={Paper}>
|
||||||
|
<Table size="small" aria-label="a dense table">
|
||||||
|
{mods.map(m =>
|
||||||
|
<TableRow
|
||||||
|
sx={{'&:last-child td, &:last-child th': {border: 0}}}
|
||||||
|
>
|
||||||
|
<TableCell component="th" scope="row">{getTarget(m.target, props.research, props.race)}</TableCell>
|
||||||
|
<TableCell style={{minWidth: 120}} component="th" scope="row">{getModName(m.reference)}</TableCell>
|
||||||
|
<TableCell component="th" scope="row">{getModIcon(m.reference)} {getChangeDescription(m.usageType, m.value)}</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
)}
|
||||||
|
</Table>
|
||||||
|
</TableContainer>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getIcon(icon?: string): string{
|
||||||
|
if(icon !== null && icon !== undefined){
|
||||||
|
return IconUrl + icon.replaceAll('\\', '/')
|
||||||
|
} else return '/images/shakal.png'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
97
src/classes/Required.tsx
Normal file
97
src/classes/Required.tsx
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
import {Paper, Table, TableCell, TableContainer, TableRow} from "@mui/material";
|
||||||
|
import React from "react";
|
||||||
|
import {IModifier} from "../types/IModifier";
|
||||||
|
import {IBuildingShort} from "../types/IBuildingShort";
|
||||||
|
import {IBuilding, IBuildingAddonShort} from "../types/IBuilding";
|
||||||
|
import {IconUrl} from "../core/api";
|
||||||
|
import {IResearchRequirements} from "../types/IResearchRequirements";
|
||||||
|
import {goToAddon} from "../pages/BuildingPage";
|
||||||
|
import {IRequirement} from "../types/IRequirement";
|
||||||
|
|
||||||
|
interface IRequiredProps{
|
||||||
|
requirement: IRequirement,
|
||||||
|
building: IBuilding,
|
||||||
|
}
|
||||||
|
|
||||||
|
function Required (props: IRequiredProps) {
|
||||||
|
|
||||||
|
function renderRequirementGlobalAddons(rgas: IBuildingAddonShort[], building: IBuilding, modId: number) {
|
||||||
|
|
||||||
|
return <div> {rgas.map(rga =>
|
||||||
|
|
||||||
|
<span> Global addon: <img style={{verticalAlign: "top", height: 25}}
|
||||||
|
src={getIcon(rga.icon)}/>
|
||||||
|
<a href={`/mod/${modId}/race/${building.race.id}/building/${rga.buildingId}#addon-${rga.id}`}>
|
||||||
|
{rga.name}
|
||||||
|
</a></span>
|
||||||
|
)}</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderRequirementBuilding(requirementBuildings: IBuildingShort[], building: IBuilding, modId: number) {
|
||||||
|
return requirementBuildings.length > 0 && (
|
||||||
|
<div> Buildings: <img style={{verticalAlign: "top", height: 25}}
|
||||||
|
src={IconUrl + requirementBuildings[0].icon.replaceAll('\\', '/')}/>
|
||||||
|
{" "}<a href={`/mod/${modId}/race/${building.race.id}/building/${requirementBuildings[0].id}`}>
|
||||||
|
{requirementBuildings[0].name}
|
||||||
|
</a> {requirementBuildings[1] !== undefined && <span>or <img style={{verticalAlign: "top", height: 25}}
|
||||||
|
src={IconUrl + requirementBuildings[1].icon.replaceAll('\\', '/')}/>
|
||||||
|
{" "}<a href={`/mod/${modId}/race/${building.race.id}/building/${requirementBuildings[1].id}`}>
|
||||||
|
{requirementBuildings[1].name}</a></span> } </div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderRequirementResearches(requirementResearches: IResearchRequirements[], modId: number, raceId: string) {
|
||||||
|
return <div> {requirementResearches.map(rs =>
|
||||||
|
|
||||||
|
<span> Research {rs.researchMustNotBeComplete ? "not" : ""}: <img style={{verticalAlign: "top", height: 25}}
|
||||||
|
src={getIcon(rs.researchShortDto.icon)}/>
|
||||||
|
<a href={`/mod/${modId}/race/${raceId}/building/${rs.researchShortDto.buildingId}#research-${rs.researchShortDto.id}`}>
|
||||||
|
{rs.researchShortDto.name}
|
||||||
|
</a></span>
|
||||||
|
)}</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
const requirement = props.requirement
|
||||||
|
const building = props.building
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h4>Required: </h4>
|
||||||
|
{requirement.requiredTotalPop !== undefined && requirement.requiredTotalPop !== null &&
|
||||||
|
<div> Population: <img style={{verticalAlign: "top", height: 25}}
|
||||||
|
src="/images/Resource_orksquadcap.gif"/>
|
||||||
|
{requirement.requiredTotalPop}</div>
|
||||||
|
}
|
||||||
|
{requirement.requireAddon !== null &&
|
||||||
|
<div> Addon: <img style={{verticalAlign: "top", height: 25}}
|
||||||
|
src={IconUrl + requirement.requireAddon.icon.replaceAll('\\', '/')}/>
|
||||||
|
<a href={"#addon-" + requirement.requireAddon.id} onClick={() => goToAddon(`#addon-` + requirement.requireAddon.id)}>{requirement.requireAddon.name}</a>
|
||||||
|
{requirement.replaceWhenDone && <i> (old provides replace)</i>}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
{requirement.requirementBuildings.map(b =>
|
||||||
|
<div> Building: <img style={{verticalAlign: "top", height: 25}}
|
||||||
|
src={IconUrl + b.icon.replaceAll('\\', '/')}/>
|
||||||
|
<a href= {'/mod/'+ building.modId +'/race/'+ building.race.id +'/building/' + b.id + "/"}>{b.name}</a></div>)
|
||||||
|
}
|
||||||
|
{requirement.requirementResearches.length !== 0 &&
|
||||||
|
renderRequirementResearches(requirement.requirementResearches, building.modId, building.race.id)}
|
||||||
|
{requirement.requirementBuildingsEither.length !== 0 &&
|
||||||
|
renderRequirementBuilding(requirement.requirementBuildingsEither, building, building.modId)}
|
||||||
|
{requirement.requirementsGlobalAddons.length !== 0 &&
|
||||||
|
renderRequirementGlobalAddons(requirement.requirementsGlobalAddons, building, building.modId)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getIcon(icon?: string): string{
|
||||||
|
if(icon !== null && icon !== undefined){
|
||||||
|
return IconUrl + icon.replaceAll('\\', '/')
|
||||||
|
} else return '/images/shakal.png'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export default Required;
|
||||||
@ -18,12 +18,15 @@ import {ExpandMore} from "@mui/icons-material";
|
|||||||
import WeaponSlot from "./WeaponSlot";
|
import WeaponSlot from "./WeaponSlot";
|
||||||
import Vision from "./Vision";
|
import Vision from "./Vision";
|
||||||
import {IMod} from "../types/Imod";
|
import {IMod} from "../types/Imod";
|
||||||
|
import {renderAffectedResearches} from "./building/Research";
|
||||||
|
import {Irace} from "../types/Irace";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export interface SergeantProps {
|
export interface SergeantProps {
|
||||||
sergeant: ISergeant,
|
sergeant: ISergeant,
|
||||||
mod: IMod
|
mod: IMod,
|
||||||
|
race: Irace,
|
||||||
}
|
}
|
||||||
|
|
||||||
const Sergeant = (props: SergeantProps) => {
|
const Sergeant = (props: SergeantProps) => {
|
||||||
@ -100,7 +103,9 @@ const Sergeant = (props: SergeantProps) => {
|
|||||||
>
|
>
|
||||||
<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 name={sergeant.armorType.name}/>
|
<ArmorType name={sergeant.armorType.name} compact={false}/>
|
||||||
|
{sergeant.armorType2 && <span style={{fontSize: 12}} ><br/><i>after upgrade can become:<br/><ArmorType
|
||||||
|
name={sergeant.armorType2.name} compact={false}/></i></span> }
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
<TableRow
|
<TableRow
|
||||||
@ -126,7 +131,7 @@ const Sergeant = (props: SergeantProps) => {
|
|||||||
<TableRow
|
<TableRow
|
||||||
sx={{'&:last-child td, &:last-child th': {border: 0}}}
|
sx={{'&:last-child td, &:last-child th': {border: 0}}}
|
||||||
>
|
>
|
||||||
<TableCell component="th" scope="row">Detect</TableCell>
|
<TableCell component="th" scope="row">Vision</TableCell>
|
||||||
<TableCell component="th" scope="row">
|
<TableCell component="th" scope="row">
|
||||||
<Vision sight={sergeant.sightRadius} detect={sergeant.detectRadius}/>
|
<Vision sight={sergeant.sightRadius} detect={sergeant.detectRadius}/>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
@ -144,7 +149,10 @@ const Sergeant = (props: SergeantProps) => {
|
|||||||
<Grid2 size={12}>
|
<Grid2 size={12}>
|
||||||
{[...mapWithUnitWeapons.keys()].sort(function (a, b) {
|
{[...mapWithUnitWeapons.keys()].sort(function (a, b) {
|
||||||
return a - b;
|
return a - b;
|
||||||
}).map(h => <WeaponSlot mod={props.mod} unitWeapons={mapWithUnitWeapons.get(h)} hardpoint={h}/>)}
|
}).map(h => <WeaponSlot race={props.race} mod={props.mod} unitWeapons={mapWithUnitWeapons.get(h)} hardpoint={h}/>)}
|
||||||
|
</Grid2>
|
||||||
|
<Grid2 size={12}>
|
||||||
|
{renderAffectedResearches(sergeant.affectedResearches, props.mod.id, props.race.id)}
|
||||||
</Grid2>
|
</Grid2>
|
||||||
</Grid2>
|
</Grid2>
|
||||||
<i className="rgdFrom">{sergeant.filename}</i>
|
<i className="rgdFrom">{sergeant.filename}</i>
|
||||||
|
|||||||
@ -1,54 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import Item from "./Item";
|
|
||||||
|
|
||||||
export class TimerExample extends React.Component {
|
|
||||||
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
// Don't call this.setState() here!
|
|
||||||
this.state = { elapsed: 10 };
|
|
||||||
this.tick = this.tick.bind(this);
|
|
||||||
console.log(this.props);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
|
|
||||||
// componentDidMount вызывается react'ом, когда компонент
|
|
||||||
// был отрисован на странице. Мы можем установить интервал здесь:
|
|
||||||
console.log(this.props);
|
|
||||||
|
|
||||||
this.timer = setInterval(this.tick, 50);
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount() {
|
|
||||||
|
|
||||||
// Этот метод вызывается сразу после того, как компонент удален
|
|
||||||
// со страницы и уничтожен. Мы можем удалить интервал здесь:
|
|
||||||
|
|
||||||
clearInterval(this.timer);
|
|
||||||
}
|
|
||||||
|
|
||||||
tick() {
|
|
||||||
|
|
||||||
// Эта функция вызывается каждые 50мс. Она обновляет
|
|
||||||
// счетчик затраченного времени. Вызов setState заставляет компонент перерисовываться
|
|
||||||
console.log(this.state);
|
|
||||||
|
|
||||||
this.setState({elapsed: new Date() - this.props.start});
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
|
|
||||||
|
|
||||||
let elapsed = Math.round(this.state.elapsed / 100);
|
|
||||||
|
|
||||||
// Это даст нам число с одной цифрой после запятой dot (xx.x):
|
|
||||||
let seconds = (elapsed / 10).toFixed(1);
|
|
||||||
|
|
||||||
// Хоть мы и возвращаем целый <p> элемент, react разумно обновит
|
|
||||||
// только измененные части, содержащие переменную seconds.
|
|
||||||
|
|
||||||
return <p>This example was started <b>{seconds} seconds</b> ago.</p>;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@ -21,11 +21,14 @@ import {IconUrl} from "../core/api";
|
|||||||
import {ExpandMore} from "@mui/icons-material";
|
import {ExpandMore} from "@mui/icons-material";
|
||||||
import Sergeant from "./Sergeant";
|
import Sergeant from "./Sergeant";
|
||||||
import {IMod} from "../types/Imod";
|
import {IMod} from "../types/Imod";
|
||||||
|
import {Irace} from "../types/Irace";
|
||||||
|
import {renderAffectedResearches} from "./building/Research";
|
||||||
|
|
||||||
interface IWeaponProps {
|
interface IWeaponProps {
|
||||||
weapon: IWeapon,
|
weapon: IWeapon,
|
||||||
isDefault: Boolean,
|
isDefault: Boolean,
|
||||||
mod: IMod,
|
mod: IMod,
|
||||||
|
race: Irace,
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IWeaponState {
|
interface IWeaponState {
|
||||||
@ -74,7 +77,7 @@ class Weapon extends React.Component<IWeaponProps, any> {
|
|||||||
const infMedPiercing = this.getPiercingK(ArmorTypeNames.InfantryMedium)
|
const infMedPiercing = this.getPiercingK(ArmorTypeNames.InfantryMedium)
|
||||||
const infHighPiercing = this.getPiercingK(ArmorTypeNames.InfantryHigh)
|
const infHighPiercing = this.getPiercingK(ArmorTypeNames.InfantryHigh)
|
||||||
const infHeavyMedPiercing = this.getPiercingK(ArmorTypeNames.InfantryHeavyMedium)
|
const infHeavyMedPiercing = this.getPiercingK(ArmorTypeNames.InfantryHeavyMedium)
|
||||||
const infHeavyHighPiercing = this.getPiercingK(ArmorTypeNames.InfantryHeavyMedium)
|
const infHeavyHighPiercing = this.getPiercingK(ArmorTypeNames.InfantryHeavyHigh)
|
||||||
const demonPiercing = this.getPiercingK(ArmorTypeNames.DemonMedium)
|
const demonPiercing = this.getPiercingK(ArmorTypeNames.DemonMedium)
|
||||||
const demonHighPiercing = this.getPiercingK(ArmorTypeNames.DemonHigh)
|
const demonHighPiercing = this.getPiercingK(ArmorTypeNames.DemonHigh)
|
||||||
const commanderPiercing = this.getPiercingK(ArmorTypeNames.Commander)
|
const commanderPiercing = this.getPiercingK(ArmorTypeNames.Commander)
|
||||||
@ -120,13 +123,16 @@ class Weapon extends React.Component<IWeaponProps, any> {
|
|||||||
|
|
||||||
const StyledTableCell = styled(TableCell)(({theme}) => ({
|
const StyledTableCell = styled(TableCell)(({theme}) => ({
|
||||||
[`&.${tableCellClasses.head}`]: {
|
[`&.${tableCellClasses.head}`]: {
|
||||||
backgroundColor: "rgb(234, 234, 234)",
|
backgroundColor: "rgb(244,244,244)",
|
||||||
color: theme.palette.common.white,
|
marginRight: 'auto',
|
||||||
|
marginLeft: 'auto',
|
||||||
paddingLeft: 10
|
paddingLeft: 10
|
||||||
},
|
},
|
||||||
[`&.${tableCellClasses.body}`]: {
|
[`&.${tableCellClasses.body}`]: {
|
||||||
fontSize: 13,
|
fontSize: 12,
|
||||||
paddingLeft: 10,
|
textAlign: 'center',
|
||||||
|
paddingRight: 18,
|
||||||
|
paddingLeft: 10
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@ -276,7 +282,7 @@ class Weapon extends React.Component<IWeaponProps, any> {
|
|||||||
</ToggleButtonGroup>
|
</ToggleButtonGroup>
|
||||||
<TableContainer>
|
<TableContainer>
|
||||||
{ this.props.mod !== undefined && this.props.mod.name.includes("Ultimate Apocalypse") ?
|
{ this.props.mod !== undefined && this.props.mod.name.includes("Ultimate Apocalypse") ?
|
||||||
<Table sx={{marginTop: "10px"}} size="small" aria-label="a dense table">
|
<Table sx={{marginTop: "10px", textAlign: "center"}} size="small" aria-label="a dense table">
|
||||||
<TableHead>
|
<TableHead>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<StyledTableCell><ArmorType
|
<StyledTableCell><ArmorType
|
||||||
@ -335,7 +341,10 @@ class Weapon extends React.Component<IWeaponProps, any> {
|
|||||||
<StyledTableCell><ArmorType
|
<StyledTableCell><ArmorType
|
||||||
name={ArmorTypeNames.BuildingSuper}/></StyledTableCell>
|
name={ArmorTypeNames.BuildingSuper}/></StyledTableCell>
|
||||||
<StyledTableCell><img style={{verticalAlign: "top"}}
|
<StyledTableCell><img style={{verticalAlign: "top"}}
|
||||||
src="/images/ARM_Morale.webp"/></StyledTableCell>
|
src="/images/ARM_Morale.webp"/>
|
||||||
|
<div style={{width: 20, fontSize: 12, height: 50}}>
|
||||||
|
<i>Morale</i></div>
|
||||||
|
</StyledTableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
</TableHead>
|
</TableHead>
|
||||||
<TableBody>
|
<TableBody>
|
||||||
@ -386,7 +395,10 @@ class Weapon extends React.Component<IWeaponProps, any> {
|
|||||||
<StyledTableCell><ArmorType
|
<StyledTableCell><ArmorType
|
||||||
name={ArmorTypeNames.BuildingHigh}/></StyledTableCell>
|
name={ArmorTypeNames.BuildingHigh}/></StyledTableCell>
|
||||||
<StyledTableCell><img style={{verticalAlign: "top"}}
|
<StyledTableCell><img style={{verticalAlign: "top"}}
|
||||||
src="/images/ARM_Morale.webp"/></StyledTableCell>
|
src="/images/ARM_Morale.webp"/>
|
||||||
|
<div style={{width: 20, fontSize: 12, height: 50}}>
|
||||||
|
<i>Morale</i></div>
|
||||||
|
</StyledTableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
</TableHead>
|
</TableHead>
|
||||||
<TableBody>
|
<TableBody>
|
||||||
@ -415,6 +427,9 @@ class Weapon extends React.Component<IWeaponProps, any> {
|
|||||||
</TableContainer>
|
</TableContainer>
|
||||||
<i className="rgdFrom">{weapon.filename}</i>
|
<i className="rgdFrom">{weapon.filename}</i>
|
||||||
</Grid2>
|
</Grid2>
|
||||||
|
<Grid2 size={12}>
|
||||||
|
{renderAffectedResearches(weapon.affectedResearches, this.props.mod.id, this.props.race.id)}
|
||||||
|
</Grid2>
|
||||||
</Grid2>
|
</Grid2>
|
||||||
</AccordionDetails>
|
</AccordionDetails>
|
||||||
</Accordion></div>
|
</Accordion></div>
|
||||||
|
|||||||
@ -2,11 +2,13 @@ import Weapon from "./Weapon";
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import {IWeapon} from "../types/IUnit";
|
import {IWeapon} from "../types/IUnit";
|
||||||
import {IMod} from "../types/Imod";
|
import {IMod} from "../types/Imod";
|
||||||
|
import {Irace} from "../types/Irace";
|
||||||
|
|
||||||
export interface WeaponSlotProps {
|
export interface WeaponSlotProps {
|
||||||
hardpoint: number,
|
hardpoint: number,
|
||||||
unitWeapons: Map<number, IWeapon> | undefined,
|
unitWeapons: Map<number, IWeapon> | undefined,
|
||||||
mod: IMod,
|
mod: IMod,
|
||||||
|
race: Irace,
|
||||||
showOnlyDefault?: boolean ,
|
showOnlyDefault?: boolean ,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -33,7 +35,7 @@ const WeaponSlot= (props: WeaponSlotProps) => {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<h3>{header}</h3>
|
<h3>{header}</h3>
|
||||||
<div style={{marginLeft: 20}}><Weapon mod={props.mod} isDefault={true} weapon={firstWeapon}/></div>
|
<div style={{marginLeft: 20}}><Weapon race={props.race} mod={props.mod} isDefault={true} weapon={firstWeapon}/></div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -43,7 +45,7 @@ const WeaponSlot= (props: WeaponSlotProps) => {
|
|||||||
<h3>{header}</h3>
|
<h3>{header}</h3>
|
||||||
{weaponsSorted.filter(wp => !wp[1].filename.includes("dummy")).map(wp =>
|
{weaponsSorted.filter(wp => !wp[1].filename.includes("dummy")).map(wp =>
|
||||||
|
|
||||||
<div style={{marginLeft: 20}}><Weapon mod={props.mod} isDefault={!haveDummy || wp[1].filename === firstWeapon.filename} weapon={wp[1]}/></div>
|
<div style={{marginLeft: 20}}><Weapon race={props.race} mod={props.mod} isDefault={!haveDummy || wp[1].filename === firstWeapon.filename} weapon={wp[1]}/></div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@ -11,120 +11,30 @@ import {
|
|||||||
TableContainer,
|
TableContainer,
|
||||||
TableRow
|
TableRow
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import {IAddonModifier, IBuilding, IBuildingAddon, IBuildingAddonShort} from "../../types/IBuilding";
|
import {IBuilding, IBuildingAddon} from "../../types/IBuilding";
|
||||||
import {WeaponHardpoint} from "../../types/IUnit";
|
import {WeaponHardpoint} from "../../types/IUnit";
|
||||||
import AvTimerOutlinedIcon from "@mui/icons-material/AvTimer";
|
import AvTimerOutlinedIcon from "@mui/icons-material/AvTimer";
|
||||||
import WeaponSlot from "../WeaponSlot";
|
import WeaponSlot from "../WeaponSlot";
|
||||||
import {ExpandMore} from "@mui/icons-material";
|
import {ExpandMore} from "@mui/icons-material";
|
||||||
import {IconUrl} from "../../core/api";
|
|
||||||
import {IBuildingShort} from "../../types/IBuildingShort";
|
|
||||||
import {goToAddon} from "../../pages/BuildingPage";
|
|
||||||
import {IMod} from "../../types/Imod";
|
import {IMod} from "../../types/Imod";
|
||||||
|
import {IModifier} from "../../types/IModifier";
|
||||||
|
import {getIcon, ModifiersProvidesAddonTable,} from "../ModifiersProvideTable";
|
||||||
|
import Required from "../Required";
|
||||||
|
import {Irace} from "../../types/Irace";
|
||||||
|
|
||||||
interface IAddonModifiersProvidesTable{
|
|
||||||
modifiers: IAddonModifier[],
|
|
||||||
}
|
|
||||||
|
|
||||||
function AddonModifiersProvidesTable (props: IAddonModifiersProvidesTable) {
|
|
||||||
|
|
||||||
function getModName(mt: String){
|
|
||||||
switch(mt) {
|
|
||||||
case 'modifiers\\armour_modifier.lua':
|
|
||||||
return 'Armor';
|
|
||||||
case 'modifiers\\health_maximum_modifier.lua':
|
|
||||||
return 'Health';
|
|
||||||
case 'modifiers\\keen_sight_radius_modifier.lua':
|
|
||||||
return 'Detect';
|
|
||||||
case 'modifiers\\sight_radius_modifier.lua':
|
|
||||||
return 'Sight';
|
|
||||||
case 'modifiers\\garrison_requisition_modifier.lua':
|
|
||||||
return 'Requisition';
|
|
||||||
case 'modifiers\\faith_max_modifier.lua':
|
|
||||||
return 'Faith max';
|
|
||||||
case 'modifiers\\holy_icon_cost_power.lua':
|
|
||||||
return <span>Next icon cost <img style={{verticalAlign: "top"}} src='/images/Resource_power.gif'/></span>;
|
|
||||||
case 'modifiers\\holy_icon_cost_requisition.lua':
|
|
||||||
return <span>Next icon cost <img style={{verticalAlign: "top"}} src='/images/Resource_requisition.gif'/></span>;
|
|
||||||
case 'modifiers\\enable_hardpoint_02.lua':
|
|
||||||
return 'Weapon slot 2';
|
|
||||||
case 'modifiers\\cost_requisition_modifier.lua':
|
|
||||||
return 'Requisition cost';
|
|
||||||
default:
|
|
||||||
return mt.replace("modifiers\\", "").replace("modifier.lua", "").replace(".lua", "").replaceAll("_", " ");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getModIcon(mt: String){
|
|
||||||
switch(mt) {
|
|
||||||
case 'modifiers\\armour_modifier.lua':
|
|
||||||
return <img style={{height: 20, verticalAlign: "top"}} src='/images/defence.png'/>;
|
|
||||||
case 'modifiers\\health_maximum_modifier.lua':
|
|
||||||
return <img style={{verticalAlign: "top"}} src='/images/Health_icon.webp'/>;
|
|
||||||
case 'modifiers\\keen_sight_radius_modifier.lua':
|
|
||||||
return <img style={{verticalAlign: "top"}} src='/images/DETECT_YES.webp'/>;
|
|
||||||
case 'modifiers\\sight_radius_modifier.lua':
|
|
||||||
return <img style={{verticalAlign: "top"}} src='/images/DETECT_NO.webp'/>;
|
|
||||||
case 'modifiers\\faith_max_modifier.lua':
|
|
||||||
return <img style={{verticalAlign: "top"}} src='/images/Resource_faith.gif'/>;
|
|
||||||
case 'modifiers\\garrison_requisition_modifier.lua':
|
|
||||||
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'/>;
|
|
||||||
default:
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getChangeDescription(ref: String, v: number) {
|
|
||||||
switch(ref) {
|
|
||||||
case 'type_modifierusagetype\\tp_mod_usage_addition.lua':
|
|
||||||
if(v < 0){
|
|
||||||
return v
|
|
||||||
}else return '+' + v;
|
|
||||||
case 'type_modifierusagetype\\tp_mod_usage_multiplication.lua':
|
|
||||||
return 'x' + v;
|
|
||||||
case 'type_modifierusagetype\\tp_mod_usage_percentage.lua':
|
|
||||||
return '%' + v;
|
|
||||||
case 'type_modifierusagetype\\tp_mod_usage_enable.lua':
|
|
||||||
if(v === 1){
|
|
||||||
return ' enable ';
|
|
||||||
} else {
|
|
||||||
return ' disable ';
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return ref;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let noWeaponMods = props.modifiers.filter(m => !m.reference.includes("default_weapon_modifier_hardpoint"))
|
|
||||||
|
|
||||||
return (
|
|
||||||
<TableContainer component={Paper}>
|
|
||||||
<Table size="small" aria-label="a dense table">
|
|
||||||
{noWeaponMods.map(m =>
|
|
||||||
<TableRow
|
|
||||||
sx={{'&:last-child td, &:last-child th': {border: 0}}}
|
|
||||||
>
|
|
||||||
<TableCell component="th" scope="row">{getModName(m.reference)}</TableCell>
|
|
||||||
<TableCell component="th" scope="row">{getModIcon(m.reference)} {getChangeDescription(m.usageType, m.value)}</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
)}
|
|
||||||
</Table>
|
|
||||||
</TableContainer>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IAddonModifiersProvidesWeapons{
|
interface IAddonModifiersProvidesWeapons{
|
||||||
modifiers: IAddonModifier[],
|
modifiers: IModifier[],
|
||||||
replacedAddonModifiers: IAddonModifier[],
|
replacedAddonModifiers: IModifier[],
|
||||||
weapons: WeaponHardpoint[],
|
weapons: WeaponHardpoint[],
|
||||||
mod: IMod,
|
mod: IMod,
|
||||||
|
race: Irace
|
||||||
}
|
}
|
||||||
|
|
||||||
function AddonModifiersProvidesWeapons (props: IAddonModifiersProvidesWeapons) {
|
function AddonModifiersProvidesWeapons (props: IAddonModifiersProvidesWeapons) {
|
||||||
|
|
||||||
|
|
||||||
function getWeaponByAddonMod(ar: IAddonModifier){
|
function getWeaponByAddonMod(ar: IModifier){
|
||||||
|
|
||||||
let hardpoint = Number(ar.reference.replace("modifiers\\default_weapon_modifier_hardpoint", "").replace(".lua", ""));
|
let hardpoint = Number(ar.reference.replace("modifiers\\default_weapon_modifier_hardpoint", "").replace(".lua", ""));
|
||||||
let value = ar.value;
|
let value = ar.value;
|
||||||
@ -133,7 +43,7 @@ function AddonModifiersProvidesWeapons (props: IAddonModifiersProvidesWeapons) {
|
|||||||
|
|
||||||
let weapon = props.weapons.find(w => w.hardpoint === hardpoint && w.hardpointOrder === 1 + value )?.weapon
|
let weapon = props.weapons.find(w => w.hardpoint === hardpoint && w.hardpointOrder === 1 + value )?.weapon
|
||||||
if (weapon != null){
|
if (weapon != null){
|
||||||
return <WeaponSlot mod={props.mod} unitWeapons={new Map().set(hardpoint, weapon)} hardpoint={hardpoint}/>;
|
return <WeaponSlot race={props.race} mod={props.mod} unitWeapons={new Map().set(hardpoint, weapon)} hardpoint={hardpoint}/>;
|
||||||
}else{
|
}else{
|
||||||
return "Can't find weapon, replaced by addon:" + value + " - "
|
return "Can't find weapon, replaced by addon:" + value + " - "
|
||||||
}
|
}
|
||||||
@ -162,43 +72,20 @@ function BuildingAddon(props: IBuildingAddonProps){
|
|||||||
const building = props.building
|
const building = props.building
|
||||||
|
|
||||||
|
|
||||||
const prevAddonModifiers: IAddonModifier[] = (function() {
|
const prevAddonModifiers: IModifier[] = (function() {
|
||||||
if (addon.addonRequirement && addon.addonRequirement.requireAddon !== null && !addon.addonRequirement.replaceWhenDone){
|
if (addon.addonRequirement && addon.addonRequirement.requireAddon !== null && !addon.addonRequirement.replaceWhenDone){
|
||||||
return building.addons.find(a => a.id === addon.addonRequirement.requireAddon.id)?.addonModifiers ?? [];
|
return building.addons.find(a => a.id === addon.addonRequirement.requireAddon.id)?.addonModifiers ?? [];
|
||||||
} else return [];
|
} else return [];
|
||||||
})()
|
})()
|
||||||
|
|
||||||
function renderRequirementBuilding(requirementBuildings: IBuildingShort[], building: IBuilding) {
|
|
||||||
return requirementBuildings.length > 0 && (
|
|
||||||
<div> Buildings: <img style={{verticalAlign: "top", height: 25}}
|
|
||||||
src={IconUrl + requirementBuildings[0].icon.replaceAll('\\', '/')}/>
|
|
||||||
{" "}<a href={`/mod/${props.mod.id}/race/${building.race.id}/building/${requirementBuildings[0].id}`}>
|
|
||||||
{requirementBuildings[0].name}
|
|
||||||
</a> {requirementBuildings[1] !== undefined && <span>or <img style={{verticalAlign: "top", height: 25}}
|
|
||||||
src={IconUrl + requirementBuildings[1].icon.replaceAll('\\', '/')}/>
|
|
||||||
{" "}<a href={`/mod/${props.mod.id}/race/${building.race.id}/building/${requirementBuildings[1].id}`}>
|
|
||||||
{requirementBuildings[1].name}</a></span> } </div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderRequirementGlobalAddons(rgas: IBuildingAddonShort[], building: IBuilding) {
|
return <div className='addon-research-accordion' id={"addon-" + addon.id} ><Accordion>
|
||||||
return <div> {rgas.map(rga =>
|
|
||||||
<span> Global addon: <img style={{verticalAlign: "top", height: 25}}
|
|
||||||
src={IconUrl + rga.icon.replaceAll('\\', '/')}/>
|
|
||||||
<a href={`/mod/${props.mod.id}/race/${building.race.id}/building/${rga.buildingId}#addon-${rga.id}`}>
|
|
||||||
{rga.name}
|
|
||||||
</a></span>
|
|
||||||
)}</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return <div className='addon-accordion' id={"addon-" + addon.id} ><Accordion>
|
|
||||||
<AccordionSummary
|
<AccordionSummary
|
||||||
expandIcon={<ExpandMore/>}
|
expandIcon={<ExpandMore/>}
|
||||||
aria-controls="panel1-content"
|
aria-controls="panel1-content"
|
||||||
>
|
>
|
||||||
<span style={{fontSize: 20}}>
|
<span style={{fontSize: 20}}>
|
||||||
{addon.icon && <img className="sergeantIcon" src={IconUrl + addon.icon.replaceAll('\\', '/')}/>}
|
<img className="sergeantIcon" src={getIcon(addon.icon)}/>
|
||||||
{addon.name}
|
{addon.name}
|
||||||
</span>
|
</span>
|
||||||
</AccordionSummary>
|
</AccordionSummary>
|
||||||
@ -242,7 +129,7 @@ function BuildingAddon(props: IBuildingAddonProps){
|
|||||||
</TableBody>
|
</TableBody>
|
||||||
</Table>
|
</Table>
|
||||||
</TableContainer><br/>
|
</TableContainer><br/>
|
||||||
<AddonModifiersProvidesTable modifiers={addon.addonModifiers}/>
|
<ModifiersProvidesAddonTable modifiers={addon.addonModifiers}/>
|
||||||
</Grid2>
|
</Grid2>
|
||||||
<Grid2 size={{xs: 12, md: 8}}>
|
<Grid2 size={{xs: 12, md: 8}}>
|
||||||
<div style={{whiteSpace: "pre-wrap"}}>
|
<div style={{whiteSpace: "pre-wrap"}}>
|
||||||
@ -251,32 +138,11 @@ function BuildingAddon(props: IBuildingAddonProps){
|
|||||||
</Grid2>
|
</Grid2>
|
||||||
{addon.addonModifiers !== null &&
|
{addon.addonModifiers !== null &&
|
||||||
<Grid2 size={{xs: 12, md: 12}}>
|
<Grid2 size={{xs: 12, md: 12}}>
|
||||||
<AddonModifiersProvidesWeapons mod={props.mod} modifiers={addon.addonModifiers} replacedAddonModifiers={prevAddonModifiers} weapons={building.weapons}/>
|
<AddonModifiersProvidesWeapons race={building.race} mod={props.mod} modifiers={addon.addonModifiers} replacedAddonModifiers={prevAddonModifiers} weapons={building.weapons}/>
|
||||||
</Grid2>}
|
</Grid2>}
|
||||||
{addon.addonRequirement !== null &&
|
{addon.addonRequirement !== null &&
|
||||||
<Grid2 size={{xs: 12, md: 12}}>
|
<Grid2 size={{xs: 12, md: 12}}>
|
||||||
<h4>Required: </h4>
|
<Required requirement={addon.addonRequirement} building={building}/>
|
||||||
{addon.addonRequirement.requiredTotalPop !== undefined && addon.addonRequirement.requiredTotalPop !== null &&
|
|
||||||
<div> Population: <img style={{verticalAlign: "top", height: 25}}
|
|
||||||
src="/images/Resource_orksquadcap.gif"/>
|
|
||||||
{addon.addonRequirement.requiredTotalPop}</div>
|
|
||||||
}
|
|
||||||
{addon.addonRequirement.requireAddon !== null &&
|
|
||||||
<div> Addon: <img style={{verticalAlign: "top", height: 25}}
|
|
||||||
src={IconUrl + addon.addonRequirement.requireAddon.icon.replaceAll('\\', '/')}/>
|
|
||||||
<a href={"#addon-" + addon.addonRequirement.requireAddon.id} onClick={() => goToAddon(`#addon-` + addon.addonRequirement.requireAddon.id)}>{addon.addonRequirement.requireAddon.name}</a>
|
|
||||||
{addon.addonRequirement.replaceWhenDone && <i> (old provides replace)</i>}
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
{addon.addonRequirement.requirementBuildings.map(b =>
|
|
||||||
<div> Building: <img style={{verticalAlign: "top", height: 25}}
|
|
||||||
src={IconUrl + b.icon.replaceAll('\\', '/')}/>
|
|
||||||
<a href= {'/mod/'+ props.mod.id +'/race/'+ building.race.id +'/building/' + b.id + "/"}>{b.name}</a></div>)
|
|
||||||
}
|
|
||||||
{addon.addonRequirement.requirementBuildingsEither.length !== 0 &&
|
|
||||||
renderRequirementBuilding(addon.addonRequirement.requirementBuildingsEither, building)}
|
|
||||||
{addon.addonRequirement.requirementsGlobalAddons.length !== 0 &&
|
|
||||||
renderRequirementGlobalAddons(addon.addonRequirement.requirementsGlobalAddons, building)}
|
|
||||||
</Grid2>}
|
</Grid2>}
|
||||||
</Grid2>
|
</Grid2>
|
||||||
<i className="rgdFrom">{addon.filename}</i>
|
<i className="rgdFrom">{addon.filename}</i>
|
||||||
|
|||||||
124
src/classes/building/Research.tsx
Normal file
124
src/classes/building/Research.tsx
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
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, ModifiersProvidesResearchTable} from "../ModifiersProvideTable";
|
||||||
|
import AvTimerOutlinedIcon from "@mui/icons-material/AvTimer";
|
||||||
|
import Required from "../Required";
|
||||||
|
import React from "react";
|
||||||
|
import {IBuilding} from "../../types/IBuilding";
|
||||||
|
import {IResearchShort} from "../../types/IResearchShort";
|
||||||
|
|
||||||
|
|
||||||
|
interface IResearchProps {
|
||||||
|
research: IResearch,
|
||||||
|
building: IBuilding,
|
||||||
|
mod: IMod,
|
||||||
|
}
|
||||||
|
|
||||||
|
function ResearchFull(props: IResearchProps){
|
||||||
|
|
||||||
|
const research = props.research
|
||||||
|
const building = props.building
|
||||||
|
|
||||||
|
return <div className='addon-research-accordion' id={"research-" + research.id}><Accordion>
|
||||||
|
<AccordionSummary
|
||||||
|
expandIcon={<ExpandMore/>}
|
||||||
|
aria-controls="panel1-content"
|
||||||
|
>
|
||||||
|
<span style={{fontSize: 20}}>
|
||||||
|
<img className="sergeantIcon" src={getIcon(research.icon)}/>
|
||||||
|
{research.name}
|
||||||
|
</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> <img style={{verticalAlign: "top"}}
|
||||||
|
src="/images/Resource_requisition.gif"/>
|
||||||
|
{research.costRequisition.toFixed(0)}</span>}
|
||||||
|
{research.costPower > 0 && <span> <img style={{verticalAlign: "top"}}
|
||||||
|
src="/images/Resource_power.gif"/>
|
||||||
|
{research.costPower.toFixed(0)}</span>}
|
||||||
|
{(research.costPopulation !== undefined && research.costPopulation > 0) &&
|
||||||
|
<span> <img style={{verticalAlign: "top"}}
|
||||||
|
src="/images/Resource_orksquadcap.gif"/>
|
||||||
|
{research.costPopulation.toFixed(0)}</span>}
|
||||||
|
{(research.costFaith !== undefined && research.costFaith > 0) &&
|
||||||
|
<span> <img style={{verticalAlign: "top"}}
|
||||||
|
src="/images/Resource_faith.gif"/>
|
||||||
|
{research.costFaith}</span>}
|
||||||
|
{(research.costSouls !== undefined && research.costSouls > 0) &&
|
||||||
|
<span> <img style={{verticalAlign: "top"}}
|
||||||
|
src="/images/Resource_souls.gif"/>
|
||||||
|
{research.costSouls.toFixed(0)}</span>}
|
||||||
|
{(research.costTime !== undefined && research.costTime > 0) &&
|
||||||
|
<span> <AvTimerOutlinedIcon
|
||||||
|
style={{verticalAlign: "top", fontSize: "18px"}}/>
|
||||||
|
{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}}>
|
||||||
|
<ModifiersProvidesResearchTable modifiers={research.modifiers} research={research} race={building.race}/>
|
||||||
|
</Grid2>
|
||||||
|
{research.requirements !== null &&
|
||||||
|
<Grid2 size={{xs: 12, md: 12}}>
|
||||||
|
<Required requirement={research.requirements} building={building}/>
|
||||||
|
</Grid2>}
|
||||||
|
</Grid2>
|
||||||
|
<i className="rgdFrom">{research.filename}</i>
|
||||||
|
</AccordionDetails>
|
||||||
|
</Accordion></div>
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export function renderAffectedResearches(researches: IResearchShort[], modId: number, raceId: string) {
|
||||||
|
|
||||||
|
function researchLink(rs: IResearchShort) {
|
||||||
|
return <span><img style={{verticalAlign: "top", height: 25}}
|
||||||
|
src={getIcon(rs.icon)}/>
|
||||||
|
<a href={`/mod/${modId}/race/${raceId}/building/${rs.buildingId}#research-${rs.id}`}>
|
||||||
|
{rs.name}
|
||||||
|
</a></span>
|
||||||
|
}
|
||||||
|
|
||||||
|
return <div> {researches.map(rs =>
|
||||||
|
rs.buildingId == null ? <span></span> :
|
||||||
|
<span>Research affect: {researchLink(rs)}<br/></span>
|
||||||
|
)}</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export default ResearchFull;
|
||||||
|
|
||||||
@ -1,4 +1,4 @@
|
|||||||
.addon-accordion{
|
.addon-research-accordion{
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
scroll-margin-top: 15px;
|
scroll-margin-top: 15px;
|
||||||
}
|
}
|
||||||
@ -6,5 +6,5 @@
|
|||||||
|
|
||||||
|
|
||||||
.selected-addon div {
|
.selected-addon div {
|
||||||
background-color: antiquewhite;
|
background-color: #fafaf2;
|
||||||
}
|
}
|
||||||
@ -3,7 +3,18 @@ import React from "react";
|
|||||||
import {withRouter} from "../core/withrouter";
|
import {withRouter} from "../core/withrouter";
|
||||||
import {IWeapon} from "../types/IUnit";
|
import {IWeapon} from "../types/IUnit";
|
||||||
import '../css/Building.css'
|
import '../css/Building.css'
|
||||||
import {Button, Grid2, Paper, Table, TableBody, TableCell, TableContainer, TableRow} from "@mui/material";
|
import {
|
||||||
|
Button,
|
||||||
|
Grid2,
|
||||||
|
Link,
|
||||||
|
ListItem,
|
||||||
|
Paper,
|
||||||
|
Table,
|
||||||
|
TableBody,
|
||||||
|
TableCell,
|
||||||
|
TableContainer,
|
||||||
|
TableRow
|
||||||
|
} from "@mui/material";
|
||||||
import {ArrowBack} from "@mui/icons-material";
|
import {ArrowBack} from "@mui/icons-material";
|
||||||
import ArmorType from "../classes/ArmorType";
|
import ArmorType from "../classes/ArmorType";
|
||||||
import AvTimerOutlinedIcon from '@mui/icons-material/AvTimer';
|
import AvTimerOutlinedIcon from '@mui/icons-material/AvTimer';
|
||||||
@ -13,12 +24,25 @@ import {IMod} from "../types/Imod";
|
|||||||
import {IBuilding} from "../types/IBuilding";
|
import {IBuilding} from "../types/IBuilding";
|
||||||
import Vision from "../classes/Vision";
|
import Vision from "../classes/Vision";
|
||||||
import BuildingAddon from "../classes/building/BuildingAddon";
|
import BuildingAddon from "../classes/building/BuildingAddon";
|
||||||
|
import {IUnitShort} from "../types/IUnitShort";
|
||||||
|
import Research, {renderAffectedResearches} from "../classes/building/Research";
|
||||||
|
|
||||||
interface UintPageState {
|
interface UintPageState {
|
||||||
building: IBuilding,
|
building: IBuilding,
|
||||||
mod: IMod,
|
mod: IMod,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function Unit (unit: IUnitShort, modId: number, raceId: String) {
|
||||||
|
|
||||||
|
return (<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> <img style={{verticalAlign: "top"}}
|
||||||
|
src="/images/DETECT_YES.webp"/></span>}
|
||||||
|
|
||||||
|
</ListItem></Link>)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function Building(building: IBuilding, mod: IMod) {
|
function Building(building: IBuilding, mod: IMod) {
|
||||||
|
|
||||||
@ -36,13 +60,16 @@ function Building(building: IBuilding, mod: IMod) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
var buildingName = building.name;
|
||||||
|
if(building.name == null){
|
||||||
|
buildingName = building.filename.replaceAll('_', ' ').replace('.rgd', '');
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<h1>{mod.name} ({mod.version})</h1>
|
<h1>{mod.name} ({mod.version})</h1>
|
||||||
<h2>{building.icon &&
|
<h2>{building.icon &&
|
||||||
<img className="unitIcon" src={IconUrl + building.icon.replaceAll('\\', '/')}/>} {building.name} </h2>
|
<img className="unitIcon" src={IconUrl + building.icon.replaceAll('\\', '/')}/>} {buildingName} </h2>
|
||||||
|
|
||||||
<Grid2 container spacing={2}>
|
<Grid2 container spacing={2}>
|
||||||
<Grid2 size={{xs: 12, md: 4}}>
|
<Grid2 size={{xs: 12, md: 4}}>
|
||||||
@ -103,7 +130,8 @@ function Building(building: IBuilding, mod: IMod) {
|
|||||||
>
|
>
|
||||||
<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 name={building.armorType.name}/>
|
<ArmorType name={building.armorType.name} compact={false}/>
|
||||||
|
{building.armorType2 && <span><br/><ArmorType name={building.armorType2.name} compact={false}/></span> }
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
<TableRow
|
<TableRow
|
||||||
@ -142,16 +170,31 @@ function Building(building: IBuilding, mod: IMod) {
|
|||||||
{building.description}
|
{building.description}
|
||||||
</div>
|
</div>
|
||||||
</Grid2>
|
</Grid2>
|
||||||
|
{building.units.length > 0 && <Grid2 size={12}>
|
||||||
|
<h3>Unit production</h3>
|
||||||
|
{building.units.map(unit =>
|
||||||
|
Unit(unit, mod.id, building.race.id)
|
||||||
|
)}
|
||||||
|
</Grid2>}
|
||||||
{building.addons.length > 0 && <Grid2 size={12}>
|
{building.addons.length > 0 && <Grid2 size={12}>
|
||||||
<h3>Addons</h3>
|
<h3>Addons</h3>
|
||||||
{building.addons.map(b =>
|
{building.addons.map(b =>
|
||||||
<BuildingAddon mod={mod} addon={b} building={building}/>
|
<BuildingAddon mod={mod} addon={b} building={building}/>
|
||||||
)}
|
)}
|
||||||
</Grid2> }
|
</Grid2> }
|
||||||
|
{building.researches.length > 0 && <Grid2 size={12}>
|
||||||
|
<h3>Researches</h3>
|
||||||
|
{building.researches.map(r =>
|
||||||
|
<Research mod={mod} research={r} building={building}/>
|
||||||
|
)}
|
||||||
|
</Grid2> }
|
||||||
<Grid2 size={12}>
|
<Grid2 size={12}>
|
||||||
{[...mapBuildingWeapons.keys()].sort(function (a, b) {
|
{[...mapBuildingWeapons.keys()].sort(function (a, b) {
|
||||||
return a - b;
|
return a - b;
|
||||||
}).map(h => <WeaponSlot mod={mod} showOnlyDefault={true} unitWeapons={mapBuildingWeapons.get(h)} hardpoint={h}/>)}
|
}).map(h => <WeaponSlot race={building.race} mod={mod} showOnlyDefault={true} unitWeapons={mapBuildingWeapons.get(h)} hardpoint={h}/>)}
|
||||||
|
</Grid2>
|
||||||
|
<Grid2 size={12}>
|
||||||
|
{renderAffectedResearches(building.affectedResearches, mod.id, building.race.id)}
|
||||||
</Grid2>
|
</Grid2>
|
||||||
</Grid2>
|
</Grid2>
|
||||||
<i className="rgdFrom">{building.filename}</i>
|
<i className="rgdFrom">{building.filename}</i>
|
||||||
@ -162,7 +205,6 @@ function Building(building: IBuilding, mod: IMod) {
|
|||||||
|
|
||||||
function goToAddon(addonHash: string) {
|
function goToAddon(addonHash: string) {
|
||||||
let addonId = addonHash.replace("#", "");
|
let addonId = addonHash.replace("#", "");
|
||||||
console.log(addonId);
|
|
||||||
[].forEach.call(document.querySelectorAll('div'), function (el: HTMLElement) {
|
[].forEach.call(document.querySelectorAll('div'), function (el: HTMLElement) {
|
||||||
el?.classList?.remove('selected-addon');
|
el?.classList?.remove('selected-addon');
|
||||||
});
|
});
|
||||||
@ -170,6 +212,15 @@ function goToAddon(addonHash: string) {
|
|||||||
document?.getElementById(addonId)?.scrollIntoView()
|
document?.getElementById(addonId)?.scrollIntoView()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function goToResearch(researchHash: string) {
|
||||||
|
let researchId = researchHash.replace("#", "");
|
||||||
|
[].forEach.call(document.querySelectorAll('div'), function (el: HTMLElement) {
|
||||||
|
el?.classList?.remove('selected-research');
|
||||||
|
});
|
||||||
|
document?.getElementById(researchId)?.classList?.add("selected-research");
|
||||||
|
document?.getElementById(researchId)?.scrollIntoView()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class BuildingPage extends React.Component<any, UintPageState> {
|
class BuildingPage extends React.Component<any, UintPageState> {
|
||||||
|
|
||||||
@ -188,6 +239,7 @@ class BuildingPage extends React.Component<any, UintPageState> {
|
|||||||
mod: modData
|
mod: modData
|
||||||
});
|
});
|
||||||
goToAddon(window.location.hash);
|
goToAddon(window.location.hash);
|
||||||
|
goToResearch(window.location.hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@ -207,4 +259,4 @@ class BuildingPage extends React.Component<any, UintPageState> {
|
|||||||
|
|
||||||
export default withRouter(BuildingPage);
|
export default withRouter(BuildingPage);
|
||||||
|
|
||||||
export { goToAddon };
|
export { goToAddon, goToResearch };
|
||||||
@ -21,6 +21,8 @@ function Mods (mods: IMod[]) {
|
|||||||
function Mod (modName: String) {
|
function Mod (modName: String) {
|
||||||
let sameMods = mapWithModVersions.get(modName) ?? []
|
let sameMods = mapWithModVersions.get(modName) ?? []
|
||||||
|
|
||||||
|
let lastBeta = sameMods.filter((m) => m.isBeta).reverse()[0]
|
||||||
|
|
||||||
return(
|
return(
|
||||||
<div>
|
<div>
|
||||||
<h1>{modName}</h1>
|
<h1>{modName}</h1>
|
||||||
@ -28,9 +30,9 @@ function Mods (mods: IMod[]) {
|
|||||||
{sameMods.filter((m) => !m.isBeta).map(mod =>
|
{sameMods.filter((m) => !m.isBeta).map(mod =>
|
||||||
<ul><NavLink state={mod.id} to= {"/mod/" + mod.id} >{mod.version}</NavLink></ul>)}
|
<ul><NavLink state={mod.id} to= {"/mod/" + mod.id} >{mod.version}</NavLink></ul>)}
|
||||||
{sameMods.find((m) => m.isBeta) != null && <div><i>Beta versions:</i></div>}
|
{sameMods.find((m) => m.isBeta) != null && <div><i>Beta versions:</i></div>}
|
||||||
{sameMods.find((m) => m.isBeta) != null &&
|
{lastBeta != null &&
|
||||||
sameMods.filter((m) => m.isBeta).map(mod =>
|
<ul><NavLink state={lastBeta.id} to={"/mod/" + lastBeta.id}>{lastBeta.version}</NavLink></ul>
|
||||||
<ul><NavLink state={mod.id} to= {"/mod/" + mod.id} >{mod.version}</NavLink></ul>)}
|
}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -62,7 +62,7 @@ function UnitSmall (unit: IUnitShort, modId: number, raceId: String) {
|
|||||||
return (<Link href={"/mod/" + modId + "/race/" + raceId + "/unit/" + unit.id}>
|
return (<Link href={"/mod/" + modId + "/race/" + raceId + "/unit/" + unit.id}>
|
||||||
{unit.icon && <img className="unitIconSmall" src={IconUrl + unit.icon.replaceAll('\\', '/')}/>}
|
{unit.icon && <img className="unitIconSmall" src={IconUrl + unit.icon.replaceAll('\\', '/')}/>}
|
||||||
<span style={{fontSize: 12}}>{unitName}</span>
|
<span style={{fontSize: 12}}>{unitName}</span>
|
||||||
{unit.canDetect && <span> <img style={{verticalAlign: "top"}}
|
{unit.canDetect && <span> <img
|
||||||
src="/images/DETECT_YES.webp"/></span>}<br/></Link>)
|
src="/images/DETECT_YES.webp"/></span>}<br/></Link>)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -140,21 +140,23 @@ class Units extends React.Component<UnitsProps, UnitsState> {
|
|||||||
<h3>All units</h3>
|
<h3>All units</h3>
|
||||||
</AccordionSummary>
|
</AccordionSummary>
|
||||||
<AccordionDetails>
|
<AccordionDetails>
|
||||||
<Grid2 size= {{xs: 12, md: 4}}>
|
<Grid2 container spacing={2}>
|
||||||
<h3>Infantry</h3>
|
<Grid2 size= {{xs: 12, md: 4}}>
|
||||||
{this.state.units.infantry.map(unit => Unit(unit, this.props.modId, this.props.raceId))}
|
<h3>Infantry</h3>
|
||||||
</Grid2>
|
{this.state.units.infantry.map(unit => Unit(unit, this.props.modId, this.props.raceId))}
|
||||||
<Grid2 size={{xs: 12, md: 4}}>
|
</Grid2>
|
||||||
<h3>Tech</h3>
|
<Grid2 size={{xs: 12, md: 4}}>
|
||||||
<List sx={{width: '100%', maxWidth: 360, bgcolor: 'background.paper'}}>
|
<h3>Tech</h3>
|
||||||
{this.state.units.tech.map(unit => Unit(unit, this.props.modId, this.props.raceId))}
|
<List sx={{width: '100%', maxWidth: 360, bgcolor: 'background.paper'}}>
|
||||||
</List>
|
{this.state.units.tech.map(unit => Unit(unit, this.props.modId, this.props.raceId))}
|
||||||
</Grid2>
|
</List>
|
||||||
<Grid2 size={{xs: 12, md: 4}}>
|
</Grid2>
|
||||||
<h3>Support</h3>
|
<Grid2 size={{xs: 12, md: 4}}>
|
||||||
<List sx={{width: '100%', maxWidth: 360, bgcolor: 'background.paper'}}>
|
<h3>Support</h3>
|
||||||
{this.state.units.support.map(unit => Unit(unit, this.props.modId, this.props.raceId))}
|
<List sx={{width: '100%', maxWidth: 360, bgcolor: 'background.paper'}}>
|
||||||
</List>
|
{this.state.units.support.map(unit => Unit(unit, this.props.modId, this.props.raceId))}
|
||||||
|
</List>
|
||||||
|
</Grid2>
|
||||||
</Grid2>
|
</Grid2>
|
||||||
</AccordionDetails>
|
</AccordionDetails>
|
||||||
</Accordion>
|
</Accordion>
|
||||||
|
|||||||
@ -24,6 +24,10 @@ import WeaponSlot from "../classes/WeaponSlot";
|
|||||||
import UnitsTable from "../classes/UnitsTable";
|
import UnitsTable from "../classes/UnitsTable";
|
||||||
import {IMod} from "../types/Imod";
|
import {IMod} from "../types/Imod";
|
||||||
import Vision from "../classes/Vision";
|
import Vision from "../classes/Vision";
|
||||||
|
import {IResearchRequirements} from "../types/IResearchRequirements";
|
||||||
|
import {getIcon} from "../classes/Required";
|
||||||
|
import {IResearchShort} from "../types/IResearchShort";
|
||||||
|
import {renderAffectedResearches} from "../classes/building/Research";
|
||||||
|
|
||||||
interface UintPageState {
|
interface UintPageState {
|
||||||
unit: IUnit,
|
unit: IUnit,
|
||||||
@ -188,7 +192,9 @@ function Unit(unit: IUnit, mod: IMod) {
|
|||||||
>
|
>
|
||||||
<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 name={unit.armorType.name}/>
|
<ArmorType name={unit.armorType.name} compact={false}/>
|
||||||
|
{unit.armorType2 && <span style={{fontSize: 12}} ><br/><i>after upgrade can become:<br/><ArmorType
|
||||||
|
name={unit.armorType2.name} compact={false}/></i></span> }
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
<TableRow
|
<TableRow
|
||||||
@ -200,7 +206,7 @@ function Unit(unit: IUnit, mod: IMod) {
|
|||||||
src="/images/Health_icon.webp"/>
|
src="/images/Health_icon.webp"/>
|
||||||
{unit.health} {unit.healthRegeneration > 0 &&
|
{unit.health} {unit.healthRegeneration > 0 &&
|
||||||
<span>+{unit.healthRegeneration}/s</span>}
|
<span>+{unit.healthRegeneration}/s</span>}
|
||||||
{unit.armour !== undefined && unit.armour > 0 && <span><img style={{height: 20, verticalAlign: "top"}} src="/images/defence.png"/>{unit.armour} </span> }
|
{unit.armour !== undefined && unit.armour !== 0 && <span><img style={{height: 20, verticalAlign: "top"}} src="/images/defence.png"/>{unit.armour} </span> }
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
<TableRow
|
<TableRow
|
||||||
@ -243,11 +249,11 @@ function Unit(unit: IUnit, mod: IMod) {
|
|||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
}
|
}
|
||||||
{unit.repairSpeed !== undefined && unit.repairSpeed !== null &&
|
{unit.repairSpeed !== undefined && unit.repairSpeed !== null && unit.repairCostPercent !== null &&
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableCell>Repair speed</TableCell>
|
<TableCell>Repair</TableCell>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
{unit.repairSpeed}
|
{unit.repairSpeed} hp/s; {unit.repairCostPercent}% cost
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
}
|
}
|
||||||
@ -271,7 +277,7 @@ function Unit(unit: IUnit, mod: IMod) {
|
|||||||
<SergeantShort name={s.name} icon={s.icon} canDetect={s.detectRadius > 0}/>
|
<SergeantShort name={s.name} icon={s.icon} canDetect={s.detectRadius > 0}/>
|
||||||
</AccordionSummary>
|
</AccordionSummary>
|
||||||
<AccordionDetails>
|
<AccordionDetails>
|
||||||
<Sergeant mod={mod} sergeant={s}/>
|
<Sergeant mod={mod} sergeant={s} race={unit.race}/>
|
||||||
<hr/>
|
<hr/>
|
||||||
</AccordionDetails>
|
</AccordionDetails>
|
||||||
|
|
||||||
@ -280,7 +286,10 @@ function Unit(unit: IUnit, mod: IMod) {
|
|||||||
<Grid2 size={12}>
|
<Grid2 size={12}>
|
||||||
{[...mapWithUnitWeapons.keys()].sort(function (a, b) {
|
{[...mapWithUnitWeapons.keys()].sort(function (a, b) {
|
||||||
return a - b;
|
return a - b;
|
||||||
}).map(h => <WeaponSlot mod={mod} unitWeapons={mapWithUnitWeapons.get(h)} hardpoint={h}/>)}
|
}).map(h => <WeaponSlot race={unit.race} mod={mod} unitWeapons={mapWithUnitWeapons.get(h)} hardpoint={h}/>)}
|
||||||
|
</Grid2>
|
||||||
|
<Grid2 size={12}>
|
||||||
|
{renderAffectedResearches(unit.affectedResearches, mod.id, unit.race.id)}
|
||||||
</Grid2>
|
</Grid2>
|
||||||
</Grid2>
|
</Grid2>
|
||||||
<i className="rgdFrom">{unit.filename}</i>
|
<i className="rgdFrom">{unit.filename}</i>
|
||||||
@ -290,6 +299,8 @@ function Unit(unit: IUnit, mod: IMod) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class UnitPage extends React.Component<any, UintPageState> {
|
class UnitPage extends React.Component<any, UintPageState> {
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -2,15 +2,15 @@ enum ArmorTypeValues {
|
|||||||
InfantryLow = 'Infantry Low',
|
InfantryLow = 'Infantry Low',
|
||||||
InfantryMedium = 'Infantry Medium',
|
InfantryMedium = 'Infantry Medium',
|
||||||
InfantryHigh = 'Infantry High',
|
InfantryHigh = 'Infantry High',
|
||||||
InfantryHeavyMedium = 'Infantry Heavy Medium',
|
InfantryHeavyMedium = 'Infantry H.Med.',
|
||||||
InfantryHeavyHigh = 'Infantry Heavy High',
|
InfantryHeavyHigh = 'Infantry H.High',
|
||||||
Commander = 'Commander',
|
Commander = 'Infantry Com.',
|
||||||
VehicleLow = 'Vehicle Low',
|
VehicleLow = 'Vehicle Low',
|
||||||
VehicleMedium = 'Vehicle Medium',
|
VehicleMedium = 'Vehicle Medium',
|
||||||
VehicleHigh = 'Vehicle High',
|
VehicleHigh = 'Vehicle High',
|
||||||
LivingMetal = 'Living Metal',
|
LivingMetal = 'Living Metal',
|
||||||
Titan = 'Titan',
|
Titan = 'Titan',
|
||||||
Air = 'Air',
|
Air = 'Vehicle Air',
|
||||||
BuildingLow = 'Building Low',
|
BuildingLow = 'Building Low',
|
||||||
BuildingMedium = 'Building Medium',
|
BuildingMedium = 'Building Medium',
|
||||||
BuildingHigh = 'Building High',
|
BuildingHigh = 'Building High',
|
||||||
|
|||||||
23
src/types/IAffectedEntity.tsx
Normal file
23
src/types/IAffectedEntity.tsx
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
export interface IAffectedEntity {
|
||||||
|
id: number
|
||||||
|
name: string
|
||||||
|
icon: string
|
||||||
|
filename: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ISergeantUnitShortDto {
|
||||||
|
id: number
|
||||||
|
name: string
|
||||||
|
icon: string
|
||||||
|
filename: string
|
||||||
|
unit: IAffectedEntity
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IWeaponUnitShortDto {
|
||||||
|
id: number
|
||||||
|
name: string
|
||||||
|
filename: string
|
||||||
|
units: IAffectedEntity[]
|
||||||
|
sergeants: ISergeantUnitShortDto[]
|
||||||
|
buildings: IAffectedEntity[]
|
||||||
|
}
|
||||||
@ -1,7 +1,11 @@
|
|||||||
import {Irace} from "./Irace";
|
import {Irace} from "./Irace";
|
||||||
import {IArmorType} from "./IArmorType";
|
import {IArmorType} from "./IArmorType";
|
||||||
import {WeaponHardpoint} from "./IUnit";
|
import {WeaponHardpoint} from "./IUnit";
|
||||||
import {IBuildingShort} from "./IBuildingShort";
|
import {IUnitShort} from "./IUnitShort";
|
||||||
|
import {IModifier} from "./IModifier";
|
||||||
|
import {IRequirement} from "./IRequirement";
|
||||||
|
import {IResearch} from "./IResearch";
|
||||||
|
import {IResearchShort} from "./IResearchShort";
|
||||||
|
|
||||||
export interface IBuilding {
|
export interface IBuilding {
|
||||||
id: number
|
id: number
|
||||||
@ -30,6 +34,9 @@ export interface IBuilding {
|
|||||||
modId: number
|
modId: number
|
||||||
weapons: WeaponHardpoint[]
|
weapons: WeaponHardpoint[]
|
||||||
addons: IBuildingAddon[]
|
addons: IBuildingAddon[]
|
||||||
|
researches: IResearch[]
|
||||||
|
units: IUnitShort[]
|
||||||
|
affectedResearches: IResearchShort[],
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IBuildingAddon {
|
export interface IBuildingAddon {
|
||||||
@ -43,8 +50,8 @@ export interface IBuildingAddon {
|
|||||||
addonCostFaith: number;
|
addonCostFaith: number;
|
||||||
addonCostSouls: number;
|
addonCostSouls: number;
|
||||||
addonCostTime: number;
|
addonCostTime: number;
|
||||||
addonModifiers: IAddonModifier[];
|
addonModifiers: IModifier[];
|
||||||
addonRequirement: IAddonRequirement;
|
addonRequirement: IRequirement;
|
||||||
icon?: string;
|
icon?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,19 +61,3 @@ export interface IBuildingAddonShort {
|
|||||||
name: string;
|
name: string;
|
||||||
icon: string;
|
icon: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IAddonRequirement {
|
|
||||||
requirementBuildings: IBuildingShort[];
|
|
||||||
requirementBuildingsEither: IBuildingShort[];
|
|
||||||
requirementsGlobalAddons: IBuildingAddonShort[];
|
|
||||||
replaceWhenDone: Boolean;
|
|
||||||
requireAddon: IBuildingAddonShort;
|
|
||||||
requiredTotalPop?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IAddonModifier {
|
|
||||||
id: number;
|
|
||||||
reference: string;
|
|
||||||
usageType: string;
|
|
||||||
value: number;
|
|
||||||
}
|
|
||||||
8
src/types/IModifier.tsx
Normal file
8
src/types/IModifier.tsx
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
export interface IModifier {
|
||||||
|
id: number;
|
||||||
|
reference: string;
|
||||||
|
usageType: string;
|
||||||
|
target: string;
|
||||||
|
value: number;
|
||||||
|
}
|
||||||
13
src/types/IRequirement.tsx
Normal file
13
src/types/IRequirement.tsx
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import {IBuildingShort} from "./IBuildingShort";
|
||||||
|
import {IBuildingAddonShort} from "./IBuilding";
|
||||||
|
import {IResearchRequirements} from "./IResearchRequirements";
|
||||||
|
|
||||||
|
export interface IRequirement {
|
||||||
|
requirementBuildings: IBuildingShort[];
|
||||||
|
requirementBuildingsEither: IBuildingShort[];
|
||||||
|
requirementsGlobalAddons: IBuildingAddonShort[];
|
||||||
|
requirementResearches: IResearchRequirements[];
|
||||||
|
replaceWhenDone: Boolean;
|
||||||
|
requireAddon: IBuildingAddonShort;
|
||||||
|
requiredTotalPop?: number;
|
||||||
|
}
|
||||||
24
src/types/IResearch.tsx
Normal file
24
src/types/IResearch.tsx
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import {IModifier} from "./IModifier";
|
||||||
|
import {IRequirement} from "./IRequirement";
|
||||||
|
import {IAffectedEntity, ISergeantUnitShortDto, IWeaponUnitShortDto} from "./IAffectedEntity";
|
||||||
|
|
||||||
|
export interface IResearch {
|
||||||
|
id: number
|
||||||
|
name: string
|
||||||
|
filename: string
|
||||||
|
description: string
|
||||||
|
costRequisition: number
|
||||||
|
costPower: number
|
||||||
|
costPopulation: number
|
||||||
|
costFaith: number
|
||||||
|
costSouls: number
|
||||||
|
costTime: number
|
||||||
|
icon: string
|
||||||
|
modId: number
|
||||||
|
affectedUnits: IAffectedEntity[]
|
||||||
|
affectedSergeants: ISergeantUnitShortDto[]
|
||||||
|
affectedBuildings: IAffectedEntity[]
|
||||||
|
affectedWeapons: IWeaponUnitShortDto[]
|
||||||
|
requirements: IRequirement
|
||||||
|
modifiers: IModifier[]
|
||||||
|
}
|
||||||
6
src/types/IResearchRequirements.tsx
Normal file
6
src/types/IResearchRequirements.tsx
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import {IResearchShort} from "./IResearchShort";
|
||||||
|
|
||||||
|
export interface IResearchRequirements {
|
||||||
|
researchShortDto: IResearchShort,
|
||||||
|
researchMustNotBeComplete: Boolean,
|
||||||
|
}
|
||||||
6
src/types/IResearchShort.tsx
Normal file
6
src/types/IResearchShort.tsx
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
export interface IResearchShort {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
icon: string;
|
||||||
|
buildingId: number;
|
||||||
|
}
|
||||||
@ -1,5 +1,6 @@
|
|||||||
import {Irace} from "./Irace";
|
import {Irace} from "./Irace";
|
||||||
import {IArmorType} from "./IArmorType";
|
import {IArmorType} from "./IArmorType";
|
||||||
|
import {IResearchShort} from "./IResearchShort";
|
||||||
|
|
||||||
export interface IUnitResponse {
|
export interface IUnitResponse {
|
||||||
race: string
|
race: string
|
||||||
@ -53,6 +54,7 @@ export interface IUnit {
|
|||||||
modId: number
|
modId: number
|
||||||
sergeants: ISergeant[]
|
sergeants: ISergeant[]
|
||||||
weapons: WeaponHardpoint[]
|
weapons: WeaponHardpoint[]
|
||||||
|
affectedResearches: IResearchShort[],
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ISergeant {
|
export interface ISergeant {
|
||||||
@ -81,6 +83,7 @@ export interface ISergeant {
|
|||||||
detectRadius: number
|
detectRadius: number
|
||||||
icon: string
|
icon: string
|
||||||
weapons: WeaponHardpoint[]
|
weapons: WeaponHardpoint[]
|
||||||
|
affectedResearches: IResearchShort[],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -118,6 +121,7 @@ export interface IWeapon {
|
|||||||
icon: string
|
icon: string
|
||||||
modId: number
|
modId: number
|
||||||
weaponArmorPiercing: IWeaponPiercing[]
|
weaponArmorPiercing: IWeaponPiercing[]
|
||||||
|
affectedResearches: IResearchShort[],
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IWeaponPiercing {
|
export interface IWeaponPiercing {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user