show degrading ws and bs characteristics, show psyker spells
This commit is contained in:
+3
-2
@@ -58,7 +58,7 @@ function App() {
|
|||||||
|
|
||||||
const rosterName = info.getAttribute("name");
|
const rosterName = info.getAttribute("name");
|
||||||
if (rosterName) {
|
if (rosterName) {
|
||||||
document.title = `PrettyScribe ${rosterName}`;
|
document.title = `FancyScribe ${rosterName}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gameType == "Warhammer 40,000 9th Edition") {
|
if (gameType == "Warhammer 40,000 9th Edition") {
|
||||||
@@ -90,6 +90,7 @@ function App() {
|
|||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
type="file"
|
type="file"
|
||||||
|
accept=".ros,.rosz"
|
||||||
name="rosterUpload"
|
name="rosterUpload"
|
||||||
id="rosterUpload"
|
id="rosterUpload"
|
||||||
onChange={handleFileSelect}
|
onChange={handleFileSelect}
|
||||||
@@ -146,7 +147,7 @@ function App() {
|
|||||||
<a href="https://github.com/NilsUeter/fancyscribe" target="_blank">
|
<a href="https://github.com/NilsUeter/fancyscribe" target="_blank">
|
||||||
FancyScribe
|
FancyScribe
|
||||||
</a>
|
</a>
|
||||||
) .
|
).
|
||||||
</div>
|
</div>
|
||||||
<div style={{ padding: "8px 0", fontSize: "1.7rem" }}>
|
<div style={{ padding: "8px 0", fontSize: "1.7rem" }}>
|
||||||
Output Examples
|
Output Examples
|
||||||
|
|||||||
+129
-18
@@ -115,6 +115,7 @@ const Unit = ({ unit, catalog }) => {
|
|||||||
modelStats,
|
modelStats,
|
||||||
modelList,
|
modelList,
|
||||||
psykers,
|
psykers,
|
||||||
|
spells,
|
||||||
cost,
|
cost,
|
||||||
} = unit;
|
} = unit;
|
||||||
|
|
||||||
@@ -238,7 +239,8 @@ const Unit = ({ unit, catalog }) => {
|
|||||||
{modelStats.map((model, index) => (
|
{modelStats.map((model, index) => (
|
||||||
<ModelStats
|
<ModelStats
|
||||||
key={index}
|
key={index}
|
||||||
modelStats={model}
|
modelStats={modelStats}
|
||||||
|
modelStat={model}
|
||||||
modelList={modelList}
|
modelList={modelList}
|
||||||
index={index}
|
index={index}
|
||||||
showName={modelStats.length > 1}
|
showName={modelStats.length > 1}
|
||||||
@@ -261,10 +263,8 @@ const Unit = ({ unit, catalog }) => {
|
|||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<table
|
<Spells title="PSYCHIC" spells={spells} />
|
||||||
className="weapons-table"
|
<table className="weapons-table" style={{ width: "100%" }}>
|
||||||
style={{ width: "100%", marginTop: 20 }}
|
|
||||||
>
|
|
||||||
<Weapons
|
<Weapons
|
||||||
title="RANGED WEAPONS"
|
title="RANGED WEAPONS"
|
||||||
weapons={rangedWeapons}
|
weapons={rangedWeapons}
|
||||||
@@ -275,11 +275,11 @@ const Unit = ({ unit, catalog }) => {
|
|||||||
weapons={meleeWeapons}
|
weapons={meleeWeapons}
|
||||||
modelStats={modelStats}
|
modelStats={modelStats}
|
||||||
/>
|
/>
|
||||||
<Psykers title="PSYKER" psykers={psykers} />
|
|
||||||
</table>
|
</table>
|
||||||
<div style={{ flex: "1" }}></div>
|
<div style={{ flex: "1" }}></div>
|
||||||
{hasDifferentProfiles && (
|
{hasDifferentProfiles && (
|
||||||
<table className="weapons-table" style={{ width: "100%" }}>
|
<table className="weapons-table" style={{ width: "100%" }}>
|
||||||
|
<tbody>
|
||||||
<tr className="emptyRow noBorderTop">
|
<tr className="emptyRow noBorderTop">
|
||||||
<td style={{ width: 37, borderTop: "none" }}>{Arrow}</td>
|
<td style={{ width: 37, borderTop: "none" }}>{Arrow}</td>
|
||||||
<td
|
<td
|
||||||
@@ -294,6 +294,7 @@ const Unit = ({ unit, catalog }) => {
|
|||||||
profiles to make attacks with.
|
profiles to make attacks with.
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
)}
|
)}
|
||||||
<Keywords keywords={keywords} />
|
<Keywords keywords={keywords} />
|
||||||
@@ -323,6 +324,7 @@ const Unit = ({ unit, catalog }) => {
|
|||||||
</div>
|
</div>
|
||||||
<Rules rules={rules} />
|
<Rules rules={rules} />
|
||||||
<Abilities abilities={abilities.Abilities} />
|
<Abilities abilities={abilities.Abilities} />
|
||||||
|
<Psykers title="PSYKER" psykers={psykers} />
|
||||||
<Factions factions={factions} />
|
<Factions factions={factions} />
|
||||||
<FactionIcon catalog={catalog} />
|
<FactionIcon catalog={catalog} />
|
||||||
</div>
|
</div>
|
||||||
@@ -333,12 +335,13 @@ const Unit = ({ unit, catalog }) => {
|
|||||||
|
|
||||||
const ModelStats = ({
|
const ModelStats = ({
|
||||||
modelStats,
|
modelStats,
|
||||||
|
modelStat,
|
||||||
index,
|
index,
|
||||||
showName,
|
showName,
|
||||||
showWeapons,
|
showWeapons,
|
||||||
modelList,
|
modelList,
|
||||||
}) => {
|
}) => {
|
||||||
let { move, toughness, save, wounds, leadership, name } = modelStats;
|
let { move, toughness, save, wounds, leadership, name, bs, ws } = modelStat;
|
||||||
if (!wounds) {
|
if (!wounds) {
|
||||||
wounds = "/";
|
wounds = "/";
|
||||||
}
|
}
|
||||||
@@ -357,6 +360,9 @@ const ModelStats = ({
|
|||||||
modelListMatches = modelListMatches.map((model) =>
|
modelListMatches = modelListMatches.map((model) =>
|
||||||
model.replaceAll(name, "")
|
model.replaceAll(name, "")
|
||||||
);
|
);
|
||||||
|
const bsChange = Math.abs(parseInt(modelStats[0].bs, 10) - parseInt(bs, 10));
|
||||||
|
const wsChange = Math.abs(parseInt(modelStats[0].ws, 10) - parseInt(ws, 10));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ display: "flex", gap: 16, alignItems: "center" }}>
|
<div style={{ display: "flex", gap: 16, alignItems: "center" }}>
|
||||||
<Characteristic title="M" characteristic={move} index={index} />
|
<Characteristic title="M" characteristic={move} index={index} />
|
||||||
@@ -374,6 +380,13 @@ const ModelStats = ({
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{name}
|
{name}
|
||||||
|
{name.toLowerCase().includes("wounds remaining") &&
|
||||||
|
(bsChange !== 0 || wsChange !== 0) &&
|
||||||
|
` Each time this model makes an attack, subtract ${
|
||||||
|
bsChange ? `${bsChange} from the balistic skill` : ""
|
||||||
|
}${bsChange !== 0 && wsChange !== 0 ? " and " : ""}${
|
||||||
|
wsChange ? `${wsChange} from the weapon skill` : ""
|
||||||
|
}.`}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{showWeapons && (
|
{showWeapons && (
|
||||||
@@ -385,7 +398,7 @@ const ModelStats = ({
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{modelListMatches.map((model, index) => (
|
{modelListMatches.map((model, index) => (
|
||||||
<div>{model}</div>
|
<div key={model}>{model}</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -578,7 +591,10 @@ const FancyBox = ({ children }) => {
|
|||||||
|
|
||||||
const Psykers = ({ title, psykers }) => {
|
const Psykers = ({ title, psykers }) => {
|
||||||
return (
|
return (
|
||||||
<>
|
<table
|
||||||
|
className="weapons-table"
|
||||||
|
style={{ width: "100%", margin: "4px 2px" }}
|
||||||
|
>
|
||||||
{psykers.length > 0 && (
|
{psykers.length > 0 && (
|
||||||
<thead>
|
<thead>
|
||||||
<tr
|
<tr
|
||||||
@@ -587,11 +603,6 @@ const Psykers = ({ title, psykers }) => {
|
|||||||
color: "#fff",
|
color: "#fff",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<th style={{ width: 37 }}>
|
|
||||||
<div style={{ display: "flex" }}>
|
|
||||||
<img src={rangedIcon} />
|
|
||||||
</div>
|
|
||||||
</th>
|
|
||||||
<th style={{ textAlign: "left" }}>{title}</th>
|
<th style={{ textAlign: "left" }}>{title}</th>
|
||||||
<th>Cast</th>
|
<th>Cast</th>
|
||||||
<th>Deny</th>
|
<th>Deny</th>
|
||||||
@@ -607,12 +618,12 @@ const Psykers = ({ title, psykers }) => {
|
|||||||
))}
|
))}
|
||||||
{psykers.length > 0 && (
|
{psykers.length > 0 && (
|
||||||
<tr className="emptyRow">
|
<tr className="emptyRow">
|
||||||
<td style={{ width: 37, borderTop: "none" }}></td>
|
<td style={{ width: 37 }}></td>
|
||||||
<td colSpan={7}></td>
|
<td colSpan={7}></td>
|
||||||
</tr>
|
</tr>
|
||||||
)}
|
)}
|
||||||
</tbody>
|
</tbody>
|
||||||
</>
|
</table>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -620,8 +631,10 @@ const Psyker = ({ psyker, index }) => {
|
|||||||
let { name, cast, deny, powers } = psyker;
|
let { name, cast, deny, powers } = psyker;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<tr className={index % 2 ? "rowOtherColor" : ""}>
|
<tr
|
||||||
<td style={{ borderTop: "none", backgroundColor: "#dfe0e2" }}></td>
|
className={index % 2 ? "rowOtherColor" : ""}
|
||||||
|
style={{ fontSize: ".8em" }}
|
||||||
|
>
|
||||||
<td style={{ textAlign: "left" }}>
|
<td style={{ textAlign: "left" }}>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
@@ -643,6 +656,104 @@ const Psyker = ({ psyker, index }) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const Spells = ({ title, spells }) => {
|
||||||
|
return (
|
||||||
|
<table className="weapons-table" style={{ width: "100%", marginTop: 20 }}>
|
||||||
|
{spells.length > 0 && (
|
||||||
|
<thead>
|
||||||
|
<tr
|
||||||
|
style={{
|
||||||
|
backgroundColor: "var(--primary-color)",
|
||||||
|
color: "#fff",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<th style={{ width: 37 }}>
|
||||||
|
<div style={{ display: "flex" }}>
|
||||||
|
<img src={rangedIcon} />
|
||||||
|
</div>
|
||||||
|
</th>
|
||||||
|
<th style={{ textAlign: "left" }}>{title}</th>
|
||||||
|
<th>RANGE</th>
|
||||||
|
<th>WARP CHARGE</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
)}
|
||||||
|
<tbody>
|
||||||
|
{spells.map((spell, index) => (
|
||||||
|
<Spell
|
||||||
|
key={spell.name}
|
||||||
|
spell={spell}
|
||||||
|
index={index}
|
||||||
|
className={getSpellClassNames(spells, index)}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
{spells.length > 0 && (
|
||||||
|
<tr className="emptyRow">
|
||||||
|
<td style={{ width: 37, borderTop: "none" }}></td>
|
||||||
|
<td colSpan={7}></td>
|
||||||
|
</tr>
|
||||||
|
)}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const Spell = ({ spell, index, className }) => {
|
||||||
|
let { name, range, manifest, details } = spell;
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<tr className={className}>
|
||||||
|
<td style={{ borderTop: "none", backgroundColor: "#dfe0e2" }}></td>
|
||||||
|
<td style={{ textAlign: "left" }}>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
flexWrap: "wrap",
|
||||||
|
gap: "0 4px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{name}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>{range}</td>
|
||||||
|
<td>{manifest}</td>
|
||||||
|
</tr>
|
||||||
|
<tr className={className + " noBorderTop"}>
|
||||||
|
<td style={{ backgroundColor: "#dfe0e2" }}></td>
|
||||||
|
<td
|
||||||
|
colSpan="7"
|
||||||
|
style={{
|
||||||
|
textAlign: "left",
|
||||||
|
fontSize: "0.8em",
|
||||||
|
paddingTop: 0,
|
||||||
|
paddingBottom: 1,
|
||||||
|
lineHeight: 1.4,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{details}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getSpellClassNames = (spells, index) => {
|
||||||
|
let differentColor = false;
|
||||||
|
for (let i = 1; i <= index; i++) {
|
||||||
|
let { name } = spells[i];
|
||||||
|
if (name !== spells[i - 1].name) {
|
||||||
|
differentColor = !differentColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const classes = [];
|
||||||
|
if (differentColor) classes.push("rowOtherColor");
|
||||||
|
if (index === 0) classes.push("noBorderTop");
|
||||||
|
if (index > 0 && spells[index].name === spells[index - 1].name)
|
||||||
|
classes.push("noBorderTop");
|
||||||
|
return classes.join(" ");
|
||||||
|
};
|
||||||
|
|
||||||
const ForceRules = ({ rules }) => {
|
const ForceRules = ({ rules }) => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
|||||||
+3
-3
@@ -97,7 +97,7 @@ const Weapon = ({ weapon, modelStats, isMelee, className }) => {
|
|||||||
type = "Plague Weapon";
|
type = "Plague Weapon";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (
|
/* if (
|
||||||
abilities?.includes(
|
abilities?.includes(
|
||||||
"Each time an attack made with this weapon targets an enemy within half range, that attack has a Damage characteristic of D6+2."
|
"Each time an attack made with this weapon targets an enemy within half range, that attack has a Damage characteristic of D6+2."
|
||||||
) ||
|
) ||
|
||||||
@@ -119,7 +119,7 @@ const Weapon = ({ weapon, modelStats, isMelee, className }) => {
|
|||||||
} else {
|
} else {
|
||||||
type = "Melta 2";
|
type = "Melta 2";
|
||||||
}
|
}
|
||||||
}
|
} */
|
||||||
|
|
||||||
if (
|
if (
|
||||||
abilities?.includes(
|
abilities?.includes(
|
||||||
@@ -251,7 +251,7 @@ const Weapon = ({ weapon, modelStats, isMelee, className }) => {
|
|||||||
<td>{damage}</td>
|
<td>{damage}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{abilities && abilities !== "-" && (
|
{abilities && abilities !== "-" && (
|
||||||
<tr className={className + " " + "noBorderTop"}>
|
<tr className={className + " noBorderTop"}>
|
||||||
<td style={{ backgroundColor: "#dfe0e2" }}></td>
|
<td style={{ backgroundColor: "#dfe0e2" }}></td>
|
||||||
<td
|
<td
|
||||||
colSpan="7"
|
colSpan="7"
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 276 KiB After Width: | Height: | Size: 278 KiB |
Reference in New Issue
Block a user