diff --git a/frontend/src/second-edition/components/KarakterKepzettsegek2E.tsx b/frontend/src/second-edition/components/KarakterKepzettsegek2E.tsx index ab708c8..8bd1347 100644 --- a/frontend/src/second-edition/components/KarakterKepzettsegek2E.tsx +++ b/frontend/src/second-edition/components/KarakterKepzettsegek2E.tsx @@ -2,11 +2,10 @@ import React from "react"; import KepzettsegSelector from "../display-components/KepzettsegSelector2E"; import { GetKepzettsegListaN, - GetNumberOfKepzettsegek, + GetNumberOfKepzettsegek, GetTolvajKepzettsegListaN, Kepzettseg, Kepzettsegek, KepzettsegId, - TolvajKepzettsegList, } from "../domain-models/kepzettsegek2E"; import {arraySetN} from "../../util"; import {useDispatch, useSelector} from "react-redux"; @@ -79,13 +78,6 @@ function KarakterKepzettsegek () { const changeKepzettsegek = (k: KepzettsegId[]) => dispatch(setKepzettsegek(k)) const changeTolvajKepzettsegek = (k: KepzettsegId[]) => dispatch(setTolvajKepzettsegek(k)) - // TODO: Birodalmi tolvajnal az Alikmia, vagy Meregkeveres kezelese - - const getTolvajKepzettsegListaN = (n: number) : Kepzettseg[] => { - const tolvajKepzettsegekWithoutN = [...tolvajKepzettsegek.slice(0, n), ...tolvajKepzettsegek.slice(n+1)] - return TolvajKepzettsegList.filter(x => !kepzettsegek.includes(x.Id) && !tolvajKepzettsegekWithoutN.includes(x.Id)) - } - return <> 0 && void, changeRolledHP: (rolledHP: number) => void, changeKalozKritikus: (krit: string) => void, - changeOsztaly: (osztaly: Osztaly2E) => void + changeOsztaly: (osztaly: Osztaly2E) => void, + changeExtraTolvajKepzettseg: (kepzettseg: KepzettsegId) => void, }) { const { szint, @@ -27,14 +30,16 @@ function LevelUp(props: { changeTulajdonsagNoveles, changeRolledHP, changeKalozKritikus, - changeOsztaly + changeOsztaly, + changeExtraTolvajKepzettseg } = props const classLevels = getClassLevels(karakter.szintlepesek) const harcosSzint = classLevels[Osztaly2E.Harcos] const kalozSzint = classLevels[Osztaly2E.Tengeresz] - const krit = karakter.szintlepesek[szint-1].kalozKritikus + const tolvajSzint = classLevels[Osztaly2E.Tolvaj] + const szintlepes = karakter.szintlepesek[szint-1]; return
@@ -68,12 +73,25 @@ function LevelUp(props: { } {osztaly === Osztaly2E.Tengeresz && kalozSzint % 3 === 0 && x.kalozKritikus)} changeKrit={changeKalozKritikus} szint={kalozSzint} /> } + {osztaly === Osztaly2E.Tolvaj + && (tolvajSzint === 5 || tolvajSzint === 9) + && szintlepes.tolvajExtraKepzettseg != null && +
+ + +
+ }
} @@ -109,6 +127,18 @@ export function LevelUps(props: {karakter: Karakter2E, changeKarakter: (inputs: }; } + const changeExtraTolvajKepzettseg = (szint: number) => { + return (k: KepzettsegId) => { + changeKarakter({ + ...karakter, + szintlepesek: arraySetN(karakter.szintlepesek, szint-1, { + ...karakter.szintlepesek[szint-1], + tolvajExtraKepzettseg: k, + }) + }) + } + } + const levels = karakter.szintlepesek.map((_, i) => i+1); return <> @@ -122,6 +152,7 @@ export function LevelUps(props: {karakter: Karakter2E, changeKarakter: (inputs: changeRolledHP={changeRolledHPAtSzint(szint)} changeKalozKritikus={changeKalozKritikusAtSzint(szint)} changeOsztaly={(o) => changeKarakter(ChangeOsztalyAtSzint(karakter, o, szint))} + changeExtraTolvajKepzettseg={changeExtraTolvajKepzettseg(szint)} /> )} diff --git a/frontend/src/second-edition/domain-models/kepzettsegek2E.test.ts b/frontend/src/second-edition/domain-models/kepzettsegek2E.test.ts new file mode 100644 index 0000000..1519d0c --- /dev/null +++ b/frontend/src/second-edition/domain-models/kepzettsegek2E.test.ts @@ -0,0 +1,123 @@ +import {GetAvailableKepzettsegek, GetNumberOfKepzettsegek, Kepzettsegek, KepzettsegLista} from "./kepzettsegek2E"; +import {Faj2E} from "./faj2E"; +import {DefaultKarakter} from "./karakter2E"; +import {Osztaly2E} from "./osztaly2E"; +import {Tulajdonsag2E} from "./tulajdonsag2E"; + +describe("Kepzettsegek 2E", () => { + describe("GetNumberOfKepzettsegek", () => { + test("Add extra kepzettseg for Ember", () => { + const actual = GetNumberOfKepzettsegek(18, Faj2E.Ember) + expect(actual).toEqual(7) + }) + test("should be min for extra stupid", () => { + const actual = GetNumberOfKepzettsegek(3, Faj2E.Osember) + expect(actual).toEqual(1) + }) + test("never less than 1", () => { + const actual = GetNumberOfKepzettsegek(-999, Faj2E.Osember) + expect(actual).toEqual(1) + }) + test("never more than 11", () => { + const actual = GetNumberOfKepzettsegek(999, Faj2E.Elf) + expect(actual).toEqual(11) + }) + }) + + describe("GetAvailableKepzettsegek", () => { + test("excludes existing karakter kepzettsegek", () => { + const karakter = { + ...DefaultKarakter, + kepzettsegek: KepzettsegLista.map(x => x.Id), + } + const actual = GetAvailableKepzettsegek(karakter) + expect(actual).toHaveLength(0) + }) + test("includes Birodalmi kepzettsegek for a Birodalmi", () => { + const karakter = { + ...DefaultKarakter, + faj: Faj2E.Birodalmi, + } + const actual = GetAvailableKepzettsegek(karakter) + expect(actual).toContain(Kepzettsegek.k_meregkeveres) + expect(actual).toContain(Kepzettsegek.k_alkimia) + expect(actual).toContain(Kepzettsegek.k_okkultizmus) + }) + test.each([ + [Osztaly2E.Varazslo], + [Osztaly2E.Illuzionista], + [Osztaly2E.Pap], + [Osztaly2E.Druida], + [Osztaly2E.Vandor], + ])("includes arcane kepzettsegek for %s", (osztaly) => { + const karakter = { + ...DefaultKarakter, + szintlepesek: [ + { osztaly, HProll: 4, }, + ] + } + const actual = GetAvailableKepzettsegek(karakter) + expect(actual).toContain(Kepzettsegek.k_csillagjoslas) + }) + describe("when Tolvaj", () => { + test("includes unpicked Tolvaj kepzettsegek", () => { + const karakter = { + ...DefaultKarakter, + szintlepesek: [ + { osztaly: Osztaly2E.Tolvaj, HProll: 6, } + ] + } + const actual = GetAvailableKepzettsegek(karakter) + expect(actual).toContain(Kepzettsegek.k_meregkeveres) + }) + test.each([ + [Kepzettsegek.k_meregkeveres], + [Kepzettsegek.k_alcazas], + [Kepzettsegek.k_csapdak], + [Kepzettsegek.k_szabadulomuveszet], + [Kepzettsegek.k_hamisitas], + ])("excludes already picked Tolvaj kepzettsegek", (kepzettseg) => { + const karakter = { + ...DefaultKarakter, + szintlepesek: [ + { osztaly: Osztaly2E.Tolvaj, HProll: 6, } + ], + tolvajKepzettsegek: [kepzettseg.Id] + } + const actual = GetAvailableKepzettsegek(karakter) + expect(actual).not.toContain(kepzettseg) + }) + test("excludes extra high level Tolvaj kepzettsegek", () => { + const karakter = { + ...DefaultKarakter, + szintlepesek: [ + { osztaly: Osztaly2E.Tolvaj, HProll: 6, }, + { osztaly: Osztaly2E.Tolvaj, HProll: 6, }, + { osztaly: Osztaly2E.Tolvaj, HProll: 6, }, + { osztaly: Osztaly2E.Tolvaj, HProll: 6, tulajdonsagNoveles: Tulajdonsag2E.Ugyesseg }, + { + osztaly: Osztaly2E.Tolvaj, + HProll: 6, + tolvajExtraKepzettseg: Kepzettsegek.k_meregkeveres.Id + }, + ] + } + const actual = GetAvailableKepzettsegek(karakter) + expect(actual).not.toContain(Kepzettsegek.k_meregkeveres) + }) + }) + test("multiclass characters get access to kepzettsegek from all classes", () => { + const karakter = { + ...DefaultKarakter, + szintlepesek: [ + { osztaly: Osztaly2E.Illuzionista, HProll: 4, }, + { osztaly: Osztaly2E.Tolvaj, HProll: 4, }, + ] + } + const actual = GetAvailableKepzettsegek(karakter) + expect(actual).toContain(Kepzettsegek.k_csillagjoslas) + expect(actual).toContain(Kepzettsegek.k_meregkeveres) + }) + }) + +}) \ No newline at end of file diff --git a/frontend/src/second-edition/domain-models/kepzettsegek2E.ts b/frontend/src/second-edition/domain-models/kepzettsegek2E.ts index 912275b..31b7ce5 100644 --- a/frontend/src/second-edition/domain-models/kepzettsegek2E.ts +++ b/frontend/src/second-edition/domain-models/kepzettsegek2E.ts @@ -1,7 +1,7 @@ import {Osztaly2E} from "./osztaly2E"; -import {Faj2E, TulajdonsagokFajjal} from "./faj2E"; +import {Faj2E} from "./faj2E"; import {Karakter2E} from "./karakter2E"; -import {Modifier, Tulajdonsag2E} from "./tulajdonsag2E"; +import {Modifier, Tulajdonsag2E, TulajdonsagokTotal} from "./tulajdonsag2E"; export type KepzettsegId = 'k_alkimia' | 'k_alcazas' | 'k_allatidomitas' | 'k_csapdak' | 'k_csillagjoslas' | 'k_egyensulyozas' | 'k_eloadas' | 'k_ertekbecsles' | 'k_gyogyitas' | 'k_hajozas' | 'k_hallgatozas' | 'k_hamisitas' | 'k_herbalizmus' | 'k_historia' | 'k_jelek_olvasasa' | 'k_lovaglas' | 'k_maszas' | 'k_megfigyeles' | 'k_meregkeveres' | 'k_mesterseg' | 'k_nyomkereses' | 'k_okkultizmus' | 'k_osonas' | 'k_szabadulomuveszet' | 'k_ugras' | 'k_uszas' | 'k_vadonjaras' | 'k_zarnyitas' | 'k_zsebmetszes' @@ -209,11 +209,7 @@ export const Kepzettsegek: Record = (acc: Record, k) => { acc[k.Id] = k; return acc; }, {} as Record); -export function AvailableKezpettsegList(...osztalyok: Osztaly2E[]): Kepzettseg[] { - return KepzettsegLista.filter(k => k.Osztalyok == null || k.Osztalyok.some(o => osztalyok.includes(o))) -} - -export function AvailableKezpettsegListFajjal(faj: Faj2E, ...osztalyok: Osztaly2E[]): Kepzettseg[] { +function availableKezpettsegListFajjal(faj: Faj2E, ...osztalyok: Osztaly2E[]): Kepzettseg[] { const birodalmiKepzettsegek : KepzettsegId[] = ["k_alkimia", "k_meregkeveres", "k_okkultizmus"] return KepzettsegLista.filter(k => k.Osztalyok == null || k.Osztalyok.some(o => osztalyok.includes(o)) @@ -222,7 +218,7 @@ export function AvailableKezpettsegListFajjal(faj: Faj2E, ...osztalyok: Osztaly2 } export function GetNumberOfKepzettsegek(t_int: number, faj: Faj2E, max: number = 11) { - //max = 3 + 1 + 3 + 5 = 12 // 3 base, 1 human, 3 from max ability modifier, 5 from Thief at 9th level + //max = 3 + 1 + 3 + 5 = 12 // 3 base, 1 human, 3 from max ability modifier, 4 from Thief +1 at 9th level let numberOfKepzettseg = 3 + Modifier(t_int) + (faj === Faj2E.Ember ? 1 : 0) if (numberOfKepzettseg < 1) { @@ -236,6 +232,21 @@ export function GetNumberOfKepzettsegek(t_int: number, faj: Faj2E, max: number = return numberOfKepzettseg; } +export function GetAvailableKepzettsegek(karakter: Pick): Kepzettseg[] { + const osztalyok = new Set(karakter.szintlepesek.map(x => x.osztaly)) + const availableKepzettsegek = availableKezpettsegListFajjal(karakter.faj, ...osztalyok) + .filter(k => !karakter.kepzettsegek.includes(k.Id)) + + if (!osztalyok.has(Osztaly2E.Tolvaj)) { + return availableKepzettsegek; + } + + return availableKepzettsegek.filter(k => + !karakter.tolvajKepzettsegek?.includes(k.Id) + && !karakter.szintlepesek.some(sz => + sz.tolvajExtraKepzettseg === k.Id && sz.osztaly === Osztaly2E.Tolvaj)) +} + export function SetDefaultTolvajKepzettsegek(karakter: Pick, changeTolvajKepzettsegek: (tolvajKepzettsegek?: KepzettsegId[]) => void) { const hasTolvajSzint = karakter.szintlepesek.some(szint => szint.osztaly === Osztaly2E.Tolvaj) if (!hasTolvajSzint && karakter.tolvajKepzettsegek != null) { @@ -247,79 +258,47 @@ export function SetDefaultTolvajKepzettsegek(karakter: Pick, n: number): Kepzettseg | undefined { - const osztalyok = new Set(karakter.szintlepesek.map(x => x.osztaly)) - let availableKepzettsegList = AvailableKezpettsegListFajjal(karakter.faj, ...osztalyok) - const kepzettsegekWithoutN = [...karakter.kepzettsegek.slice(0, n), ...karakter.kepzettsegek.slice(n + 1)] - return availableKepzettsegList.find(x => !kepzettsegekWithoutN.includes(x.Id) && !karakter.tolvajKepzettsegek?.includes(x.Id)) +export function GetKepzettsegListaN(karakter: Pick) { + return (n: number): Kepzettseg[] => { + if (karakter.kepzettsegek.length > n){ + return [Kepzettsegek[karakter.kepzettsegek[n]], ...GetAvailableKepzettsegek(karakter)] + } + else { + return GetAvailableKepzettsegek(karakter); + } + } } -export function GetKepzettsegListaN(karakter: Pick) { - const osztalyok = new Set(karakter.szintlepesek.map(x => x.osztaly)) - let availableKepzettsegList = AvailableKezpettsegListFajjal(karakter.faj, ...osztalyok) +export function GetTolvajKepzettsegListaN(karakter: Pick) { return (n: number): Kepzettseg[] => { - let available = [...availableKepzettsegList] - // if (n === 0) { - // if (karakter.faj === Faj2E.Eszaki) { - // return [Kepzettsegek.k_hajozas] - // } - // if (karakter.faj === Faj2E.Nomad) { - // return [Kepzettsegek.k_lovaglas] - // } - // // TODO birodalmi tolvaj, tolvajképzettségek! - // if (karakter.faj === Faj2E.Birodalmi) { - // return [Kepzettsegek.k_alkimia, Kepzettsegek.k_meregkeveres, Kepzettsegek.k_okkultizmus] - // } - // if (karakter.faj === Faj2E.Torpe) { - // return [Kepzettsegek.k_ertekbecsles] - // } - // if (karakter.faj === Faj2E.Elf || karakter.faj === Faj2E.Felelf) { - // return [Kepzettsegek.k_vadonjaras] - // } - // if (osztalyok.has(Osztaly2E.Druida) || osztalyok.has(Osztaly2E.Vandor)) { - // return [Kepzettsegek.k_vadonjaras] - // } - // if (osztalyok.has(Osztaly2E.Dalnok)) { - // return [Kepzettsegek.k_eloadas] - // } - // } - // if (n === 1) { - // if ((osztalyok.has(Osztaly2E.Vandor) || osztalyok.has(Osztaly2E.Druida)) - // && karakter.kepzettsegek[0] !== Kepzettsegek.k_vadonjaras.Id) { - // return [Kepzettsegek.k_vadonjaras] - // } - // if (osztalyok.has(Osztaly2E.Dalnok) - // && karakter.kepzettsegek[0] !== Kepzettsegek.k_eloadas.Id) { - // return [Kepzettsegek.k_eloadas] - // } - // } - // if (n === 2) { // maybe write a better abstraction? - // if (osztalyok.has(Osztaly2E.Dalnok) - // && karakter.kepzettsegek[0] !== Kepzettsegek.k_eloadas.Id - // && karakter.kepzettsegek[1] !== Kepzettsegek.k_eloadas.Id) { - // return [Kepzettsegek.k_eloadas] - // } - // } - const kepzettsegekWithoutN = [...karakter.kepzettsegek.slice(0, n), ...karakter.kepzettsegek.slice(n + 1)] - return available.filter(x => !kepzettsegekWithoutN.includes(x.Id) && !karakter.tolvajKepzettsegek?.includes(x.Id)) + if (karakter.tolvajKepzettsegek != null && karakter.tolvajKepzettsegek.length > n){ + return [Kepzettsegek[karakter.tolvajKepzettsegek[n]], ...GetAvailableKepzettsegek(karakter)] + } + else { + return GetAvailableKepzettsegek(karakter); + } } } export function AdjustKepzettsegek(karakter: Pick): KepzettsegId[] { - const tulajdonsagokFajjal = TulajdonsagokFajjal(karakter.tulajdonsagok, karakter.faj) - const numberOfKepzettsegek = GetNumberOfKepzettsegek(tulajdonsagokFajjal.t_int, karakter.faj) + const tul = TulajdonsagokTotal(karakter) + const numberOfKepzettsegek = GetNumberOfKepzettsegek(tul.t_int, karakter.faj) const diff = numberOfKepzettsegek - karakter.kepzettsegek.length if (diff > 0) { const kepzettsegek = [...karakter.kepzettsegek] for (let i = 0; i < diff; i++) { - const next = GetKepzettsegAtN({ + const next = GetAvailableKepzettsegek({ ...karakter, kepzettsegek, - }, kepzettsegek.length + 1)!.Id; + })[0].Id; kepzettsegek.push(next) } return kepzettsegek; - } else { + } + else if (diff === 0) { + return karakter.kepzettsegek + } + else { return karakter.kepzettsegek.slice(0, numberOfKepzettsegek); } } \ No newline at end of file diff --git a/frontend/src/second-edition/domain-models/mentok.test.ts b/frontend/src/second-edition/domain-models/mentok.test.ts index 83c343b..4458418 100644 --- a/frontend/src/second-edition/domain-models/mentok.test.ts +++ b/frontend/src/second-edition/domain-models/mentok.test.ts @@ -2,7 +2,7 @@ import {ElsodlegesMento, MasodlagosMento, MentokTotal, MultiClassMentokAlap} fro import {Osztaly2E} from "./osztaly2E"; import {Szintlepes} from "./szintlepes"; -describe("mentok", () => { +describe("mentok 2E", () => { test.each([ [1, 2], [2, 3], diff --git a/frontend/src/second-edition/domain-models/szintlepes.ts b/frontend/src/second-edition/domain-models/szintlepes.ts index 0d604ee..61c3030 100644 --- a/frontend/src/second-edition/domain-models/szintlepes.ts +++ b/frontend/src/second-edition/domain-models/szintlepes.ts @@ -1,5 +1,5 @@ import {Tulajdonsag2E, TulajdonsagIDs, TulajdonsagokTotal} from "./tulajdonsag2E"; -import {KepzettsegId} from "./kepzettsegek2E"; +import {GetAvailableKepzettsegek, KepzettsegId} from "./kepzettsegek2E"; import {Osztaly2E} from "./osztaly2E"; import {Karakter2E} from "./karakter2E"; import {BaseHP} from "./masodlagos_ertekek"; @@ -23,15 +23,15 @@ export function PickableSpecializationFilter(existingSpecilizations: (string | u } export function LevelUp(karakter: Karakter2E, changeKarakter: (input: Karakter2E) => void) { - const osztaly = karakter.szintlepesek[0].osztaly + const autoPickedOsztaly = karakter.szintlepesek[karakter.szintlepesek.length - 1].osztaly const tulajdonsagokTotal = TulajdonsagokTotal(karakter) const szint = karakter.szint + 1 - const dice = BaseHP(osztaly) + const dice = BaseHP(autoPickedOsztaly) const roll = dAny(dice) console.log(`Level ${szint} HP roll on d${dice} is: ${roll}`) const szintlepes: Szintlepes = { - osztaly: osztaly, + osztaly: autoPickedOsztaly, HProll: roll } @@ -43,16 +43,19 @@ export function LevelUp(karakter: Karakter2E, changeKarakter: (input: Karakter2E } } } - if(osztaly === Osztaly2E.Harcos) { - const harcosSzint = karakter.szintlepesek.filter(x => x.osztaly === Osztaly2E.Harcos).length + 1 + + if (autoPickedOsztaly === Osztaly2E.Harcos){ + const harcosSzint = 1 + karakter.szintlepesek.filter(x => x.osztaly === Osztaly2E.Harcos).length if (harcosSzint % 2 === 1) { const harcosSpecializaciok = karakter.szintlepesek.map(x => x.harcosFegyver) let specializationFilter = PickableSpecializationFilter(harcosSpecializaciok, harcosSzint) szintlepes.harcosFegyver = AllowedFegyver(Osztaly2E.Harcos).filter(specializationFilter)[0].Id } } - if(osztaly === Osztaly2E.Tengeresz) { - const kalozSzint = karakter.szintlepesek.filter(x => x.osztaly === Osztaly2E.Tengeresz).length + 1 + + if(autoPickedOsztaly === Osztaly2E.Tengeresz) + { + const kalozSzint = 1 + karakter.szintlepesek.filter(x => x.osztaly === Osztaly2E.Tengeresz).length if (kalozSzint % 3 === 0) { let kalozKritikus = karakter.szintlepesek.map(x => x.kalozKritikus) let specializationFilter = PickableSpecializationFilter(kalozKritikus, kalozSzint) @@ -60,6 +63,14 @@ export function LevelUp(karakter: Karakter2E, changeKarakter: (input: Karakter2E } } + if (autoPickedOsztaly === Osztaly2E.Tolvaj){ + const tolvajSzint = 1 + karakter.szintlepesek.filter(x => x.osztaly === Osztaly2E.Tolvaj).length + if (tolvajSzint === 5 || tolvajSzint === 9) { + const availableKepzettsegek = GetAvailableKepzettsegek(karakter) + szintlepes.tolvajExtraKepzettseg = availableKepzettsegek[0].Id; + } + } + changeKarakter({...karakter, szint, szintlepesek: [...karakter.szintlepesek, szintlepes]}) }