mirror of
https://github.com/morbalint/kemkas.git
synced 2026-07-18 11:23:47 +00:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b50a54f92b | |||
| 9476c1f44c | |||
| 35b12524ab | |||
| 2fe8fb05e7 | |||
| 540c665a14 | |||
| 8f32daf337 |
@@ -11,14 +11,14 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node.js 18.x
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18.x
|
||||
- name: Cache NPM packages
|
||||
uses: actions/cache@v3
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
key: npm-${{ hashFiles('./Kemkas.Web/frontend/yarn.lock') }}
|
||||
path: |
|
||||
@@ -40,7 +40,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: docker/setup-buildx-action@v3
|
||||
- name: Install doctl
|
||||
uses: digitalocean/action-doctl@v2
|
||||
@@ -75,7 +75,7 @@ jobs:
|
||||
- docker-build
|
||||
if: ${{ github.event_name == 'push' || ( github.event_name == 'pull_request' && github.event.action == 'closed' && github.event.pull_request.merged ) }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install doctl
|
||||
uses: digitalocean/action-doctl@v2
|
||||
with:
|
||||
|
||||
@@ -49,6 +49,7 @@ public static class WebApplicationBuilderExtensions
|
||||
{
|
||||
var otlResourceBuilder = ResourceBuilder.CreateDefault().AddService("kemkas-api");
|
||||
var openTelemetryEndpoint = builder.Configuration.GetSection("monitoring")["url"];
|
||||
var consoleEnabled = builder.Configuration.GetSection("monitoring")["console"] == "true";
|
||||
|
||||
builder.Services.AddOpenTelemetry().WithTracing(tracerProviderBuilder =>
|
||||
{
|
||||
@@ -59,11 +60,10 @@ public static class WebApplicationBuilderExtensions
|
||||
{
|
||||
tracerProviderBuilder.AddOtlpExporter(opt => opt.Endpoint = new Uri(openTelemetryEndpoint));
|
||||
}
|
||||
else
|
||||
else if (consoleEnabled)
|
||||
{
|
||||
tracerProviderBuilder.AddConsoleExporter();
|
||||
}
|
||||
|
||||
})
|
||||
.WithMetrics(meterProviderBuilder =>
|
||||
{
|
||||
@@ -74,7 +74,7 @@ public static class WebApplicationBuilderExtensions
|
||||
{
|
||||
meterProviderBuilder.AddOtlpExporter(opt => opt.Endpoint = new Uri(openTelemetryEndpoint));
|
||||
}
|
||||
else
|
||||
else if (consoleEnabled)
|
||||
{
|
||||
meterProviderBuilder.AddConsoleExporter();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
import React from "react";
|
||||
import {Karakter2E} from "../domain-models/karakter2E";
|
||||
import {SignedNumberToText} from "../../shared/components/Helpers";
|
||||
import {Mentok, MentoTipus, MentoTipusLabel, MentokTotal} from "../domain-models/mentok";
|
||||
import {BaseHP, HP} from "../domain-models/masodlagos_ertekek";
|
||||
import {MultiClassTamadasBonusz} from "../domain-models/tamadas_bonusz";
|
||||
import {Osztaly2E} from "../domain-models/osztaly2E";
|
||||
|
||||
function MentoNovekedes(props : {tipus: MentoTipus, ujMentok: Mentok, regiMentok: Mentok}) {
|
||||
const {tipus, ujMentok, regiMentok} = props
|
||||
if (ujMentok[tipus] === regiMentok[tipus]) {
|
||||
return null
|
||||
}
|
||||
return <div className='row m-2'>
|
||||
<label className='col-sm-6 col-8 col-form-label'>{MentoTipusLabel(tipus)} mentő növekedés{' '}</label>
|
||||
<span className='col col-form-label'>{SignedNumberToText(ujMentok[tipus] - regiMentok[tipus])}</span>
|
||||
</div>;
|
||||
}
|
||||
|
||||
function UjMento(props : {tipus: MentoTipus, ujMentok: Mentok, regiMentok: Mentok}) {
|
||||
const {tipus, ujMentok, regiMentok} = props
|
||||
if (ujMentok[tipus] === regiMentok[tipus]) {
|
||||
return null
|
||||
}
|
||||
return <div className='row m-2'>
|
||||
<label className='col-sm-6 col-8 col-form-label'>{MentoTipusLabel(tipus)} mentő új értéke{' '}</label>
|
||||
<span className='col col-form-label'>{SignedNumberToText(ujMentok[tipus])}</span>
|
||||
</div>;
|
||||
}
|
||||
|
||||
function BasicNewLevel(props: {
|
||||
karakter: Pick<Karakter2E, 'tulajdonsagok' | 'szintlepesek'>
|
||||
szint: number
|
||||
osztaly: Osztaly2E
|
||||
changeRolledHP: (hp: number) => void,
|
||||
}) {
|
||||
const {karakter, szint, osztaly, changeRolledHP} = props
|
||||
const regiMentok = MentokTotal({...karakter, szintlepesek: karakter.szintlepesek.slice(0, szint - 1)})
|
||||
const ujMentok = MentokTotal({...karakter, szintlepesek: karakter.szintlepesek.slice(0, szint)})
|
||||
|
||||
const regiTB = MultiClassTamadasBonusz(karakter.szintlepesek.slice(0, szint-1))
|
||||
const ujTB = MultiClassTamadasBonusz(karakter.szintlepesek.slice(0, szint))
|
||||
if (regiTB.length < ujTB.length) {
|
||||
regiTB.push(0)
|
||||
}
|
||||
const TBdiff = ujTB.map((x, i) => x - regiTB[i])
|
||||
const isTBincreased = TBdiff.length > 0 && TBdiff.some(x => x > 0)
|
||||
|
||||
return <>
|
||||
<div className='row'>
|
||||
<div className='col-lg-6'>
|
||||
<div className='row m-2'>
|
||||
<label className='col-sm-6 col-8 col-form-label'>Plusz dobott HP (d{BaseHP(osztaly)}){' '}</label>
|
||||
<div className='col-sm-2 col'>
|
||||
<input
|
||||
className='form-control'
|
||||
maxLength={2}
|
||||
type='number'
|
||||
value={karakter.szintlepesek[szint-1].HProll || 1}
|
||||
min={1}
|
||||
max={12}
|
||||
onChange={e => changeRolledHP(Number(e.target.value))} />
|
||||
</div>
|
||||
</div>
|
||||
{ isTBincreased && <div className='row m-2'>
|
||||
<label className='col-sm-6 col-8 col-form-label'>Támadás bónusz növekedés{' '}</label>
|
||||
<span className='col col-form-label'>{TBdiff.map(SignedNumberToText).join(" / ")}</span>
|
||||
</div>}
|
||||
<MentoNovekedes tipus={'kitartas'} ujMentok={ujMentok} regiMentok={regiMentok} />
|
||||
<MentoNovekedes tipus={'reflex'} ujMentok={ujMentok} regiMentok={regiMentok} />
|
||||
<MentoNovekedes tipus={'akaratero'} ujMentok={ujMentok} regiMentok={regiMentok} />
|
||||
</div>
|
||||
<div className='col-lg-6'>
|
||||
<div className='row m-2'>
|
||||
<label className='col-sm-6 col-8 col-form-label'>Új HP{' '}</label>
|
||||
<span className='col col-form-label'>{HP({...karakter, szintlepesek: karakter.szintlepesek.slice(0, szint)})}</span>
|
||||
</div>
|
||||
{isTBincreased && <div className='row m-2'>
|
||||
<label className='col-sm-6 col-8 col-form-label'>Új Támadás bónusz{' '}</label>
|
||||
<span className='col col-form-label'>{ujTB.map(SignedNumberToText).join(" / ")}</span>
|
||||
</div>}
|
||||
<UjMento tipus={'kitartas'} ujMentok={ujMentok} regiMentok={regiMentok} />
|
||||
<UjMento tipus={'reflex'} ujMentok={ujMentok} regiMentok={regiMentok} />
|
||||
<UjMento tipus={'akaratero'} ujMentok={ujMentok} regiMentok={regiMentok} />
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
|
||||
export default BasicNewLevel
|
||||
@@ -0,0 +1,89 @@
|
||||
import React from "react";
|
||||
import {Fegyver} from "../domain-models/felszereles";
|
||||
|
||||
export function FegyverLabel(fegyver: Fegyver): string {
|
||||
const flags = [] as string[]
|
||||
if (fegyver.Megterheltseg) {
|
||||
flags.push("M")
|
||||
}
|
||||
if (fegyver.Harcos) {
|
||||
flags.push("H")
|
||||
}
|
||||
if (fegyver.Masfelkezes) {
|
||||
flags.push("1½K")
|
||||
}
|
||||
if (fegyver.Ketkezes) {
|
||||
if (fegyver.Type === "kozelharci") {
|
||||
flags.push("2K")
|
||||
} else {
|
||||
if (fegyver.KetkezesBonusz) {
|
||||
flags.push("2KB")
|
||||
} else {
|
||||
flags.push("2KN")
|
||||
}
|
||||
}
|
||||
}
|
||||
if (fegyver.HarciManover) {
|
||||
flags.push("+2 HM")
|
||||
}
|
||||
if (fegyver.Alakzat){
|
||||
flags.push("AL")
|
||||
}
|
||||
if (fegyver.Hosszu) {
|
||||
flags.push("HO")
|
||||
}
|
||||
if (fegyver.Kabito) {
|
||||
flags.push("KO")
|
||||
}
|
||||
if (fegyver.LovasRoham) {
|
||||
flags.push("LR")
|
||||
}
|
||||
if (fegyver.PancelToro) {
|
||||
flags.push("PT")
|
||||
}
|
||||
if (fegyver.PajzsZuzo) {
|
||||
flags.push("PZ")
|
||||
}
|
||||
if (fegyver.Rohamtoro) {
|
||||
flags.push("RT")
|
||||
}
|
||||
// Replaced by new group "dobhato"
|
||||
// if (fegyver.EroBonusz) {
|
||||
// flags.push("ER")
|
||||
// }
|
||||
if (fegyver.Megjegyzes != null && fegyver.Megjegyzes.length > 0) {
|
||||
flags.push(fegyver.Megjegyzes)
|
||||
}
|
||||
const serializedFlags = flags.reduce((p,c) => `${p}${c} `, '');
|
||||
const sizeLabel = fegyver.Size < 1 && fegyver.Size > 0 ? ` [1/${Math.round(1 / fegyver.Size)}]` : (fegyver.Size > 1 ? ` [${fegyver.Size}]` : '');
|
||||
return `${fegyver.Name}${sizeLabel} ${serializedFlags.length > 0 ? "| " + serializedFlags : ''}| ${
|
||||
fegyver.DamageMultiplier > 1 ? `${fegyver.DamageMultiplier}*` : ''}${fegyver.NumberOfDamageDice}d${fegyver.DamageDice}${fegyver.DamageBonus > 0 ? ` +${fegyver.DamageBonus}` : ''} ${
|
||||
fegyver.CritRangeStart < 20 ? `${fegyver.CritRangeStart}-20` : ''}x${fegyver.CritMultiplier}${
|
||||
fegyver.Price > 0 ? ` | ár: ${fegyver.Price} at ` : ''}${
|
||||
fegyver.Range > 10 ? ` | táv: ${fegyver.Range}` : ''}`
|
||||
}
|
||||
|
||||
function FegyverSelector(props: {fegyverek: Fegyver[], selectedId: string, onChange: (id: string) => void}) {
|
||||
|
||||
const { fegyverek, selectedId, onChange } = props
|
||||
|
||||
return <>
|
||||
<select className='col form-select' value={selectedId}
|
||||
onChange={e => onChange(e.target.value)}>
|
||||
<optgroup label="Közelharci">
|
||||
{fegyverek.filter(f => f.Type === 'kozelharci').map(f => <option
|
||||
key={f.Id} value={f.Id}>{FegyverLabel(f)}</option>)}
|
||||
</optgroup>
|
||||
<optgroup label="Közelharci, dobható">
|
||||
{fegyverek.filter(f => f.Type === 'dobhato').map(f => <option
|
||||
key={f.Id} value={f.Id}>{FegyverLabel(f)}</option>)}
|
||||
</optgroup>
|
||||
<optgroup label="Távolsági">
|
||||
{fegyverek.filter(f => f.Type === 'lofegyver').map(f => <option
|
||||
key={f.Id} value={f.Id}>{FegyverLabel(f)}</option>)}
|
||||
</optgroup>
|
||||
</select>
|
||||
</>
|
||||
}
|
||||
|
||||
export default FegyverSelector
|
||||
@@ -5,64 +5,7 @@ import {AllowedPajzsTypes, AllowedPancelTypes} from '../domain-models/allowed-pa
|
||||
import fegyverek from '../domain-models/fegyver.json'
|
||||
import {arraySetN} from "../../util";
|
||||
import {AllowedFegyver} from "../domain-models/allowed-fegyver";
|
||||
|
||||
export function FegyverLabel(fegyver: Fegyver): string {
|
||||
const flags = [] as string[]
|
||||
if (fegyver.Harcos) {
|
||||
flags.push("H")
|
||||
}
|
||||
if (fegyver.Masfelkezes) {
|
||||
flags.push("1½K")
|
||||
}
|
||||
if (fegyver.Ketkezes) {
|
||||
if (fegyver.Type === "kozelharci") {
|
||||
flags.push("2K")
|
||||
} else {
|
||||
if (fegyver.KetkezesBonusz) {
|
||||
flags.push("2KB")
|
||||
} else {
|
||||
flags.push("2KN")
|
||||
}
|
||||
}
|
||||
}
|
||||
if (fegyver.HarciManover) {
|
||||
flags.push("+2 HM")
|
||||
}
|
||||
if (fegyver.Alakzat){
|
||||
flags.push("AL")
|
||||
}
|
||||
if (fegyver.Hosszu) {
|
||||
flags.push("HO")
|
||||
}
|
||||
if (fegyver.Kabito) {
|
||||
flags.push("KO")
|
||||
}
|
||||
if (fegyver.LovasRoham) {
|
||||
flags.push("LR")
|
||||
}
|
||||
if (fegyver.PancelToro) {
|
||||
flags.push("PT")
|
||||
}
|
||||
if (fegyver.PajzsZuzo) {
|
||||
flags.push("PZ")
|
||||
}
|
||||
if (fegyver.Rohamtoro) {
|
||||
flags.push("RT")
|
||||
}
|
||||
// if (fegyver.EroBonusz) {
|
||||
// flags.push("ER")
|
||||
// }
|
||||
if (fegyver.Megjegyzes != null && fegyver.Megjegyzes.length > 0) {
|
||||
flags.push(fegyver.Megjegyzes)
|
||||
}
|
||||
const serializedFlags = flags.reduce((p,c) => `${p}${c} `, '');
|
||||
const sizeLabel = fegyver.Size < 1 && fegyver.Size > 0 ? ` [1/${Math.round(1 / fegyver.Size)}]` : (fegyver.Size > 1 ? ` [${fegyver.Size}]` : '');
|
||||
return `${fegyver.Name}${sizeLabel} ${serializedFlags.length > 0 ? "| " + serializedFlags : ''}| ${
|
||||
fegyver.DamageMultiplier > 1 ? `${fegyver.DamageMultiplier}*` : ''}${fegyver.NumberOfDamageDice}d${fegyver.DamageDice}${fegyver.DamageBonus > 0 ? ` +${fegyver.DamageBonus}` : ''} ${
|
||||
fegyver.CritRangeStart < 20 ? `${fegyver.CritRangeStart}-20` : ''}x${fegyver.CritMultiplier}${
|
||||
fegyver.Price > 0 ? ` | ár: ${fegyver.Price} at ` : ''}${
|
||||
fegyver.Range > 10 ? ` | táv: ${fegyver.Range}` : ''}`
|
||||
}
|
||||
import FegyverSelector from "./FegyverSelector";
|
||||
|
||||
function Felszereles(props: {felszereles: KarakterFelszereles, changeFelszereles: (felszereles: KarakterFelszereles) => void, osztaly: Osztaly2E}) {
|
||||
const { felszereles, changeFelszereles, osztaly } = props;
|
||||
@@ -116,21 +59,7 @@ function Felszereles(props: {felszereles: KarakterFelszereles, changeFelszereles
|
||||
{valasztottFegyverek.map(({idx, fegyver}) => (
|
||||
<div key={`fegyver-${idx}`} className='row m-2'>
|
||||
<label className='col-md-2 col-sm-3 col-form-label'>Fegyver</label>
|
||||
<select className='col form-select' value={fegyver.Id}
|
||||
onChange={e => changeFegyver(idx, e.target.value)}>
|
||||
<optgroup label="Közelharci">
|
||||
{allowedFegyverek.filter(f => f.Type === 'kozelharci').map(f => <option
|
||||
key={f.Id} value={f.Id}>{FegyverLabel(f)}</option>)}
|
||||
</optgroup>
|
||||
<optgroup label="Közelharci, dobható">
|
||||
{allowedFegyverek.filter(f => f.Type === 'dobhato').map(f => <option
|
||||
key={f.Id} value={f.Id}>{FegyverLabel(f)}</option>)}
|
||||
</optgroup>
|
||||
<optgroup label="Távolsági">
|
||||
{allowedFegyverek.filter(f => f.Type === 'lofegyver').map(f => <option
|
||||
key={f.Id} value={f.Id}>{FegyverLabel(f)}</option>)}
|
||||
</optgroup>
|
||||
</select>
|
||||
<FegyverSelector fegyverek={allowedFegyverek} selectedId={fegyver.Id} onChange={(fegyverId) => changeFegyver(idx, fegyverId)} />
|
||||
<button className='col-md-2 col-sm-3 btn btn-outline-dark btn-lg ms-2' type='button'
|
||||
onClick={() => changeFelszereles({
|
||||
...felszereles,
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
import React from "react";
|
||||
import {GetFegyver} from "../domain-models/felszereles";
|
||||
import FegyverSelector from "./FegyverSelector";
|
||||
import {AllowedFegyver} from "../domain-models/allowed-fegyver";
|
||||
import {Osztaly2E} from "../domain-models/osztaly2E";
|
||||
|
||||
function HarcosFegyverSpecializacio(props: {
|
||||
specialization: string,
|
||||
existingSpecializations: (string | undefined)[],
|
||||
changeSpecialization: (specialization: string) => void
|
||||
szint: number
|
||||
}) {
|
||||
const {
|
||||
specialization,
|
||||
existingSpecializations,
|
||||
changeSpecialization,
|
||||
szint
|
||||
} = props
|
||||
const fegyver = GetFegyver(specialization)
|
||||
const availableFegyverek = AllowedFegyver(Osztaly2E.Harcos)
|
||||
const pickableFegyverek = availableFegyverek.filter(f => f.Id === specialization || existingSpecializations.filter(x => x === f.Id).length < (szint < 9 ? 1 : 2))
|
||||
|
||||
return <>
|
||||
<div className='row m-2'>
|
||||
<label className='col-md-2 col-sm-3 col-form-label' >Fegyver specializáció</label>
|
||||
<FegyverSelector fegyverek={pickableFegyverek} selectedId={specialization} onChange={changeSpecialization} />
|
||||
{fegyver == null && <span className='form-field-error'>Ismeretlen fegyver: {specialization}</span>}
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
|
||||
export default HarcosFegyverSpecializacio
|
||||
@@ -0,0 +1,28 @@
|
||||
import React from "react";
|
||||
import {Osztaly2E} from "../domain-models/osztaly2E";
|
||||
import {AllowedFegyver} from "../domain-models/allowed-fegyver";
|
||||
import FegyverSelector from "./FegyverSelector";
|
||||
|
||||
function KalozKritikus(props: {
|
||||
kritFegyverId: string,
|
||||
existingKrits: (string | undefined)[],
|
||||
changeKrit: (krit: string) => void,
|
||||
szint: number
|
||||
}) {
|
||||
const {
|
||||
kritFegyverId,
|
||||
existingKrits,
|
||||
changeKrit,
|
||||
szint
|
||||
} = props
|
||||
const fegyverek = AllowedFegyver(Osztaly2E.Tengeresz).filter(f => f.Id === kritFegyverId || existingKrits.filter(x => x === f.Id).length < (szint < 9 ? 1 : 2))
|
||||
|
||||
return <>
|
||||
<div className='row m-2'>
|
||||
<label className='col-md-2 col-sm-3 col-form-label'>Kritikus</label>
|
||||
<FegyverSelector fegyverek={fegyverek} selectedId={kritFegyverId} onChange={changeKrit} />
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
|
||||
export default KalozKritikus
|
||||
@@ -0,0 +1,147 @@
|
||||
import React from "react";
|
||||
import {ChangeOsztalyAtSzint, Karakter2E} from "../domain-models/karakter2E";
|
||||
import BasicNewLevel from "./BasicNewLevel";
|
||||
import TulajdonsagNoveles from "./TulajdonsagNoveles";
|
||||
import {Tulajdonsag2E, TulajdonsagokTotal} from "../domain-models/tulajdonsag2E";
|
||||
import {arraySetN} from "../../util";
|
||||
import {Osztaly2E} from "../domain-models/osztaly2E";
|
||||
import KalozKritikus from "./KalozKritikus";
|
||||
import HarcosFegyverSpecializacio from "./HarcosFegyverSpec";
|
||||
|
||||
import MultiClassOsztalySelector2E from "./MultiClassOsztalySelector2E";
|
||||
import {getClassLevels} from "../domain-models/szintlepes";
|
||||
|
||||
function LevelUp(props: {
|
||||
szint: number,
|
||||
osztaly: Osztaly2E,
|
||||
karakter: Karakter2E,
|
||||
changeTulajdonsagNoveles: (t: Tulajdonsag2E) => void,
|
||||
changeRolledHP: (rolledHP: number) => void,
|
||||
changeHarcosSpec: (spec: string) => void,
|
||||
changeKalozKritikus: (krit: string) => void,
|
||||
changeOsztaly: (osztaly: Osztaly2E) => void
|
||||
}) {
|
||||
const {
|
||||
szint,
|
||||
osztaly,
|
||||
karakter,
|
||||
changeTulajdonsagNoveles,
|
||||
changeRolledHP,
|
||||
changeHarcosSpec,
|
||||
changeKalozKritikus,
|
||||
changeOsztaly
|
||||
} = props
|
||||
|
||||
const classLevels = getClassLevels(karakter.szintlepesek)
|
||||
|
||||
const harcosSzint = classLevels[Osztaly2E.Harcos]
|
||||
const kalozSzint = classLevels[Osztaly2E.Tengeresz]
|
||||
const specialization = karakter.szintlepesek[szint-1].harcosFegyver
|
||||
const krit = karakter.szintlepesek[szint-1].kalozKritikus
|
||||
|
||||
return <div>
|
||||
<div className='row mt-3'>
|
||||
<h5 className='col align-self-center'>{szint}. Szint</h5>
|
||||
</div>
|
||||
{osztaly !== Osztaly2E.Druida &&
|
||||
<MultiClassOsztalySelector2E
|
||||
faj={karakter.faj}
|
||||
osztaly={osztaly}
|
||||
changeOsztaly={changeOsztaly}
|
||||
osztalySzint={classLevels[osztaly]}
|
||||
/> }
|
||||
{szint % 4 === 0 &&
|
||||
<TulajdonsagNoveles
|
||||
tulajdonsagok={karakter.tulajdonsagok}
|
||||
tulajdonsagNoveles={karakter.szintlepesek[szint-1].tulajdonsagNoveles!}
|
||||
changeTulajdonsagNoveles={changeTulajdonsagNoveles}
|
||||
/> }
|
||||
<BasicNewLevel
|
||||
szint={szint}
|
||||
karakter={karakter}
|
||||
osztaly={osztaly}
|
||||
changeRolledHP={changeRolledHP}
|
||||
/>
|
||||
{osztaly === Osztaly2E.Harcos && harcosSzint % 2 === 1 &&
|
||||
<HarcosFegyverSpecializacio
|
||||
specialization={specialization!}
|
||||
existingSpecializations={karakter.szintlepesek.map(x => x.harcosFegyver)}
|
||||
changeSpecialization={changeHarcosSpec}
|
||||
szint={harcosSzint}
|
||||
/>
|
||||
}
|
||||
{osztaly === Osztaly2E.Tengeresz && kalozSzint % 3 === 0 &&
|
||||
<KalozKritikus
|
||||
kritFegyverId={krit!}
|
||||
existingKrits={karakter.szintlepesek.map(x => x.kalozKritikus)}
|
||||
changeKrit={changeKalozKritikus}
|
||||
szint={kalozSzint}
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
export function LevelUps(props: {karakter: Karakter2E, changeKarakter: (inputs: Karakter2E) => void}){
|
||||
const {karakter, changeKarakter} = props;
|
||||
|
||||
const tulajdonsagokAtSzint = (szint: number) => TulajdonsagokTotal({...karakter, szintlepesek: karakter.szintlepesek.slice(0, szint)})
|
||||
|
||||
const changeRolledHPAtSzint = (szint: number) => (newHProll: number) => {
|
||||
changeKarakter({...karakter, szintlepesek: arraySetN(karakter.szintlepesek, szint-1, {...karakter.szintlepesek[szint-1], HProll: newHProll})})
|
||||
console.log(karakter)
|
||||
}
|
||||
const changeTulajdonsagNovelesAtSzint = (szint: number) => {
|
||||
return (t: Tulajdonsag2E) => {
|
||||
changeKarakter({...karakter,
|
||||
szintlepesek: arraySetN(karakter.szintlepesek, szint-1, {
|
||||
...karakter.szintlepesek[szint-1],
|
||||
tulajdonsagNoveles: t
|
||||
})
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
const changeHarcosSpecializacioAtSzint = (szint: number) => {
|
||||
return (spec: string) => {
|
||||
changeKarakter({
|
||||
...karakter,
|
||||
szintlepesek: arraySetN(karakter.szintlepesek, szint-1, {
|
||||
...karakter.szintlepesek[szint-1],
|
||||
harcosFegyver: spec
|
||||
})
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
const changeKalozKritikusAtSzint = (szint: number) => {
|
||||
return (krit: string) => {
|
||||
changeKarakter({
|
||||
...karakter,
|
||||
szintlepesek: arraySetN(karakter.szintlepesek, szint-1, {
|
||||
...karakter.szintlepesek[szint-1],
|
||||
kalozKritikus: krit
|
||||
})
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
const levels = karakter.szintlepesek.map((_, i) => i+1);
|
||||
|
||||
return <>
|
||||
{levels.slice(1).map(szint =>
|
||||
<LevelUp
|
||||
key={`level-up-${szint}`}
|
||||
szint={szint}
|
||||
osztaly={karakter.szintlepesek[szint-1].osztaly}
|
||||
karakter={{...karakter, szintlepesek: karakter.szintlepesek.slice(0, szint) ,tulajdonsagok: tulajdonsagokAtSzint(szint)}}
|
||||
changeTulajdonsagNoveles={changeTulajdonsagNovelesAtSzint(szint)}
|
||||
changeRolledHP={changeRolledHPAtSzint(szint)}
|
||||
changeHarcosSpec={changeHarcosSpecializacioAtSzint(szint)}
|
||||
changeKalozKritikus={changeKalozKritikusAtSzint(szint)}
|
||||
changeOsztaly={(o) => changeKarakter(ChangeOsztalyAtSzint(karakter, o, szint))}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
}
|
||||
|
||||
export default LevelUps
|
||||
@@ -0,0 +1,54 @@
|
||||
import {Faj2E, SzintKorlatokFajokra} from "../domain-models/faj2E";
|
||||
import {Osztaly2E, OsztalyLabel} from "../domain-models/osztaly2E";
|
||||
import React from "react";
|
||||
|
||||
function MultiClassOsztalySelector2E(props: {
|
||||
faj: Faj2E,
|
||||
osztaly: Osztaly2E,
|
||||
changeOsztaly: (o: Osztaly2E) => void
|
||||
osztalySzint: number
|
||||
}) {
|
||||
const {faj, osztaly, changeOsztaly, osztalySzint} = props
|
||||
|
||||
return <>
|
||||
<div className='row'>
|
||||
<div className="col-lg-6">
|
||||
<div className="row m-md-2">
|
||||
<label className='col-8 col-sm-6 col-form-label'>Osztály</label>
|
||||
<div className="col">
|
||||
<select className="col form-select" value={osztaly}
|
||||
onChange={(e) => changeOsztaly(e.target.value as Osztaly2E)}>
|
||||
<optgroup label='Harcos'>
|
||||
<option value={Osztaly2E.Harcos}>{OsztalyLabel(Osztaly2E.Harcos)}</option>
|
||||
{SzintKorlatokFajokra(faj)[Osztaly2E.Amazon] > 0 && (
|
||||
<option value={Osztaly2E.Amazon}>{OsztalyLabel(Osztaly2E.Amazon)}</option>)}
|
||||
<option value={Osztaly2E.Barbar}>{OsztalyLabel(Osztaly2E.Barbar)}</option>
|
||||
<option value={Osztaly2E.Ijasz}>{OsztalyLabel(Osztaly2E.Ijasz)}</option>
|
||||
<option value={Osztaly2E.Tengeresz}>{OsztalyLabel(Osztaly2E.Tengeresz)}</option>
|
||||
</optgroup>
|
||||
<option value={Osztaly2E.Pap}>{OsztalyLabel(Osztaly2E.Pap)}</option>
|
||||
<option value={Osztaly2E.Tolvaj}>{OsztalyLabel(Osztaly2E.Tolvaj)}</option>
|
||||
<optgroup label='Varázsló'>
|
||||
<option value={Osztaly2E.Varazslo}>{OsztalyLabel(Osztaly2E.Varazslo)}</option>
|
||||
<option value={Osztaly2E.Illuzionista}>{OsztalyLabel(Osztaly2E.Illuzionista)}</option>
|
||||
</optgroup>
|
||||
<optgroup label='Opcionális'>
|
||||
<option value={Osztaly2E.Dalnok}>{OsztalyLabel(Osztaly2E.Dalnok)}</option>
|
||||
{/* Druida nem lehet tobb osztalyu karakter! */}
|
||||
<option value={Osztaly2E.Vandor}>{OsztalyLabel(Osztaly2E.Vandor)}</option>
|
||||
</optgroup>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-lg-6">
|
||||
<div className="row m-2">
|
||||
<label className='col-sm-6 col-8 col-form-label'>Új {OsztalyLabel(osztaly)} szint{' '}</label>
|
||||
<span className='col col-form-label'>{osztalySzint}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
|
||||
export default MultiClassOsztalySelector2E
|
||||
@@ -0,0 +1,37 @@
|
||||
import React from "react";
|
||||
import {
|
||||
KarakterTulajdonsagok,
|
||||
Modifier,
|
||||
Tulajdonsag2E,
|
||||
TulajdonsagIDs,
|
||||
TulajdonsagLabel
|
||||
} from "../domain-models/tulajdonsag2E";
|
||||
import {SignedNumberToText} from "../../shared/components/Helpers";
|
||||
|
||||
function TulajdonsagNoveles(props: {tulajdonsagok: KarakterTulajdonsagok, tulajdonsagNoveles: Tulajdonsag2E, changeTulajdonsagNoveles: (tulajdonsag: Tulajdonsag2E) => void}) {
|
||||
const {tulajdonsagok, tulajdonsagNoveles, changeTulajdonsagNoveles} = props
|
||||
return <>
|
||||
<div className='row'>
|
||||
<div className='col-lg-6'>
|
||||
<div className='row m-2'>
|
||||
<label className='col-sm-6 col-8 col-form-label'>Tulajdonság növelés{' '}</label>
|
||||
<div className='col'>
|
||||
<select className='form-select' value={tulajdonsagNoveles} onChange={(e) => changeTulajdonsagNoveles(e.target.value as Tulajdonsag2E)}>
|
||||
{TulajdonsagIDs.filter(t => t === tulajdonsagNoveles || tulajdonsagok[t] < 18).map(t => (
|
||||
<option value={t} key={t}>{TulajdonsagLabel(t)}</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className='col-lg-6'>
|
||||
<div className='row m-2'>
|
||||
<label className='col-sm-6 col-8 col-form-label'>Új {TulajdonsagLabel(tulajdonsagNoveles)}{' '}</label>
|
||||
<span className='col col-form-label'>{tulajdonsagok[tulajdonsagNoveles]} ({SignedNumberToText(Modifier(tulajdonsagok[tulajdonsagNoveles]))})</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
|
||||
export default TulajdonsagNoveles
|
||||
@@ -487,7 +487,7 @@
|
||||
"Harcos": true,
|
||||
"CsakNemHarcos": false,
|
||||
"Masfelkezes": false,
|
||||
"Ketkezes": false,
|
||||
"Ketkezes": true,
|
||||
"KetkezesBonusz": false,
|
||||
"HarciManover": false,
|
||||
"Alakzat": false,
|
||||
|
||||
@@ -4,6 +4,10 @@ import {Osztaly2E} from "./osztaly2E";
|
||||
import {KepzettsegId} from "./kepzettsegek2E";
|
||||
import {JellemID} from "./jellem";
|
||||
import {DefaultFelszereles, KarakterFelszereles} from "./felszereles";
|
||||
import {Szintlepes} from "./szintlepes";
|
||||
import {AllowedFegyver} from "./allowed-fegyver";
|
||||
import {BaseHP} from "./masodlagos_ertekek";
|
||||
import {dAny} from "../../shared/domain-models/kockak";
|
||||
|
||||
export interface Karakter2E {
|
||||
name: string,
|
||||
@@ -13,14 +17,11 @@ export interface Karakter2E {
|
||||
jellem: JellemID,
|
||||
tulajdonsagok: KarakterTulajdonsagok
|
||||
faj: Faj2E
|
||||
osztaly: Osztaly2E
|
||||
harcosSpecializaciok: string[],
|
||||
kalozKritikus: string[],
|
||||
kepzettsegek: KepzettsegId[]
|
||||
tolvajKepzettsegek?: KepzettsegId[]
|
||||
szint: number,
|
||||
hpRolls: number[]
|
||||
felszereles: KarakterFelszereles,
|
||||
szintlepesek: Szintlepes[]
|
||||
}
|
||||
|
||||
export const DefaultKarakter: Karakter2E = {
|
||||
@@ -29,12 +30,103 @@ export const DefaultKarakter: Karakter2E = {
|
||||
jellem: 'S',
|
||||
tulajdonsagok: TulajdonsagDefaults,
|
||||
faj: Faj2E.Ember,
|
||||
osztaly: Osztaly2E.Tengeresz,
|
||||
harcosSpecializaciok: [],
|
||||
kalozKritikus: [],
|
||||
kepzettsegek: [],
|
||||
tolvajKepzettsegek: [],
|
||||
szint: 1,
|
||||
hpRolls: [],
|
||||
felszereles: DefaultFelszereles,
|
||||
szintlepesek: [{
|
||||
osztaly: Osztaly2E.Tengeresz,
|
||||
HProll: 10,
|
||||
}]
|
||||
}
|
||||
|
||||
function setOsztalyForSzintLepes(szintlepesek: Szintlepes[], osztaly: Osztaly2E, szint: number): Szintlepes[] {
|
||||
const prev = szintlepesek.slice(0, szint-1)
|
||||
const current : Szintlepes = {
|
||||
...szintlepesek[szint-1],
|
||||
osztaly,
|
||||
harcosFegyver: undefined,
|
||||
kalozKritikus: undefined
|
||||
}
|
||||
const tail = szintlepesek.slice(szint).map(x => {return {...x}})
|
||||
|
||||
let harcosSzint = prev.filter(x => x.osztaly === Osztaly2E.Harcos).length
|
||||
// TODO: only pick specializations from valid levels
|
||||
const harcosSpecializaciok = szintlepesek
|
||||
.map(x => x.harcosFegyver)
|
||||
.filter(x => x != null)
|
||||
const harcosSpecAtSzint = (hSzint: number): (string | undefined) => {
|
||||
if (hSzint % 2 === 1) {
|
||||
const specIndex = (hSzint+1)/2 -1
|
||||
if (harcosSpecializaciok.length > specIndex ) {
|
||||
return harcosSpecializaciok[specIndex]
|
||||
} else {
|
||||
return AllowedFegyver(Osztaly2E.Harcos)[specIndex].Id
|
||||
}
|
||||
}
|
||||
else {
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
if (osztaly === Osztaly2E.Harcos) {
|
||||
harcosSzint += 1
|
||||
current.harcosFegyver = harcosSpecAtSzint(harcosSzint)
|
||||
}
|
||||
for (let i = 0; i < tail.length; i++) {
|
||||
if (tail[i].osztaly === Osztaly2E.Harcos) {
|
||||
harcosSzint++
|
||||
tail[i].harcosFegyver = harcosSpecAtSzint(harcosSzint)
|
||||
}
|
||||
}
|
||||
|
||||
let kalozSzint = prev.filter(x => x.osztaly === Osztaly2E.Tengeresz).length
|
||||
const existingKalozKrits = szintlepesek
|
||||
.map(x => x.kalozKritikus)
|
||||
.filter(x => x != null)
|
||||
const kalozKritAtSzint = (kSzint: number): (string | undefined) => {
|
||||
if (kSzint % 3 === 0) {
|
||||
const specIndex = kSzint/3 -1
|
||||
if (existingKalozKrits.length > specIndex ) {
|
||||
return existingKalozKrits[specIndex]
|
||||
} else {
|
||||
return AllowedFegyver(Osztaly2E.Tengeresz)[specIndex].Id
|
||||
}
|
||||
}
|
||||
else {
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
if (osztaly === Osztaly2E.Tengeresz) {
|
||||
kalozSzint++
|
||||
current.kalozKritikus = kalozKritAtSzint(szint)
|
||||
}
|
||||
for (let i = 0; i < tail.length; i++) {
|
||||
if (tail[i].osztaly === Osztaly2E.Tengeresz) {
|
||||
kalozSzint++
|
||||
tail[i].kalozKritikus = kalozKritAtSzint(kalozSzint)
|
||||
}
|
||||
}
|
||||
|
||||
const HD = BaseHP(osztaly)
|
||||
if (szint === 1) {
|
||||
current.HProll = HD
|
||||
} else {
|
||||
current.HProll = dAny(HD)
|
||||
console.log(`Level ${szint} HP roll on d${HD} is: ${current.HProll} due to class change`)
|
||||
}
|
||||
return [...prev, current, ...tail]
|
||||
}
|
||||
|
||||
export function ChangeLvl1Osztaly(karakter: Karakter2E, osztaly: Osztaly2E): Karakter2E {
|
||||
return {
|
||||
...karakter,
|
||||
szintlepesek: [{...karakter.szintlepesek[0], osztaly}, ...karakter.szintlepesek.slice(1)]
|
||||
}
|
||||
}
|
||||
|
||||
export function ChangeOsztalyAtSzint(karakter: Karakter2E, osztaly: Osztaly2E, szint: number): Karakter2E {
|
||||
return {
|
||||
...karakter,
|
||||
szintlepesek: setOsztalyForSzintLepes(karakter.szintlepesek, osztaly, szint)
|
||||
}
|
||||
}
|
||||
@@ -209,12 +209,12 @@ export const Kepzettsegek: Record<KepzettsegId, Kepzettseg> =
|
||||
(acc: Record<KepzettsegId, Kepzettseg>, k) => { acc[k.Id] = k; return acc; },
|
||||
{} as Record<KepzettsegId, Kepzettseg>);
|
||||
|
||||
export function AvailableKezpettsegList(osztaly: Osztaly2E): Kepzettseg[] {
|
||||
return KepzettsegLista.filter(k => k.Osztalyok == null || k.Osztalyok.includes(osztaly))
|
||||
export function AvailableKezpettsegList(...osztalyok: Osztaly2E[]): Kepzettseg[] {
|
||||
return KepzettsegLista.filter(k => k.Osztalyok == null || k.Osztalyok.some(o => osztalyok.includes(o)))
|
||||
}
|
||||
|
||||
export function GetNumberOfKepzettsegek(t_int: number, faj: Faj2E, max: number = 11) {
|
||||
//max = 3 + 1 + 3 + 4 // 3 base, 1 human, 3 from max ability modifier, 4 from Thief
|
||||
//max = 3 + 1 + 3 + 5 = 12 // 3 base, 1 human, 3 from max ability modifier, 5 from Thief at 9th level
|
||||
let numberOfKepzettseg = 3 + Modifier(t_int) + (faj === Faj2E.Ember ? 1 : 0)
|
||||
|
||||
if (numberOfKepzettseg < 1) {
|
||||
@@ -228,18 +228,20 @@ export function GetNumberOfKepzettsegek(t_int: number, faj: Faj2E, max: number =
|
||||
return numberOfKepzettseg;
|
||||
}
|
||||
|
||||
export function SetDefaultTolvajKepzettsegek(karakter: Pick<Karakter2E, 'osztaly' | 'tolvajKepzettsegek'>, changeTolvajKepzettsegek: (tolvajKepzettsegek?: KepzettsegId[]) => void) {
|
||||
if (karakter.osztaly !== Osztaly2E.Tolvaj && karakter.tolvajKepzettsegek != null) {
|
||||
export function SetDefaultTolvajKepzettsegek(karakter: Pick<Karakter2E, 'szintlepesek' | 'tolvajKepzettsegek'>, changeTolvajKepzettsegek: (tolvajKepzettsegek?: KepzettsegId[]) => void) {
|
||||
const hasTolvajSzint = karakter.szintlepesek.some(szint => szint.osztaly === Osztaly2E.Tolvaj)
|
||||
if (!hasTolvajSzint && karakter.tolvajKepzettsegek != null) {
|
||||
changeTolvajKepzettsegek(undefined)
|
||||
}
|
||||
if (karakter.osztaly === Osztaly2E.Tolvaj && (karakter.tolvajKepzettsegek == null || karakter.tolvajKepzettsegek.length === 0)) {
|
||||
if (hasTolvajSzint && (karakter.tolvajKepzettsegek == null || karakter.tolvajKepzettsegek.length === 0)) {
|
||||
const tolvajKepzettsegek = TolvajKepzettsegList.slice(0, 4).map(x => x.Id)
|
||||
changeTolvajKepzettsegek(tolvajKepzettsegek)
|
||||
}
|
||||
}
|
||||
|
||||
export function GetKepzettsegListaN(karakter: Pick<Karakter2E, 'osztaly' | 'faj' | 'kepzettsegek' | 'tolvajKepzettsegek'>) {
|
||||
const availableKepzettsegList = AvailableKezpettsegList(karakter.osztaly)
|
||||
export function GetKepzettsegListaN(karakter: Pick<Karakter2E, 'szintlepesek' | 'faj' | 'kepzettsegek' | 'tolvajKepzettsegek'>) {
|
||||
const osztalyok = new Set(karakter.szintlepesek.map(x => x.osztaly))
|
||||
const availableKepzettsegList = AvailableKezpettsegList(...osztalyok)
|
||||
return (n: number): Kepzettseg[] => {
|
||||
let available = [...availableKepzettsegList]
|
||||
if (n === 0) {
|
||||
@@ -249,7 +251,8 @@ export function GetKepzettsegListaN(karakter: Pick<Karakter2E, 'osztaly' | 'faj'
|
||||
if (karakter.faj === Faj2E.Nomad) {
|
||||
return [Kepzettsegek.k_lovaglas]
|
||||
}
|
||||
if (karakter.faj === Faj2E.Birodalmi && karakter.osztaly !== Osztaly2E.Tolvaj) {
|
||||
// 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) {
|
||||
@@ -258,21 +261,28 @@ export function GetKepzettsegListaN(karakter: Pick<Karakter2E, 'osztaly' | 'faj'
|
||||
if (karakter.faj === Faj2E.Elf || karakter.faj === Faj2E.Felelf) {
|
||||
return [Kepzettsegek.k_vadonjaras]
|
||||
}
|
||||
if (karakter.osztaly === Osztaly2E.Druida || karakter.osztaly === Osztaly2E.Vandor) {
|
||||
if (osztalyok.has(Osztaly2E.Druida) || osztalyok.has(Osztaly2E.Vandor)) {
|
||||
return [Kepzettsegek.k_vadonjaras]
|
||||
}
|
||||
if (karakter.osztaly === Osztaly2E.Dalnok) {
|
||||
if (osztalyok.has(Osztaly2E.Dalnok)) {
|
||||
return [Kepzettsegek.k_eloadas]
|
||||
}
|
||||
}
|
||||
if (n === 1) {
|
||||
if (karakter.osztaly === Osztaly2E.Dalnok
|
||||
&& [Faj2E.Eszaki, Faj2E.Nomad, Faj2E.Birodalmi, Faj2E.Torpe, Faj2E.Elf, Faj2E.Felelf].includes(karakter.faj)) {
|
||||
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 ([Osztaly2E.Druida, Osztaly2E.Vandor].includes(karakter.osztaly)
|
||||
&& [Faj2E.Eszaki, Faj2E.Nomad, Faj2E.Birodalmi, Faj2E.Torpe].includes(karakter.faj)) {
|
||||
return [Kepzettsegek.k_vadonjaras]
|
||||
}
|
||||
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)]
|
||||
@@ -280,8 +290,9 @@ export function GetKepzettsegListaN(karakter: Pick<Karakter2E, 'osztaly' | 'faj'
|
||||
}
|
||||
}
|
||||
|
||||
export function SetDefaultKepzettsegek(karakter: Pick<Karakter2E, 'osztaly' | 'faj' | 'tulajdonsagok' | 'kepzettsegek' | 'tolvajKepzettsegek'>, changeKepzettsegek: (k: KepzettsegId[]) => void) {
|
||||
const availableKepzettsegList = AvailableKezpettsegList(karakter.osztaly)
|
||||
export function SetDefaultKepzettsegek(karakter: Pick<Karakter2E, 'szintlepesek' | 'faj' | 'tulajdonsagok' | 'kepzettsegek' | 'tolvajKepzettsegek'>, changeKepzettsegek: (k: KepzettsegId[]) => void) {
|
||||
const osztalyok = karakter.szintlepesek.map(x => x.osztaly)
|
||||
const availableKepzettsegList = AvailableKezpettsegList(...osztalyok)
|
||||
const numberOfKepzettsegek = GetNumberOfKepzettsegek(karakter.tulajdonsagok.t_int, karakter.faj, availableKepzettsegList.length)
|
||||
let kepzettsegek = [...karakter.kepzettsegek]
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ export interface MasodlagosErtekekView {
|
||||
Mentok: Mentok
|
||||
}
|
||||
|
||||
export type KarakterpickForMasodlagosErtekek = Pick<Karakter2E, 'osztaly' | 'hpRolls' | 'tulajdonsagok' | 'szint' | 'faj' | 'felszereles'>
|
||||
export type KarakterpickForMasodlagosErtekek = Pick<Karakter2E, 'tulajdonsagok' | 'szint' | 'szintlepesek' | 'faj' | 'felszereles'>
|
||||
|
||||
export function BaseHP(osztaly: Osztaly2E) {
|
||||
let base = 4;
|
||||
@@ -45,15 +45,16 @@ export function BaseHP(osztaly: Osztaly2E) {
|
||||
return base
|
||||
}
|
||||
|
||||
export function HP(karakter: Pick<Karakter2E, 'osztaly' | 'tulajdonsagok' | 'hpRolls'>): number {
|
||||
export function HP(karakter: Pick<Karakter2E, 'tulajdonsagok' | 'szintlepesek'>): number {
|
||||
const egeszsegModifier = Modifier(karakter.tulajdonsagok.t_egs)
|
||||
return BaseHP(karakter.osztaly) + egeszsegModifier + karakter.hpRolls
|
||||
return karakter.szintlepesek
|
||||
// https://lfg.hu/forum/topic/15079-kard-es-magia/page/219/#comment-2218333
|
||||
.map(hp => Math.max(1, hp + egeszsegModifier))
|
||||
.map(szint => Math.max(1, szint.HProll + egeszsegModifier))
|
||||
.reduce((sum, val) => sum + val, 0)
|
||||
}
|
||||
|
||||
export function VO(karakter: Pick<Karakter2E, 'tulajdonsagok' | 'faj' | 'osztaly' | 'szint' | 'felszereles'>): number {
|
||||
export function VO(karakter: Pick<Karakter2E, 'tulajdonsagok' | 'faj' | 'szintlepesek' | 'szint' | 'felszereles'>): number {
|
||||
const osztalyok = new Set(karakter.szintlepesek.map(x => x.osztaly))
|
||||
const pancel = GetPancel(karakter.felszereles.pancelID)
|
||||
const pancelVO = pancel?.VO ?? 0
|
||||
const pajzsVO = GetPajzs(karakter.felszereles.pajzsID)?.VO ?? 0
|
||||
@@ -62,14 +63,16 @@ export function VO(karakter: Pick<Karakter2E, 'tulajdonsagok' | 'faj' | 'osztaly
|
||||
if (pancel) {
|
||||
abilityVO = Math.min(MaxAbilityVO(pancel.Type), abilityVO)
|
||||
}
|
||||
if (karakter.osztaly === Osztaly2E.Tengeresz) {
|
||||
vo += Math.floor(karakter.szint / 3)
|
||||
if (osztalyok.has(Osztaly2E.Tengeresz)) {
|
||||
const kalozSzintek = karakter.szintlepesek.filter(x => x.osztaly === Osztaly2E.Tengeresz).length
|
||||
vo += Math.floor(kalozSzintek / 3)
|
||||
}
|
||||
if (karakter.osztaly === Osztaly2E.Amazon) {
|
||||
if (osztalyok.has(Osztaly2E.Amazon)) {
|
||||
if (karakter.tulajdonsagok.t_kar > karakter.tulajdonsagok.t_ugy) {
|
||||
abilityVO = Modifier(karakter.tulajdonsagok.t_kar)
|
||||
}
|
||||
vo += Math.floor(karakter.szint / 2)
|
||||
const amazonSzintek = karakter.szintlepesek.filter(x => x.osztaly === Osztaly2E.Amazon).length
|
||||
vo += Math.floor(amazonSzintek / 2)
|
||||
}
|
||||
if (karakter.faj === Faj2E.Felszerzet || karakter.faj === Faj2E.Gnom){
|
||||
vo += 1
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import {Osztaly2E} from "./osztaly2E";
|
||||
import {Karakter2E} from "./karakter2E";
|
||||
import {KarakterTulajdonsagok, TulajdonsagModosito} from "./tulajdonsag2E";
|
||||
import {Szintlepes} from "./szintlepes";
|
||||
|
||||
export interface Mentok {
|
||||
kitartas: number
|
||||
@@ -73,8 +74,26 @@ export function MentoModositok(tulajdonsagModositok: KarakterTulajdonsagok) {
|
||||
}
|
||||
}
|
||||
|
||||
export function MentokTotal(karakter: Pick<Karakter2E, 'osztaly' | 'szint' | 'tulajdonsagok'>) {
|
||||
const alap = MentokAlap(karakter.osztaly, karakter.szint)
|
||||
export function MultiClassMentokAlap(szintlepesek: Szintlepes[]) : Mentok {
|
||||
const osztalyok = new Set(szintlepesek.map(x => x.osztaly))
|
||||
let mentok = {
|
||||
kitartas: 0,
|
||||
reflex: 0,
|
||||
akaratero: 0
|
||||
}
|
||||
for (const osztaly of osztalyok) {
|
||||
const szint = szintlepesek.filter(x => x.osztaly === osztaly).length
|
||||
const uj_mentok = MentokAlap(osztaly, szint)
|
||||
mentok.akaratero += uj_mentok.akaratero
|
||||
mentok.reflex += uj_mentok.reflex
|
||||
mentok.kitartas += uj_mentok.kitartas
|
||||
}
|
||||
|
||||
return mentok
|
||||
}
|
||||
|
||||
export function MentokTotal(karakter: Pick<Karakter2E, 'szintlepesek' | 'tulajdonsagok'>) {
|
||||
const alap = MultiClassMentokAlap(karakter.szintlepesek)
|
||||
const modositok = MentoModositok(TulajdonsagModosito(karakter.tulajdonsagok))
|
||||
return {
|
||||
kitartas: alap.kitartas + modositok.kitartas,
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
import {Tulajdonsag2E, TulajdonsagIDs, TulajdonsagokTotal} from "./tulajdonsag2E";
|
||||
import {KepzettsegId} from "./kepzettsegek2E";
|
||||
import {Osztaly2E} from "./osztaly2E";
|
||||
import {Karakter2E} from "./karakter2E";
|
||||
import {BaseHP} from "./masodlagos_ertekek";
|
||||
import {dAny} from "../../shared/domain-models/kockak";
|
||||
import {AllowedFegyver} from "./allowed-fegyver";
|
||||
|
||||
export interface Szintlepes {
|
||||
osztaly: Osztaly2E;
|
||||
HProll: number;
|
||||
tolvajExtraKepzettseg?: KepzettsegId; // only at level 5 and 9
|
||||
harcosFegyver?: string; // every 3rd level
|
||||
kalozKritikus?: string; // every odd level
|
||||
tulajdonsagNoveles?: Tulajdonsag2E;
|
||||
}
|
||||
|
||||
export function PickableSpecializationFilter(existingSpecilizations: (string | undefined)[], szint: number) {
|
||||
return (fegyver: {Id: string}) => existingSpecilizations.filter(x => x === fegyver.Id).length < (szint < 9 ? 1 : 2)
|
||||
}
|
||||
|
||||
export function LevelUp(karakter: Karakter2E, changeKarakter: (input: Karakter2E) => void) {
|
||||
const osztaly = karakter.szintlepesek[0].osztaly
|
||||
const tulajdonsagokTotal = TulajdonsagokTotal(karakter)
|
||||
const szint = karakter.szint + 1
|
||||
const dice = BaseHP(osztaly)
|
||||
const roll = dAny(dice)
|
||||
console.log(`Level ${szint} HP roll on d${dice} is: ${roll}`)
|
||||
|
||||
const szintlepes: Szintlepes = {
|
||||
osztaly: osztaly,
|
||||
HProll: roll
|
||||
}
|
||||
|
||||
if (szint % 4 === 0) {
|
||||
for (const tulajdonsag of TulajdonsagIDs) {
|
||||
if (tulajdonsagokTotal[tulajdonsag] < 18) {
|
||||
szintlepes.tulajdonsagNoveles = tulajdonsag;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(osztaly === Osztaly2E.Harcos) {
|
||||
const harcosSzint = karakter.szintlepesek.filter(x => x.osztaly === Osztaly2E.Harcos).length + 1
|
||||
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 (kalozSzint % 3 === 0) {
|
||||
let kalozKritikus = karakter.szintlepesek.map(x => x.kalozKritikus)
|
||||
let specializationFilter = PickableSpecializationFilter(kalozKritikus, kalozSzint)
|
||||
szintlepes.kalozKritikus = AllowedFegyver(Osztaly2E.Tengeresz).filter(specializationFilter)[0].Id
|
||||
}
|
||||
}
|
||||
|
||||
changeKarakter({...karakter, szint, szintlepesek: [...karakter.szintlepesek, szintlepes]})
|
||||
}
|
||||
|
||||
export function LevelDown(karakter: Karakter2E, changeKarakter: (input: Karakter2E) => void) {
|
||||
if (karakter.szintlepesek.length > 1) {
|
||||
changeKarakter({...karakter, szint: karakter.szint - 1, szintlepesek: karakter.szintlepesek.slice(0, -1)})
|
||||
}
|
||||
else {
|
||||
console.error(`Can't level down character: ${karakter}`)
|
||||
}
|
||||
}
|
||||
|
||||
export type ClassLevels = {
|
||||
[key in Osztaly2E]: number;
|
||||
};
|
||||
|
||||
const defaultClassLevels: ClassLevels = {
|
||||
[Osztaly2E.Amazon]: 0,
|
||||
[Osztaly2E.Barbar]: 0,
|
||||
[Osztaly2E.Ijasz]: 0,
|
||||
[Osztaly2E.Harcos]: 0,
|
||||
[Osztaly2E.Tengeresz]: 0,
|
||||
|
||||
[Osztaly2E.Tolvaj]: 0,
|
||||
[Osztaly2E.Pap]: 0,
|
||||
|
||||
[Osztaly2E.Varazslo]: 0,
|
||||
[Osztaly2E.Illuzionista]: 0,
|
||||
|
||||
[Osztaly2E.Dalnok]: 0,
|
||||
[Osztaly2E.Druida]: 0,
|
||||
[Osztaly2E.Vandor]: 0,
|
||||
}
|
||||
|
||||
export function getClassLevels(szintlepesek: Szintlepes[]) : ClassLevels {
|
||||
return szintlepesek.map(x => x.osztaly).reduce((classLevels, osztaly) => {
|
||||
classLevels[osztaly]++
|
||||
return classLevels
|
||||
}, {...defaultClassLevels})
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import {Osztaly2E} from "./osztaly2E";
|
||||
import {Karakter2E} from "./karakter2E";
|
||||
import {Modifier} from "./tulajdonsag2E";
|
||||
import {Szintlepes} from "./szintlepes";
|
||||
|
||||
/// Finally second edition doesn't mess with the extra attack of high level clerics rouges and wizards!
|
||||
function ExtraAttack(attack: number): number[] {
|
||||
@@ -10,6 +11,31 @@ function ExtraAttack(attack: number): number[] {
|
||||
return [Math.min(attack, 10), ...ExtraAttack(attack - 5)]
|
||||
}
|
||||
|
||||
export function TamadasBonuszBase(osztaly: Osztaly2E, szint: number = 1) : number {
|
||||
let base = 0
|
||||
switch (osztaly){
|
||||
case Osztaly2E.Barbar:
|
||||
case Osztaly2E.Amazon:
|
||||
case Osztaly2E.Ijasz:
|
||||
case Osztaly2E.Tengeresz:
|
||||
case Osztaly2E.Harcos:
|
||||
case Osztaly2E.Vandor:
|
||||
base = szint;
|
||||
break;
|
||||
case Osztaly2E.Pap:
|
||||
case Osztaly2E.Tolvaj:
|
||||
case Osztaly2E.Dalnok:
|
||||
case Osztaly2E.Druida:
|
||||
base = Math.floor(szint * 2 / 3);
|
||||
break;
|
||||
case Osztaly2E.Varazslo:
|
||||
case Osztaly2E.Illuzionista:
|
||||
base = Math.floor(szint / 2);
|
||||
break;
|
||||
}
|
||||
return base
|
||||
}
|
||||
|
||||
export function TamadasBonusz(osztaly: Osztaly2E, szint: number = 1) : number[] {
|
||||
let base = 0
|
||||
switch (osztaly){
|
||||
@@ -35,12 +61,23 @@ export function TamadasBonusz(osztaly: Osztaly2E, szint: number = 1) : number[]
|
||||
return ExtraAttack(base)
|
||||
}
|
||||
|
||||
export function KozelharciTB(karakter : Pick<Karakter2E, 'szint' | 'tulajdonsagok' | 'osztaly'>): number[] {
|
||||
const base = TamadasBonusz(karakter.osztaly, karakter.szint);
|
||||
|
||||
export function MultiClassTamadasBonusz(szintlepesek: Szintlepes[]) : number[]{
|
||||
const osztalyok = new Set(szintlepesek.map(x => x.osztaly))
|
||||
let TBB = 0
|
||||
for (const osztaly of osztalyok) {
|
||||
const szint = szintlepesek.filter(x => x.osztaly === osztaly).length
|
||||
TBB += TamadasBonuszBase(osztaly, szint)
|
||||
}
|
||||
return ExtraAttack(TBB)
|
||||
}
|
||||
|
||||
export function KozelharciTB(karakter : Pick<Karakter2E, 'szintlepesek' | 'tulajdonsagok'>): number[] {
|
||||
const base = MultiClassTamadasBonusz(karakter.szintlepesek);
|
||||
let modifier = Modifier(karakter.tulajdonsagok.t_ero)
|
||||
return base.map(v => v + modifier)
|
||||
}
|
||||
export function CelzoTB(karakter : Pick<Karakter2E, 'szint' | 'tulajdonsagok' | 'osztaly'>): number[] {
|
||||
let base = TamadasBonusz(karakter.osztaly, karakter.szint)
|
||||
export function CelzoTB(karakter : Pick<Karakter2E, 'szintlepesek' | 'tulajdonsagok'>): number[] {
|
||||
let base = MultiClassTamadasBonusz(karakter.szintlepesek)
|
||||
return base.map(v => v + Modifier(karakter.tulajdonsagok.t_ugy))
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
import {d6} from "../../shared/domain-models/kockak";
|
||||
import {Karakter2E} from "./karakter2E";
|
||||
import {TulajdonsagokFajjal} from "./faj2E";
|
||||
|
||||
export enum Tulajdonsag2E {
|
||||
Ero = 't_ero',
|
||||
@@ -74,3 +76,15 @@ export function TulajdonsagModosito(tulajdonsagok: KarakterTulajdonsagok): Karak
|
||||
}
|
||||
return response
|
||||
}
|
||||
|
||||
export function TulajdonsagokTotal(karakter: Pick<Karakter2E, 'tulajdonsagok' | 'faj' | 'szintlepesek'>): KarakterTulajdonsagok {
|
||||
const level1 = TulajdonsagokFajjal(karakter.tulajdonsagok, karakter.faj)
|
||||
return {
|
||||
t_ero: level1.t_ero + karakter.szintlepesek.filter(s => s.tulajdonsagNoveles === 't_ero').length,
|
||||
t_ugy: level1.t_ugy + karakter.szintlepesek.filter(s => s.tulajdonsagNoveles === 't_ugy').length,
|
||||
t_egs: level1.t_egs + karakter.szintlepesek.filter(s => s.tulajdonsagNoveles === 't_egs').length,
|
||||
t_int: level1.t_int + karakter.szintlepesek.filter(s => s.tulajdonsagNoveles === 't_int').length,
|
||||
t_bol: level1.t_bol + karakter.szintlepesek.filter(s => s.tulajdonsagNoveles === 't_bol').length,
|
||||
t_kar: level1.t_kar + karakter.szintlepesek.filter(s => s.tulajdonsagNoveles === 't_kar').length,
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, {useState} from 'react'
|
||||
import {useParams} from "react-router-dom";
|
||||
import {Toast, ToastContainer} from "react-bootstrap";
|
||||
import {DefaultKarakter} from "../domain-models/karakter2E";
|
||||
import {ChangeLvl1Osztaly, DefaultKarakter} from "../domain-models/karakter2E";
|
||||
import {KarakterTulajdonsagok} from "../domain-models/tulajdonsag2E";
|
||||
import FajSelector2E from "../components/FajSelector2E";
|
||||
import Tulajdonsagok2E from "../components/Tulajdonsagok2E";
|
||||
@@ -20,6 +20,10 @@ import MasodlagosErtekek from "../components/MasodlagosErtekek";
|
||||
import {CalculateMasodlagosErtekek} from "../domain-models/masodlagos_ertekek";
|
||||
import Felszereles from "../components/Felszereles";
|
||||
import {KarakterFelszereles} from "../domain-models/felszereles";
|
||||
import LevelUps from "../components/LevelUps";
|
||||
import {LevelDown, LevelUp} from "../domain-models/szintlepes";
|
||||
import HarcosFegyverSpecializacio from "../components/HarcosFegyverSpec";
|
||||
import {arraySetN} from "../../util";
|
||||
|
||||
function CreateCharacter2E(props: {}) {
|
||||
|
||||
@@ -99,13 +103,16 @@ function CreateCharacter2E(props: {}) {
|
||||
</div>
|
||||
<OsztalySelector2E
|
||||
currentFaj={karakter.faj}
|
||||
currentOsztaly={karakter.osztaly}
|
||||
changeOsztaly={osztaly => setKarakter({...karakter, osztaly})}
|
||||
currentOsztaly={karakter.szintlepesek[0].osztaly}
|
||||
changeOsztaly={o => setKarakter(ChangeLvl1Osztaly(karakter, o))}
|
||||
/>
|
||||
{karakter.osztaly === Osztaly2E.Harcos &&
|
||||
<div className='mb-6'>
|
||||
TODO: Harcos fegyver specializaciok!!
|
||||
</div>}
|
||||
{karakter.szintlepesek[0].osztaly === Osztaly2E.Harcos &&
|
||||
<HarcosFegyverSpecializacio
|
||||
specialization={karakter.szintlepesek[0].harcosFegyver || 'kard_hosszu'}
|
||||
szint={1}
|
||||
changeSpecialization={(spec) => setKarakter({...karakter, szintlepesek: arraySetN(karakter.szintlepesek, 0, {...karakter.szintlepesek[0], harcosFegyver: spec})})}
|
||||
existingSpecializations={karakter.szintlepesek.map(x => x.harcosFegyver)}
|
||||
/>}
|
||||
<KarakterKepzettsegek
|
||||
getKepzettsegListaN={GetKepzettsegListaN(karakter)}
|
||||
numberOfKepzettsegek={GetNumberOfKepzettsegek(tulajdonsagokFajjal.t_int, karakter.faj)}
|
||||
@@ -120,10 +127,21 @@ function CreateCharacter2E(props: {}) {
|
||||
...karakter,
|
||||
tulajdonsagok: tulajdonsagokFajjal,
|
||||
szint: 1,
|
||||
hpRolls: []
|
||||
szintlepesek: karakter.szintlepesek.slice(0,1)
|
||||
})} />
|
||||
<hr />
|
||||
<Felszereles osztaly={karakter.osztaly} felszereles={karakter.felszereles}
|
||||
<LevelUps karakter={karakter} changeKarakter={setKarakter}/>
|
||||
{karakter.szint > 1 && <hr/>}
|
||||
<div className='d-grid gap-2 m-5'>
|
||||
{karakter.szint < 12 &&
|
||||
<button className='btn btn-dark btn-lg' type='button' onClick={() => LevelUp(karakter, setKarakter)}>Szintlépés!
|
||||
⇧</button>}
|
||||
{karakter.szint > 1 &&
|
||||
<button className='btn btn-dark btn-lg' type='button' onClick={() => LevelDown(karakter, setKarakter)}>Visszalépés!
|
||||
⇩</button>}
|
||||
</div>
|
||||
<hr/>
|
||||
<Felszereles osztaly={karakter.szintlepesek[0].osztaly} felszereles={karakter.felszereles}
|
||||
changeFelszereles={changeFelszereles}/>
|
||||
|
||||
<hr/>
|
||||
|
||||
@@ -13,12 +13,21 @@
|
||||
|
||||
## Features
|
||||
- multiclass characters
|
||||
- 2e -ben félig működik
|
||||
- druidáknál tiltani kell
|
||||
- PDF-ben varazshasznalok
|
||||
- tolvaj 9ik szintű képessége
|
||||
|
||||
## Epics
|
||||
- Backend
|
||||
- Varazslatok
|
||||
- Felszerelés
|
||||
- KéM 2.0-ra felkészülés
|
||||
- 1e-ben fel van már véve az adat. Gergő neki veselkedik.
|
||||
- 2e-ben teljesen hiányzik
|
||||
- KéM 2e
|
||||
- frontend oldalon úgy, ahogy megvan
|
||||
- PDF hiányzik
|
||||
- backend hiányzik
|
||||
- Klikkelésre dobás
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user