Skip to content

Commit

Permalink
Refactor race support and character creation steps
Browse files Browse the repository at this point in the history
  • Loading branch information
gvorbeck committed Jan 21, 2024
1 parent d460188 commit 086ab69
Show file tree
Hide file tree
Showing 5 changed files with 125 additions and 90 deletions.
16 changes: 7 additions & 9 deletions src/components/PageNewCharacter/StepAbilities/StepAbilities.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -117,18 +117,16 @@ const StepAbilities: React.FC<
scores: { ...character.abilities?.scores, ...scores },
modifiers: { ...character.abilities?.modifiers, ...modifiers },
},
class: character.class || [],
race: character.race || "",
class: [],
race: "",
hp: {
dice: character.hp?.dice || "",
points: character.hp?.points || 0,
max: character.hp?.max || 0,
desc: character.hp?.desc || "",
dice: "",
points: 0,
max: 0,
desc: "",
},
equipment: character.equipment || [],
equipment: [],
});
setComboClass && setComboClass(false);
setComboClassSwitch && setComboClassSwitch(false);
};

const rollAllAbilities = () => {
Expand Down
3 changes: 2 additions & 1 deletion src/components/PageNewCharacter/StepClass/StepClass.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,6 @@ const StepClass: React.FC<

// EFFECTS
React.useEffect(() => {
console.log("foo");
setStartingSpells([]);
setCustomClass(undefined);
if (standardClass) {
Expand Down Expand Up @@ -177,6 +176,8 @@ const StepClass: React.FC<
...character,
class: getFinalClass(),
spells: startingSpells,
hp: { dice: "", points: 0, desc: "", max: 0 },
equipment: [],
});
}
// eslint-disable-next-line react-hooks/exhaustive-deps
Expand Down
168 changes: 97 additions & 71 deletions src/components/PageNewCharacter/StepRace/StepRace.tsx
Original file line number Diff line number Diff line change
@@ -1,91 +1,117 @@
import React from "react";
import HomebrewWarning from "@/components/HomebrewWarning/HomebrewWarning";
import { CharData, RaceNames } from "@/data/definitions";
import { Flex, SelectProps } from "antd";
import { races } from "@/data/races";
import RaceClassSelector from "../RaceClassSelector/RaceClassSelector";
import RaceClassDescription from "../RaceClassDescription/RaceClassDescription";
import Options from "../StepClass/Options/Options";
import { getRaceSelectOptions } from "@/support/raceSupport";
import { useDeviceType } from "@/hooks/useDeviceType";
import { getRaceSelectOptions, isStandardRace } from "@/support/raceSupport";
import { Flex, Input, Select, Switch, Typography } from "antd";
import classNames from "classnames";
import React, { ChangeEvent } from "react";
import WRaceClassDescription from "../StepClass/WRaceClassDescription/WRaceClassDescription";
import { useImages } from "@/hooks/useImages";
import { toSlugCase } from "@/support/stringSupport";

interface StepRaceProps {
character: CharData;
setCharacter: (character: CharData) => void;
setComboClass: (comboClass: boolean) => void;
setComboClassSwitch: (comboClassSwitch: boolean) => void;
}

const StepRace: React.FC<
StepRaceProps & React.ComponentPropsWithRef<"div">
> = ({
className,
character,
setCharacter,
setComboClass,
setComboClassSwitch,
}) => {
const [raceSelector, setRaceSelector] = React.useState<string>(
character.race,
> = ({ className, character, setCharacter }) => {
// STATE
const [supplementalContent, setSupplementalContent] = React.useState<boolean>(
character.race ? !isStandardRace(character.race, true) : false,
);
const [customRaceInput, setCustomRaceInput] = React.useState<string>("");
const [supplementalContentSwitch, setSupplementalContentSwitch] =
React.useState<boolean>(false);

const raceSelectOptions: SelectProps["options"] = getRaceSelectOptions(
character,
!supplementalContentSwitch,
const [race, setRace] = React.useState<string | undefined>(
character.race as string | undefined,
);

const handleSelectChange = (value: string) => {
setRaceSelector(value);
setComboClassSwitch(false);
// if the character has a non-custom race
const [standardRace, setStandardRace] = React.useState<string | undefined>(
!!character.race && isStandardRace(character.race)
? character.race
: undefined,
);
//
const [customRace, setCustomRace] = React.useState<string | undefined>(
!!character.race && !isStandardRace(character.race)
? character.race
: undefined,
);
// HOOKS
const { isMobile } = useDeviceType();
// VARS
const { getRaceClassImage } = useImages();
const innerFlexClassNames = classNames({ "justify-between": isMobile });
const raceImage = (className: RaceNames) =>
getRaceClassImage(toSlugCase(className));
// METHODS
const onSupplementalContentChange = (checked: boolean) => {
setSupplementalContent(checked);
setRace(undefined);
setStandardRace(undefined);
setCustomRace(undefined);
};

const handleCustomRaceInputChange = (
e: React.ChangeEvent<HTMLInputElement>,
) => {
setCustomRaceInput(e.target.value);
setCharacter({ ...character, race: e.target.value });
const onStandardRaceChange = (value: string) => {
setCustomRace(undefined);
setStandardRace(value);
setRace(value);
};

const onCustomRaceChange = (event: ChangeEvent<HTMLInputElement>) => {
const { value } = event.target;
setCustomRace(value);
setRace(value);
};
// EFFECTS
React.useEffect(() => {
if (raceSelector !== "custom" && raceSelector !== "") {
setCharacter({ ...character, race: raceSelector });
}
// setCharacter({}) // race and class, dice, equipment, spells, hp, etc
if (race)
setCharacter({
...character,
race,
class: [],
spells: [],
hp: { dice: "", points: 0, desc: "", max: 0 },
equipment: [],
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [raceSelector]);

React.useEffect(
() => {
setComboClass(
!!races[character.race as keyof typeof races]?.allowedCombinationClasses
?.length || false,
);
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[character],
);

}, [race]);
return (
<Flex vertical className={className} gap={16}>
<Options
setComboClassSwitch={setComboClassSwitch}
setSupplementalContentSwitch={setSupplementalContentSwitch}
supplementalContentSwitch={supplementalContentSwitch}
/>
<RaceClassSelector
customInput={customRaceInput}
handleCustomInputChange={handleCustomRaceInputChange}
handleSelectChange={handleSelectChange}
selectOptions={raceSelectOptions}
selector={raceSelector}
type="race"
/>
{!!raceSelector && (
<RaceClassDescription
name={raceSelector}
description={`${races[raceSelector as RaceNames]?.details
?.description}`}
<Flex gap={16} vertical className={className}>
<Flex gap={8} className={innerFlexClassNames}>
<Typography.Text>Enable Supplemental Content</Typography.Text>
<Switch
checked={supplementalContent}
onChange={onSupplementalContentChange}
/>
</Flex>
<Select
options={
supplementalContent
? getRaceSelectOptions(character, false)
: getRaceSelectOptions(character)
}
value={
standardRace === undefined && isStandardRace(character.race)
? "Custom"
: standardRace
}
onChange={onStandardRaceChange}
placeholder="Select a race"
/>
{customRace || standardRace === "Custom" ? (
<>
<HomebrewWarning homebrew="race" />
<Input
value={customRace ?? character.race}
onChange={(e) => onCustomRaceChange(e)}
/>
</>
) : (
standardRace && (
<WRaceClassDescription
subject={race ?? ""}
image={raceImage(race as RaceNames)}
/>
)
)}
</Flex>
);
Expand Down
14 changes: 7 additions & 7 deletions src/support/pageNewCharacterSupport.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,21 @@ const newCharacterStepDescriptions = {
abilities:
marked(`Roll for your character's Abilities. **You can click the "Roll" buttons or use your own dice and record your scores**. Afterward your character will have a score ranging from 3 to 18 in each of the Abilities below. A bonus (or penalty) Modifier is then associated with each score. Your character's Abilities will begin to determine the options available to them in the next steps as well, so good luck!
<a href="https://basicfantasy.org/srd/abilities.html" target="_blank">BFRPG Character Ability documentation</a>`),
<a href="https://basicfantasy.org/srd/abilities.html" target="_blank">BFRPG Official Character Ability documentation</a>`),
race: marked(`Choose your character's Race. **Some options may be unavailable due to your character's Ability Scores**. Each Race except Humans has a minimum and maximum value for specific Abilities that your character's Ability Scores must meet in order to select them. Consider that each Race has specific restrictions, special abilities, and Saving Throws. Choose wisely. **Races included in the base game rules are in bold**.
<a href="https://basicfantasy.org/srd/races.html" target="_blank">BFRPG Character Race documentation</a>`),
<a href="https://basicfantasy.org/srd/races.html" target="_blank">BFRPG Official Character Race documentation</a>`),
class:
marked(`Choose your character's Class. **Your character's Race and Ability Scores will determine which Class options are available**. Your Class choice determines your character's background and how they will progress through the game as they level up. **Classes included in the base game rules are in bold**.
<a href="https://basicfantasy.org/srd/class.html" target="_blank">BFRPG Character Class documentation</a>`),
<a href="https://basicfantasy.org/srd/class.html" target="_blank">BFRPG Official Character Class documentation</a>`),
hp: marked(`Roll for your character's Hit Points. **Your character's Race may place restrictions on the Hit Dice available to them, but generally this is determined by their chosen Class**. Additionally, your character's Constitution modifier is added/subtracted from this value with a minimum value of 1. The end result is the amount of Hit Points your character will start with and determines how much damage your character can take in battle.
<a href="https://basicfantasy.org/srd/abilities.html#hit-points-and-hit-dice" target="_blank">BFRPG Character Hit Points documentation</a>`),
<a href="https://basicfantasy.org/srd/abilities.html#hit-points-and-hit-dice" target="_blank">BFRPG Official Character Hit Points documentation</a>`),
equipment: marked(
`Roll for your character's starting gold and purchase their equipment. **Keep in mind that your character's Race and Class selections may limit types and amounts of equipment they can have**.
<a href="https://basicfantasy.org/srd/equipment.html" target="_blank">BFRPG Character Equipment documentation</a>`,
<a href="https://basicfantasy.org/srd/equipment.html" target="_blank">BFRPG Official Character Equipment documentation</a>`,
),
name: marked(
`You're almost done! Personalize your newly minted character by giving them an identity. Optionally, upload a portrait image if you'd like. Image must be PNG/JPG and <= 1mb`,
Expand Down Expand Up @@ -66,8 +66,8 @@ export const getStepsItems = (
<StepRace
character={character}
setCharacter={setCharacter}
setComboClass={setComboClass}
setComboClassSwitch={setComboClassSwitch}
// setComboClass={setComboClass}
// setComboClassSwitch={setComboClassSwitch}
/>
),
},
Expand Down
14 changes: 12 additions & 2 deletions src/support/raceSupport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,15 @@ export const getRaceSelectOptions = (
}));
};

export const isStandardRace = (raceName: string) =>
Object.values(RaceNames).includes(raceName as RaceNames);
export const baseClasses = [
RaceNames.DWARF,
RaceNames.ELF,
RaceNames.HALFLING,
RaceNames.HUMAN,
];

export const isStandardRace = (raceName: string, isBase = false) => {
if (!raceName) return false;
if (isBase) return Object.values(baseClasses).includes(raceName as RaceNames);
return Object.values(RaceNames).includes(raceName as RaceNames);
};

0 comments on commit 086ab69

Please sign in to comment.