From a8b72983f7fa15d8376fced2b01f15ad87d3abd7 Mon Sep 17 00:00:00 2001 From: QxBytes Date: Mon, 5 Jul 2021 16:53:30 -0700 Subject: [PATCH] Polishing, simulator fully functional --- src/App.tsx | 4 +- src/cc.scss | 8 +- src/features/item/DamageCalculator.tsx | 5 +- src/features/item/Parts.tsx | 78 +++++++- src/features/item/SetupContainer.tsx | 19 +- src/features/item/Utils.ts | 10 +- src/features/item/activeSlice.ts | 26 +-- src/features/item/damage.ts | 11 +- src/features/item/entity.ts | 6 +- src/features/item/images/full_armor.png | Bin 0 -> 663 bytes src/features/item/images/full_heart.png | Bin 0 -> 665 bytes src/features/item/images/half_armor.png | Bin 0 -> 648 bytes src/features/item/images/no_armor.png | Bin 0 -> 581 bytes src/features/item/images/no_heart.png | Bin 0 -> 589 bytes src/features/simulator/DamageCard.tsx | 28 ++- src/features/simulator/DragContainer.tsx | 1 + src/features/simulator/Simulator.tsx | 216 ++++++++++++++++------- src/features/weapon/WeaponEditor.tsx | 7 +- src/features/weapon/weapon.ts | 10 +- 19 files changed, 323 insertions(+), 106 deletions(-) create mode 100644 src/features/item/images/full_armor.png create mode 100644 src/features/item/images/full_heart.png create mode 100644 src/features/item/images/half_armor.png create mode 100644 src/features/item/images/no_armor.png create mode 100644 src/features/item/images/no_heart.png diff --git a/src/App.tsx b/src/App.tsx index 3e20e87..df5d1e1 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -38,12 +38,12 @@ function App() { - + - + diff --git a/src/cc.scss b/src/cc.scss index 342ca5c..ab58bac 100644 --- a/src/cc.scss +++ b/src/cc.scss @@ -76,6 +76,10 @@ button.btn-primary { text-align: center; } +.type-display { + max-width: 10rem; +} + .damage-display-container { margin: 20px; background-color: rgb(240,240,240); @@ -112,8 +116,8 @@ button.btn-primary { } -.h-100vh { - height: 100vh; +.h-95vh { + height: 95vh; } .overflow-scroll { overflow-x: hidden; diff --git a/src/features/item/DamageCalculator.tsx b/src/features/item/DamageCalculator.tsx index 1b1d452..7fbe5eb 100644 --- a/src/features/item/DamageCalculator.tsx +++ b/src/features/item/DamageCalculator.tsx @@ -10,10 +10,9 @@ import RangeSlider from 'react-bootstrap-range-slider'; import { DamageGraph } from "./DamageGraph"; import NumericInput from 'react-numeric-input'; -import heart from './images/half_heart_lg.png'; import { DamageSummaryTable } from "./DamageSummary"; import { WeaponEditor } from "../weapon/WeaponEditor"; -import { Collapseable } from "./Parts"; +import { Collapseable, HalfHeart } from "./Parts"; import EditInPlace from "./EditInPlace"; import { baseDamageType } from "./damageTypes"; import Icon from "./Icons"; @@ -47,7 +46,7 @@ export function DamageCalculator(props : DamageCalculatorType) { - +
void, inner?: React.ReactNode, - title?: string, + title?: React.ReactNode, options?: React.ReactNode, className?: string } @@ -66,4 +78,66 @@ export function Collapseable(props: CollapseableType) { ); +} +export function HalfHeart() { + return ( + + ); +} +export function HealthBar(props: {health: number}) { + return +} +export function ArmorBar(props: {armor: number}) { + return +} +export function ToughnessBar(props: {toughness: number}) { + return +} +interface DynamicBarType { + value: number, + max?: number, + full: string, + half: string, + empty?: string +} +export function DynamicBar(props: DynamicBarType) { + let val = Math.round(props.value); + let full = Math.floor(val / 2); + let half = val % 2 === 1; + let elements = []; + for (let i = 0 ; i < full ; i++) { + elements.push( + + ) + } + if (half) { + elements.push( + + ) + } + if (props.max && props.empty) { + let left = Math.floor((props.max - val)/2); + for (let i = 0 ; i < left ; i++) { + elements.push( + + ); + } + } + return {elements}; } \ No newline at end of file diff --git a/src/features/item/SetupContainer.tsx b/src/features/item/SetupContainer.tsx index 6f75192..6055a6d 100644 --- a/src/features/item/SetupContainer.tsx +++ b/src/features/item/SetupContainer.tsx @@ -4,10 +4,11 @@ import { Row, Col, ButtonGroup, Button } from "react-bootstrap"; import { useSelector } from "react-redux"; import { useAppDispatch } from "../../app/hooks"; import { addSetup, removeSetup, selectEntity } from "./activeSlice"; -import { Entity, getDefaultSetup, maxSetups, summary } from "./entity"; +import { getSetArmor, getSetToughness } from "./armor"; +import { Entity, getDefaultSetup, maxHealth, maxSetups, summary } from "./entity"; import { EntityContainer } from "./EntityContainer"; import Icon from "./Icons"; -import { Collapseable } from "./Parts"; +import { ArmorBar, Collapseable, HealthBar, ToughnessBar } from "./Parts"; import { getPresetColor } from "./Utils"; const _ = require('lodash'); @@ -37,9 +38,19 @@ export function SetupContainer() { } > } - title={summary(item)} + title={ + <> + {" " + summary(item) + " "} + + | + + | + + + } options={ + {!maxSetups(entities) ? : "" } diff --git a/src/features/item/Utils.ts b/src/features/item/Utils.ts index eccca2d..6913bd4 100644 --- a/src/features/item/Utils.ts +++ b/src/features/item/Utils.ts @@ -1,4 +1,5 @@ import functionPlot from "function-plot"; +import { bound } from "./maths"; var Color = require('color'); export function range(from: number, to: number) { @@ -12,7 +13,14 @@ export function round(num : number) { return Math.round((num + Number.EPSILON) * 100) / 100; } export function getColor(max: number, value: number) { - return Color.hsl(180*(value/max),70,70); + return Color.hsl(180*(bound(value/max,0,1)),70,70); +} +export function getDeltaColor(max: number, value: number) { + if (value > 0) { + return Color.rgb(255 - (-value/max*255), 255, 255 - (-value/max*255)); + } else { + return Color.rgb(255, 255 - (-value/max*255), 255 - (-value/max*255)); + } } export function getPresetColor(index: number) { return functionPlot.globals.COLORS[index]; diff --git a/src/features/item/activeSlice.ts b/src/features/item/activeSlice.ts index fac3813..a62534e 100644 --- a/src/features/item/activeSlice.ts +++ b/src/features/item/activeSlice.ts @@ -6,7 +6,7 @@ import * as e from "./enchants"; import * as d from "./damageTypes"; import * as f from "./effects"; import { Entity, MAX_SETUPS, removeSetup as removeEntity } from "./entity"; -import { Damage, DamageItem } from "./damage"; +import { Damage, DamageItem, find } from "./damage"; import { defaultWeapon, Weapon } from "../weapon/weapon"; export interface matchState { @@ -53,7 +53,8 @@ const initialState : matchState = { ticks: 10 }, id: 0, - visible: true + visible: true, + times: 1 }, { dmg: { @@ -62,7 +63,8 @@ const initialState : matchState = { ticks: 10 }, id: 1, - visible: true + visible: true, + times: 1 }, { dmg: { @@ -71,7 +73,8 @@ const initialState : matchState = { ticks: 10 }, id: 2, - visible: true + visible: true, + times: 1 }, ], id: 99 @@ -118,10 +121,13 @@ export const activeSlice = createSlice( { state.damage.ticks = action.payload; }, toggleDamage: (state, action: PayloadAction) => { - for (let i = 0 ; i < state.damages.length ; i++) { - if (state.damages[i].id === action.payload) { - state.damages[i].visible = !state.damages[i].visible; - } + find(state.damages, action.payload).visible = !find(state.damages, action.payload).visible; + }, + addDamageTimes: (state, action: PayloadAction<{id: number, change: number}>) => { + let p = action.payload; + find(state.damages, p.id).times += p.change; + if (find(state.damages, p.id).times < 0) { + find(state.damages, p.id).times = 0; } }, removeSetup: (state, action: PayloadAction) => { @@ -132,7 +138,7 @@ export const activeSlice = createSlice( { }, saveDamage: (state, action: PayloadAction) => { state.id = state.id + 1; - state.damages.push({dmg:action.payload, id:state.id+1, visible: true}); + state.damages.push({dmg:action.payload, id:state.id+1, visible: true, times: 1}); }, //number is ID value! removeDamage: (state, action: PayloadAction) => { @@ -152,7 +158,7 @@ export const activeSlice = createSlice( { }); export const {setType, setEnchant, removeEnchant, removeEffect, setEffect, setDamageType, setDamage, - setDamageTicks, toggleDamage, removeSetup, addSetup, saveDamage, removeDamage, moveDamage} = activeSlice.actions; + setDamageTicks, toggleDamage, addDamageTimes, removeSetup, addSetup, saveDamage, removeDamage, moveDamage} = activeSlice.actions; // The function below is called a selector and allows us to select a value from // the state. Selectors can also be defined inline where they're used instead of // in the slice file. For example: `useSelector((state: RootState) => state.counter.value)` diff --git a/src/features/item/damage.ts b/src/features/item/damage.ts index cc14433..58446a6 100644 --- a/src/features/item/damage.ts +++ b/src/features/item/damage.ts @@ -11,7 +11,16 @@ export interface Damage { export interface DamageItem { dmg: Damage, id: number, - visible: boolean + visible: boolean, + times: number +} +export function find(items: DamageItem[], id: number) { + for (let i = 0 ; i < items.length ; i++) { + if (items[i].id === id) { + return items[i]; + } + } + return items[0]; } /* export class Damage implements DamageType { diff --git a/src/features/item/entity.ts b/src/features/item/entity.ts index 0695ef9..417548a 100644 --- a/src/features/item/entity.ts +++ b/src/features/item/entity.ts @@ -28,12 +28,12 @@ export function summary(e : Entity) { let str = ""; str += e.armor[0].type.charAt(0) + "/" + e.armor[1].type.charAt(0) + "/" + e.armor[2].type.charAt(0) + "/" + e.armor[3].type.charAt(0); - str += " A: " + getSetArmor(e.armor); - str += " T: " + getSetToughness(e.armor); + //str += " A: " + getSetArmor(e.armor); + //str += " T: " + getSetToughness(e.armor); if (getEffect(e.effects, RESISTANCE.key)) { str += " R: " + nomar(getEffect(e.effects, RESISTANCE.key)!.value); } - str += " HP: " + maxHealth(e); + //str += " HP: " + maxHealth(e); return str; } export function removeSetup(setups : Entity[], i : number) { diff --git a/src/features/item/images/full_armor.png b/src/features/item/images/full_armor.png new file mode 100644 index 0000000000000000000000000000000000000000..051819860acf5f18956360fe56284290b914b017 GIT binary patch literal 663 zcmeAS@N?(olHy`uVBq!ia0vp^oFL4?3?zm2ODh8@mUKs7M+SzC{oH>NS%G|oWRD45bDP46hOx7_4S6Fo+k-*%fF5ln@H=32_C|K=ADOryV~G#cA;yv*zhDN3XE)M7oFs2|7uFy7FOLH`>?NMQuIx`Zq&Soex2=*` z1Cq}#@(W=+zo>i`kYniS;uyklJvl)nK_DeTz$hg|EJ46b%nV2xnwbeEB$$|)r8ua@ z#>hxYNU*T7N-$Iwv+Q_#a^W_hX4Mkch?11Vl2ohYqEsNoU}Ruqple{EYh)H;U}0r! zVr62fZD3$!U=ZnXCmTgWZhlH;S|x4`8loxJff^J*HWcTlm6RtIr7~ocloS+O>Fa0a zCZ?wXab|M1UVc&fp7XMEfodf{YC|$gbCayBT=J7kb5rw5tgHfnN{bl`m;c|b4^$(D zq{b6uij|c|YG!&y34^8N|IbE16{46b!ZTA!G8incOcZ|)R3M3@z&A5DwWP8jl>zJ` z{erx7`%PhfKtNS%G|oWRD45bDP46hOx7_4S6Fo+k-*%fF5ln@H=32_C|K=5B!`2UF$ zyMg5Y|LSrJU4TN2B|(0{3=Yq3q=7g|-tI1}Kk{E52Xfd;Jbhi+pKyrtGD@pU?$HIw z=NI{fFrHsjJ`2b(@pN$v;kYh(%8{4BfaBfnd)5*X4O(opP7>cm{X@NNHhWJS1oakC`m~yNwrEYN(E93Mg~R(x&{`yM#dqA z##RP~Rz~L91_o9J1{-`D%uzJt=BH$)RpQogP|%_qs6hc_LvemuNqJ&XDnmv|NkOrd zzJ6wIVtOhNXC`OsmFUU)`-xTHtR3wh9CWfxgx+$t}NS%G|oWRD45bDP46hOx7_4S6Fo+k-*%fF5ln@Q@32_C|K(J%yk!R08 z+1lD>W)=MZ|6lEa=Qf}yV@Z%-FoVOh8)+a;lDE4HTVmRdS|EqL#M9T6{V9_mpN9E& zx6~y-AuUfA#}JO|$q6C}0x1asMkytN2?9YvjFT26ObYZoGLdmA59>sprY0sHhQcW< z*40P4P6KtRmbgZgq$HN4S|t~y0x1R~10zFS10!8S^AJN5D-#1NQ&VjN11kfAM=#!m zplHa=PsvQH#I0fS)-a&Y85BS^6z8XvlqVLYGGvsL6ck(O>u2UBrl$gNW^%S(eo^|K z^Rjb+Y9&BwLo!NpldP;<@{>z*Q}ar!tO9^aix~`;|KF_-R3nC@#uH?Um6b^FH#0Z2q_QBD0qi3Eg1mJ5O<{gOMdHYc zLV&6m49yIUP0fugOii~e-)IF?B7meMII{|6afPv-O$L=*uv1z(8%1t%*4{v#K6qJRN_U=b)X3hp00i_>zopr0Qr2t AvH$=8 literal 0 HcmV?d00001 diff --git a/src/features/item/images/no_armor.png b/src/features/item/images/no_armor.png new file mode 100644 index 0000000000000000000000000000000000000000..2998a149dced079245990fc8670fa876c0e7a202 GIT binary patch literal 581 zcmeAS@N?(olHy`uVBq!ia0vp^oFL4>0wld=oSO}#SkfJR9T^xl_H+M9WCik>lDyqr z82-2SpV<%Ov6p!Iy0Smvkm67>IJj2B3Mj;wU*s3Ucz#j&EFj0!)5S4_zl#m4U&3`|I~nH00)| zWTsW(*3g%fpA6KX0J5PtKdq!Zu_%=xqokyu*h*hNGdD3k6^JvFv-R?e()XN~oeNYe z0a6>1QJR}%W#y8eT$-DjS7K!q090DcV7UDMZhfE{F(fsfAXBWYJW?~$GfEgNCI5dm z0;&+jR1uz;Qj)=7d1a#bd!PbIBn7^ixv3?U1*r^R7wH$|rQ2@`^8+dpM^+R9RK;Lu zYH49&Y+zww!OT><4yc3=Nl9>KRVssl)5&~^w?G*)B$?14PlmM2oK&E%^-FRK^h-eg t%P9f|LAs%tv9Y0wld=oSO}#SkfJR9T^xl_H+M9WCik>lDyqr z82-2SpV<%Ov6p!Iy0Smv5a;2vzM4~W3n;{xU*s3Ucz#j&EFj0u)5S4_<9hD7-MkGB zJS-Q}R~7!h8e^{Z;h}RArtjb1u)m7-*(oo<&m-967xu-4FhPTAAChi5ya zi_HZ!uCcUD2AZT=;u=wsl30>zm0Xkxq!^40j0|)QEOd>GLkx|q3@oioEwv2{tPBiP zzGpL`Xvob^$xN%nts!*P<$XX63LqPb^V3So6N^$AGD=DcimmkZGjkKuQ-L@$Ia@Ek zD1Fa)*||Wq5+Jo98Kt>NR#q0YIh242H}9@74#Z5kper2{OgX$|E&1 zJ)?xdQu6<2BcKXVOcmjoDJ2;UmRBZ void @@ -32,7 +35,7 @@ interface DragItem { type: string } -export const Card: FC = ({ id, visible, damage, index, moveCard }) => { +export const Card: FC = ({ id, visible, times, damage, index, moveCard }) => { const dispatch = useAppDispatch(); const ref = useRef(null) @@ -109,18 +112,29 @@ export const Card: FC = ({ id, visible, damage, index, moveCard }) =>
- dispatch(toggleDamage(id))} defaultChecked - /> - {damage.type} + /> + + + + + {times} x + + { " " + round(damage.amount) + " · " + damage.type} - + diff --git a/src/features/simulator/DragContainer.tsx b/src/features/simulator/DragContainer.tsx index ea2578e..13891ac 100644 --- a/src/features/simulator/DragContainer.tsx +++ b/src/features/simulator/DragContainer.tsx @@ -43,6 +43,7 @@ export const DragContainer: FC = () => { key={card.id} index={index} visible={card.visible} + times={card.times} id={card.id} damage={card.dmg} moveCard={moveCard} diff --git a/src/features/simulator/Simulator.tsx b/src/features/simulator/Simulator.tsx index b321338..a3498e9 100644 --- a/src/features/simulator/Simulator.tsx +++ b/src/features/simulator/Simulator.tsx @@ -1,78 +1,148 @@ import { useState } from "react"; -import { Table } from "react-bootstrap"; +import { Form, Table } from "react-bootstrap"; import { useSelector } from "react-redux"; import { selectDamages, selectEntity } from "../item/activeSlice"; import { Damage, DamageItem } from "../item/damage"; import { maxHealth, summary } from "../item/entity"; +import { Tip } from "../item/Icons"; import { takeDamage } from "../item/maths"; -import { elementwiseAdd, getPresetColor, round } from "../item/Utils"; +import { elementwiseAdd, getColor, getDeltaColor, getPresetColor, round } from "../item/Utils"; import { getDamage, getSeconds, toString, Weapon } from "../weapon/weapon"; -const ABSOLUTE = 'Absolute'; -const PERCENT = 'Percent'; -const SETTINGS = [ABSOLUTE, PERCENT]; +const CHANGES = 'Changes'; +const STEPS = 'Steps'; export function Simulator() { const entities = useSelector(selectEntity); const damages = useSelector(selectDamages); - const [setting, setSetting] = useState(ABSOLUTE); - + const [percent, setPercent] = useState(false); + const [showChanges, setShowChanges] = useState(false); + const [showSteps, setShowSteps] = useState(true); + const renderSettings = () => { + return ( +
+
+ setShowSteps(!showSteps)} + /> + setShowChanges(!showChanges)} + /> + + setPercent(!percent)} + /> +
+
+ ); + } const renderHeader = () => { - return entities.map( (item, index) => { - return ( - - {summary(item)} - - ); - }) + return ( + <> + + Attack + + + Time + + {entities.map( (item, index) => { + return ( + <> + + + {summary(item)} + + + )})} + + ); } const renderBody = () => { let hp = entities.map((item) => maxHealth(item)); let time = 0; let maxHp = entities.map((item) => maxHealth(item)); - return ( - damages.map( (damageItem: DamageItem, index) => { + const renderDamages = () => { + let initial = ( + 0)} + newHp={hp} + maxHp={maxHp} + index={-1} + percent={percent} + type={"Initial"} + showChanges={false} + showResults={true} + /> + ); + let rows = damages.map( (damageItem: DamageItem, index) => { if (damageItem.visible === false) { return ""; } + let elements = []; + for (let i = 0 ; i < damageItem.times ; i++) { + let damage = damageItem.dmg; - let damage = damageItem.dmg; - - //update the values one step forward - time += damage.ticks; - let deltaHp = entities.map( (entity) => { - return -takeDamage(damage, entity); - }); - hp = elementwiseAdd(hp, deltaHp); - - return (); + //update the values one step forward + time += damage.ticks; + let deltaHp = entities.map( (entity) => { + return -takeDamage(damage, entity); + }); + hp = elementwiseAdd(hp, deltaHp); + + elements.push (); + } + return <>{elements} }) + + return (<> + {initial} + + {showChanges || showSteps ? renderHeader() : ""} + {rows} + ); + } + return ( + renderDamages() ); } return (
+ {renderSettings()} - - + {renderHeader()} @@ -90,31 +160,37 @@ interface SimulatorRowType { newHp: number[], maxHp: number[], index: number, - show: string, - type: string + percent: boolean, + type: string, + showChanges: boolean, + showResults: boolean } export function SimulatorRow(props: SimulatorRowType) { + const [hover, setHover] = useState(false); + const getPercent = (num: number, index: number) => { + return round(num*100 / props.maxHp[index]); + } const getContent = (num: number, index: number) => { - if (props.show === ABSOLUTE) { - return num; - } - if (props.show === PERCENT) { - return num / props.maxHp[index]; + if (props.percent) { + return getPercent(num, index) + "%"; + } else { + return round(num); } } const renderDeltas = () => { return ( - - setHover(true)} onMouseLeave={() => setHover(false)}> + { props.deltaHp.map( (num, index) => { - return (); }) } @@ -123,27 +199,35 @@ export function SimulatorRow(props: SimulatorRowType) { } const renderResults = () => { return ( - - setHover(true)} onMouseLeave={() => setHover(false)}> + { props.newHp.map( (num, index) => { - return () }) } ); } + const renderName = () => { + return ( + hover ? + props.type : + props.type.substring(0, 25) + ) + } return ( <> - {renderDeltas()} - {renderResults()} + {props.showChanges ? renderDeltas() : ""} + {props.showResults ? renderResults() : ""} ); } \ No newline at end of file diff --git a/src/features/weapon/WeaponEditor.tsx b/src/features/weapon/WeaponEditor.tsx index 45c0bf4..296252a 100644 --- a/src/features/weapon/WeaponEditor.tsx +++ b/src/features/weapon/WeaponEditor.tsx @@ -1,7 +1,7 @@ import React, { useState } from "react"; import { Button, ButtonGroup, Col, Form, Row, Image } from "react-bootstrap"; import NumericInput from "react-numeric-input"; -import { getDamage, getDamageEquation, getSeconds, getTicks, preset, toString } from "./weapon"; +import { getDamage, getDamageEquation, getSeconds, getTicks, percentCharge, preset, toString } from "./weapon"; import { DropInput } from "../item/Parts"; import { defaultWeapon, FIST, makeWeapon, TRIDENT, Weapon, WEAPONS, WEAPON_MATERIALS } from "./weapon"; import { WeaponDamageGraph } from "./WeaponDamageGraph"; @@ -67,7 +67,7 @@ export function WeaponEditor() { Wait - { //Poor documentation @@ -75,7 +75,8 @@ export function WeaponEditor() { const x = c(); x.ticksSinceLast = valueAsNumber*20; setWeapon(x); } }} - format={(num) => num + " sec (" + (getTicks(num||0)) + " ticks)"} + format={(num) => num + " sec (" + (getTicks(num||0)) + " ticks, " + + percentCharge(weapon) + "%)"} /> before attack diff --git a/src/features/weapon/weapon.ts b/src/features/weapon/weapon.ts index ce13a16..d0d41fd 100644 --- a/src/features/weapon/weapon.ts +++ b/src/features/weapon/weapon.ts @@ -4,6 +4,7 @@ import * as e from "../item/enchants"; import { DIAMOND, IRON, GOLDEN, NETHERITE, NONE } from "../item/armor"; import { Effect, getEffectLevel, makeEffect, setEffect, STRENGTH, WEAKNESS } from "../item/effects"; import functionPlot from "function-plot"; +import { round } from "../item/Utils"; const _ = require('lodash'); const nomar = require('nomar'); //cooldown T = 20 / attackSpeed @@ -223,7 +224,7 @@ export function toString(w: Weapon) { } else { temp += w.material + " " + w.type + " · "; } - temp += w.ticksSinceLast + " · "; + temp += percentCharge(w) + "% · "; if (w.critical) { temp += "Critical · "; } @@ -238,7 +239,12 @@ export function toString(w: Weapon) { } return temp + " Melee" } - +export function ticksToFullCharge(w: Weapon) { + return 20 / w.attackSpeed; +} +export function percentCharge(w: Weapon) { + return round(Math.min(100, w.ticksSinceLast*100/ticksToFullCharge(w))); +} /* export class MeleeWeapon implements Weapon { constructor (
- Order - - Time -
- {props.type} +
+ {renderName()} - +{getSeconds(props.deltaTime)} sec (+{props.deltaTime}) ticks + +{getSeconds(props.deltaTime)} sec - {getContent(round(num), index)} + return ( + + {getContent(num, index)}
- +
+ {!props.showChanges ? renderName() : ""} - {getSeconds(props.newTime)} sec ({props.deltaTime}) ticks + {getSeconds(props.newTime)} sec - {getContent(round(num), index)} + return ( + + {getContent(num, index)}